diff --git a/DEPS b/DEPS
index a84d056..776372c 100644
--- a/DEPS
+++ b/DEPS
@@ -236,7 +236,7 @@
   #
   # CQ_INCLUDE_TRYBOTS=luci.chrome.try:lacros-amd64-generic-chrome-skylab
   # CQ_INCLUDE_TRYBOTS=luci.chrome.try:lacros-arm-generic-chrome-skylab
-  'lacros_sdk_version': '15778.0.0',
+  'lacros_sdk_version': '16018.0.0',
 
   # Generate location tag metadata to include in tests result data uploaded
   # to ResultDB. This isn't needed on some configs and the tool that generates
@@ -300,19 +300,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': 'f6ebb87b52ef6b2cbd2dd6968eef2f24e7df84fd',
+  'src_internal_revision': 'f30a872d58afa35e1ff0f265d1a11c676e6d9958',
   # 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': '5515c08c2e444dca4d18fa252ea5e629f27850aa',
+  'skia_revision': 'dd985e9faf6c7d57ad626ed7cbd46cb1dc532a79',
   # 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': '225c383245605b717d45c0debf45b8f6b4fabc5b',
+  'v8_revision': 'a9fc5925179a7e303fc7f9ccb9a40e11c6405d0e',
   # 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': '177d15b3807b2db8df7168f5fef51705bd600592',
+  'angle_revision': 'd1a4b0ff5b83be6b9a1d9c4f0c1e3f36948713ce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'aec00b5fe296ad2edec9466e5bc59603ef776f5a',
+  'pdfium_revision': '6cf693686f2253201c49a68b3dc10504d199ac1d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -372,11 +372,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '663fc204a73a11aaeb16b736f157f3d6369c7a8b',
+  'catapult_revision': 'c282fc3e7b5112f924f5ef5885b5c0c676a017f5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling chromium_variations
   # and whatever else without interference from each other.
-  'chromium_variations_revision': '3aeb8c54b12300bc89701e4b2274f026786c38e3',
+  'chromium_variations_revision': 'dc907bcb5f5a617ed495d29d5eb10c1404f80c1e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -396,7 +396,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': 'b12a19e856d1c4696a5dd7f078b34a94a0296a82',
+  'devtools_frontend_revision': '8ace1760c168472d29a83716ca2255909a27aaa6',
   # 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.
@@ -420,11 +420,11 @@
   # 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': 'adaa316da8417eba6b63f981351c2908341b67dc',
+  'dawn_revision': '39fe382afd1e91d1edcb56f7687f1f3052f4aa36',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'b686c15a23031e9e18f911fc2c483f84a491789e',
+  'quiche_revision': 'ae346f567c2b7a60ccf20b4c05bc16dd94a80a0e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -456,7 +456,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': '725c8e83b6dd3e46ad334468f65902a8361a4d92',
+  'nearby_revision': '3917415c7a2833dd3122f0b5ab8b7d0fbdcbc0f6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -492,7 +492,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ffmpeg
   # and whatever else without interference from each other.
-  'ffmpeg_revision': '3f8a122bfa6c383460c4bf48db654379b68dc959',
+  'ffmpeg_revision': '468b9c8a52555953618ba1677fffb0a778dd6d87',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling beto-core
   # and whatever else without interference from each other.
@@ -503,7 +503,7 @@
   'libcxx_revision':       'd12ed9caf29e9ef0570093ba7000f62dcb1c9d34',
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:50ecf4c84d08dad3d16d14590221dd43fe65ea62',
+  'gn_version': 'git_revision:a737c2849f13f47b7808020f1a37c8403da66d92',
 
   # ninja CIPD package.
   'ninja_package': 'infra/3pp/tools/ninja/',
@@ -1287,7 +1287,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '13f9fa6614f55af1da0a158af6c69ea6e4686179',
+    '474110c7a5ee2acfa8f3781ae4a19c8b314f1e63',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1430,7 +1430,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'fJv91yWoUJjI2yDLRE8QfkEKMlWBtKpGniDKofLe8Q0C',
+          'version': '3gW3pK9QklJnSopWR3c6vVuTXHMRM5CEgXkEKlyEZSUC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -1766,7 +1766,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'eafa1f8d0782656b27401d12d223eac3e87ef834',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '3d8fef794f4f2b2667cc40d7153aba2141160109',
     'condition': 'checkout_src_internal',
   },
 
@@ -2226,7 +2226,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '478e5ab3eca30e600006d5a0a08b176fd34d3bd1',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '25b92ba79ba8cf1b4a56cc4bf9219245e7e6d140',
+    Var('chromium_git') + '/openscreen' + '@' + '351bb789b665a9f3a90a76d456a7fd493afeadb0',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '95fe35ffb383710a6e0567e958ead9a3b66e930c',
@@ -2252,7 +2252,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f2aa5c0df5f4b73f14b024288da6f900955b118a',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'c0a4867c020aad93d2707c52d6599f6a2c504be0',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2515,9 +2515,9 @@
     Var('chromium_git') + '/external/github.com/GoogleChromeLabs/text-fragments-polyfill.git' + '@' + 'c036420683f672d685e27415de0a5f5e85bdc23f',
 
   'src/third_party/tflite/src':
-    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '3322062409382997beb3280ea6e130c3f5cc6a86',
+    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '0109742147fab2b753a92090f0480a125a93818b',
 
-  'src/third_party/turbine': {
+  'src/third_party/turbine/cipd': {
       'packages': [
           {
               'package': 'chromium/third_party/turbine',
@@ -2528,16 +2528,16 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@5e29f5c97fc482e48e04309b4460b0d9be3804f3',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@725499142cb601efc3f66bdb16d75843c0760478',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@a496a34b439022750d41d2ba04fbbe416ef81c9a',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@efb6b4099ddb8fa60f62956dee592c4b94ec6a49',
-  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@b21dda0ee7a3ea4e0192a7b2b09db1df1de9d5e7',
+  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@e1782d6675b88225225e331a6318554d473c54db',
   'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@c6391a7b8cd57e79ce6b6c832c8e3043c4d9967b',
-  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@d25bd69eb7d386bc584cbca4f0f005c7ee57535f',
+  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@c758bac8bf1580b5018adafd3a2ec709237b0134',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@4c63e845962ff3b197855f3ae4907a47d0863f5a',
   'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@fbb4db92c6b2ac09003b2b8e5ceb978f4f2dda71',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@a44622ed6e1dfaa2754a65eec35ef1b19ec3e782',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@af7b0a35d009b5ad6e0b280a5b81388608ebfe39',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21',
@@ -2574,10 +2574,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '1b6371436a0a60e6b9a4ae2a40a8eba198e3af02',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '07d31bdbc0cdde5dc561d563d4898f6ed7dae38c',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '39d1b236b2c57db4725a46e1e95b066572d095f5',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '55ed9501d22f8f4e3b93714b790ef15ed3504314',
+    Var('webrtc_git') + '/src.git' + '@' + '64d68c3984e80ab0e7ac56d9c745fc4498708cd0',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -2706,7 +2706,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/boca_app/app',
-        'version': 'UmJ-yKBtv4q7oK3XVGftTA7PbIcwczo3Ed5GikUKIrIC',
+        'version': 'rqwhe7yUBE7rMWmGZU0IL4otI76ddmh2Dmgi2LGCXJ4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2761,7 +2761,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'VgXGUWHT5H59PGlPuMAbZjmQyA0xwg0qceeeCjfQQyoC',
+        'version': 'kYkYnjLM4TH_rUv0XOvJX4nYEU5Fvq2XXX2QojHNXTAC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4356,7 +4356,7 @@
   # Dependencies from src_internal
   'src/chromeos/ash/resources/internal': {
       'url': Var('chrome_git') + '/chrome/chromeos/ash/resources/internal.git' + '@' +
-        '23ef9d168e94db8a6f5630e059dabb2c0b24994e',
+        '6f536dc3c7bb9557fe54bb4ebef02eb9143e8a92',
       'condition': 'checkout_src_internal and checkout_chromeos',
   },
 
@@ -4585,7 +4585,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        'bae07ac124624f1389990348b3d2df951083f28d',
+        '42b598fff77ed715391248780615f506aea2860f',
       'condition': 'checkout_src_internal',
   },
 
@@ -4657,7 +4657,7 @@
 
   'src/remoting/internal': {
       'url': Var('chrome_git') + '/chrome/remoting/internal.git' + '@' +
-        '9d8de68788f20252dc2ece395077ac3b7ffdcfbe',
+        '1cf2ed8f6e38a924f1b2754a880d039516611493',
       'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_permission_manager.cc b/android_webview/browser/aw_permission_manager.cc
index 1aee89f..621c2acc 100644
--- a/android_webview/browser/aw_permission_manager.cc
+++ b/android_webview/browser/aw_permission_manager.cc
@@ -15,6 +15,7 @@
 #include "android_webview/browser/aw_context_permissions_delegate.h"
 #include "android_webview/browser/aw_settings.h"
 #include "android_webview/common/aw_features.h"
+#include "base/check.h"
 #include "base/containers/contains.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
@@ -80,9 +81,7 @@
     }
 
     std::string key = GetCacheKey(requesting_origin, embedding_origin);
-    if (key.empty()) {
-      NOTREACHED();
-    }
+    CHECK(!key.empty());
     pmi_result_cache_[key] = status;
   }
 
diff --git a/android_webview/browser/gfx/aw_draw_fn_impl.cc b/android_webview/browser/gfx/aw_draw_fn_impl.cc
index cfb52d1..ae64d08 100644
--- a/android_webview/browser/gfx/aw_draw_fn_impl.cc
+++ b/android_webview/browser/gfx/aw_draw_fn_impl.cc
@@ -5,6 +5,7 @@
 #include "android_webview/browser/gfx/aw_draw_fn_impl.h"
 
 #include <sys/prctl.h>
+
 #include <utility>
 
 #include "android_webview/browser/gfx/aw_vulkan_context_provider.h"
@@ -14,8 +15,8 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
 #include "third_party/skia/include/private/chromium/GrVkSecondaryCBDrawContext.h"
 #include "ui/gfx/color_space.h"
 
diff --git a/android_webview/browser/gfx/aw_vulkan_context_provider.cc b/android_webview/browser/gfx/aw_vulkan_context_provider.cc
index fdd0d757..b86bdad 100644
--- a/android_webview/browser/gfx/aw_vulkan_context_provider.cc
+++ b/android_webview/browser/gfx/aw_vulkan_context_provider.cc
@@ -20,7 +20,7 @@
 #include "gpu/vulkan/vulkan_fence_helper.h"
 #include "gpu/vulkan/vulkan_function_pointers.h"
 #include "gpu/vulkan/vulkan_util.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h"
 #include "third_party/skia/include/gpu/vk/VulkanBackendContext.h"
 #include "third_party/skia/include/gpu/vk/VulkanExtensions.h"
diff --git a/android_webview/browser/gfx/aw_vulkan_context_provider.h b/android_webview/browser/gfx/aw_vulkan_context_provider.h
index 739a0fc..df0f5b4 100644
--- a/android_webview/browser/gfx/aw_vulkan_context_provider.h
+++ b/android_webview/browser/gfx/aw_vulkan_context_provider.h
@@ -12,7 +12,7 @@
 #include "components/viz/common/gpu/vulkan_context_provider.h"
 #include "gpu/vulkan/vulkan_device_queue.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "third_party/skia/include/private/chromium/GrVkSecondaryCBDrawContext.h"
 
 struct AwDrawFn_InitVkParams;
diff --git a/android_webview/docs/test-instructions.md b/android_webview/docs/test-instructions.md
index f20e0cf6..db124f1 100644
--- a/android_webview/docs/test-instructions.md
+++ b/android_webview/docs/test-instructions.md
@@ -213,6 +213,17 @@
 $ (shell) third_party/android_platform/development/scripts/stack --output-directory=out/Release /tmp/t1
 ```
 
+#### Instrumentation test process modes
+
+You may use `--webview-process-mode` argument to run tests only in the
+specified process mode. The valid switch values are `multiple` and `single`.
+When the argument is not set (default), both process modes execute. Note that
+the argument acts as an additional filter on top of the
+[`OnlyRunIn` test annotation](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/test/shell/src/org/chromium/android_webview/test/OnlyRunIn.java;drc=02c4c92d88aecbc14e715fd7fcac842d5dd814fe;l=25).
+
+Also note that in chromium CQ builders and all builders running tests on
+Android R and plus, `--webview-process-mode=multiple` is appended to the test
+command so that the instrumentation tests only run in multiple processes mode.
 
 ## External tests
 
diff --git a/android_webview/test/shell/src/draw_fn/context_manager.cc b/android_webview/test/shell/src/draw_fn/context_manager.cc
index e866ed3..29fb98ad 100644
--- a/android_webview/test/shell/src/draw_fn/context_manager.cc
+++ b/android_webview/test/shell/src/draw_fn/context_manager.cc
@@ -28,17 +28,17 @@
 #include "third_party/skia/include/core/SkDrawable.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkSurfaceProps.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
 #include "third_party/skia/include/gpu/MutableTextureState.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrBackendDrawableInfo.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h"
-#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
 #include "third_party/skia/include/gpu/vk/VulkanBackendContext.h"
 #include "third_party/skia/include/gpu/vk/VulkanExtensions.h"
 #include "third_party/skia/include/gpu/vk/VulkanMutableTextureState.h"
diff --git a/ash/app_list/app_list_presenter_unittest.cc b/ash/app_list/app_list_presenter_unittest.cc
index 410c973..9d29941 100644
--- a/ash/app_list/app_list_presenter_unittest.cc
+++ b/ash/app_list/app_list_presenter_unittest.cc
@@ -560,33 +560,23 @@
 // tablet/clamshell mode and drag and whether drag and drop refactor is enabled.
 class AppListBubbleAndTabletDragTest
     : public AppListBubbleAndTabletTestBase,
-      public testing::WithParamInterface<std::tuple<bool, bool>> {
+      public testing::WithParamInterface<bool> {
  public:
   AppListBubbleAndTabletDragTest()
       : AppListBubbleAndTabletTestBase(
-            /*tablet_mode=*/std::get<0>(GetParam())) {
-    scoped_feature_list_.InitWithFeatureState(
-        app_list_features::kDragAndDropRefactor,
-        is_drag_drop_refactor_enabled());
-  }
+            /*tablet_mode=*/GetParam()) {}
   AppListBubbleAndTabletDragTest(const AppListBubbleAndTabletDragTest&) =
       delete;
   AppListBubbleAndTabletDragTest& operator=(
       const AppListBubbleAndTabletDragTest&) = delete;
   ~AppListBubbleAndTabletDragTest() override = default;
-
-  bool is_drag_drop_refactor_enabled() { return std::get<1>(GetParam()); }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Instantiate the values in the parameterized tests. The boolean
-// determines whether to run the test in tablet mode with or without drag and
-// drop refactor feature enabled.
+// determines whether to run the test in tablet mode.
 INSTANTIATE_TEST_SUITE_P(TabletMode,
                          AppListBubbleAndTabletDragTest,
-                         testing::Combine(testing::Bool(), testing::Bool()));
+                         testing::Bool());
 
 // Tests only tablet mode.
 class AppListTabletTest : public AppListBubbleAndTabletTestBase {
@@ -598,17 +588,13 @@
 };
 
 // Used to test app_list behavior with a populated apps_grid.
-class PopulatedAppListTest : public AshTestBase,
-                             public testing::WithParamInterface<bool> {
+class PopulatedAppListTest : public AshTestBase {
  public:
-  PopulatedAppListTest() : is_drag_drop_refactor_enabled_(GetParam()) {}
+  PopulatedAppListTest() {}
   ~PopulatedAppListTest() override = default;
 
   void SetUp() override {
     AppListConfigProvider::Get().ResetForTesting();
-    scoped_feature_list_.InitWithFeatureState(
-        app_list_features::kDragAndDropRefactor,
-        is_drag_drop_refactor_enabled_);
     AshTestBase::SetUp();
 
     // Make the display big enough to hold the app list.
@@ -703,21 +689,13 @@
     app_list_view_->OnParentWindowBoundsChanged();
   }
 
-  bool is_drag_drop_refactor_enabled() {
-    return is_drag_drop_refactor_enabled_;
-  }
-
   std::unique_ptr<test::AppsGridViewTestApi> apps_grid_test_api_;
   raw_ptr<AppListView, DanglingUntriaged> app_list_view_ =
       nullptr;  // Owned by native widget.
   raw_ptr<PagedAppsGridView, DanglingUntriaged> apps_grid_view_ =
       nullptr;  // Owned by |app_list_view_|.
-  base::test::ScopedFeatureList scoped_feature_list_;
-  const bool is_drag_drop_refactor_enabled_;
 };
 
-INSTANTIATE_TEST_SUITE_P(All, PopulatedAppListTest, testing::Bool());
-
 // Subclass of PopulatedAppListTest which enables the virtual keyboard.
 class PopulatedAppListWithVKEnabledTest : public PopulatedAppListTest {
  public:
@@ -730,20 +708,6 @@
     PopulatedAppListTest::SetUp();
   }
 };
-INSTANTIATE_TEST_SUITE_P(All,
-                         PopulatedAppListWithVKEnabledTest,
-                         testing::Values(true));
-
-// Subclass of PopulatedAppListTest which tests legacy behavior for the apps
-// grid without the drag and drop refactoring enabled.
-class PopulatedAppListLegacyBehaviourTest : public PopulatedAppListTest {
- public:
-  PopulatedAppListLegacyBehaviourTest() = default;
-  ~PopulatedAppListLegacyBehaviourTest() override = default;
-};
-INSTANTIATE_TEST_SUITE_P(All,
-                         PopulatedAppListLegacyBehaviourTest,
-                         testing::Values(false));
 
 // Subclass of PopulatedAppListTest which tests the apps grid drag behavior
 // interrumpted during a screen rotation. Enables drag and drop refactor by
@@ -753,9 +717,6 @@
   PopulatedAppListScreenRotationTest() = default;
   ~PopulatedAppListScreenRotationTest() override = default;
 };
-INSTANTIATE_TEST_SUITE_P(All,
-                         PopulatedAppListScreenRotationTest,
-                         testing::Values(true));
 
 // Verify that open folders are closed after sorting apps grid.
 TEST_P(AppListBubbleAndTabletTest, SortingClosesOpenFolderView) {
@@ -2420,7 +2381,7 @@
 
 // Tests that mouse app list item drag is cancelled when mouse capture is lost
 // (e.g. on screen rotation).
-TEST_P(PopulatedAppListTest, CancelItemDragOnMouseCaptureLoss) {
+TEST_F(PopulatedAppListTest, CancelItemDragOnMouseCaptureLoss) {
   InitializeAppsGrid();
   PopulateApps(apps_grid_test_api_->TilesPerPageInPagedGrid(0) + 1);
 
@@ -2456,7 +2417,7 @@
 
 // Tests that app list item drag gets canceled if the dragged app list item gets
 // deleted.
-TEST_P(PopulatedAppListTest, CancelItemDragOnDragItemDeletion) {
+TEST_F(PopulatedAppListTest, CancelItemDragOnDragItemDeletion) {
   InitializeAppsGrid();
   PopulateApps(4);
 
@@ -2501,7 +2462,7 @@
 
 // Tests that app list item drag in folder gets canceled if the dragged app list
 // item gets deleted.
-TEST_P(PopulatedAppListTest, CancelFolderItemDragOnDragItemDeletion) {
+TEST_F(PopulatedAppListTest, CancelFolderItemDragOnDragItemDeletion) {
   InitializeAppsGrid();
   PopulateApps(2);
   AppListFolderItem* folder = CreateAndPopulateFolderWithApps(3);
@@ -2554,7 +2515,7 @@
 
 // Tests that app list item drag from folder to root apps grid gets canceled if
 // the dragged app list item gets deleted.
-TEST_P(PopulatedAppListTest, CancelFolderItemReparentDragOnDragItemDeletion) {
+TEST_F(PopulatedAppListTest, CancelFolderItemReparentDragOnDragItemDeletion) {
   InitializeAppsGrid();
   PopulateApps(2);
   AppListFolderItem* folder = CreateAndPopulateFolderWithApps(3);
@@ -2595,8 +2556,7 @@
     event_generator->MoveTouch(
         apps_grid_view_->GetItemViewAt(3)->GetBoundsInScreen().CenterPoint());
     EXPECT_TRUE(apps_grid_view_->IsDragging());
-    EXPECT_EQ(!is_drag_drop_refactor_enabled(),
-              folder_view()->items_grid_view()->IsDragging());
+    EXPECT_FALSE(folder_view()->items_grid_view()->IsDragging());
   }));
   tasks.push_back(base::BindLambdaForTesting([&]() {
     // Delete the dragged item.
@@ -2626,7 +2586,7 @@
   helper->DismissAndRunLoop();
 }
 
-TEST_P(PopulatedAppListTest,
+TEST_F(PopulatedAppListTest,
        CancelFolderItemReparentDragOnDragItemAndFolderDeletion) {
   InitializeAppsGrid();
   PopulateApps(2);
@@ -2670,8 +2630,7 @@
         apps_grid_view_->GetItemViewAt(3)->GetBoundsInScreen().CenterPoint());
 
     EXPECT_TRUE(apps_grid_view_->IsDragging());
-    EXPECT_EQ(!is_drag_drop_refactor_enabled(),
-              folder_view()->items_grid_view()->IsDragging());
+    EXPECT_FALSE(folder_view()->items_grid_view()->IsDragging());
   }));
   tasks.push_back(base::BindLambdaForTesting([&]() {
     // Leave the dragged item as it's folder only child, and then delete it,
@@ -2706,7 +2665,7 @@
 
 // Tests that apps grid item layers are not destroyed immediately after item
 // drag ends.
-TEST_P(PopulatedAppListTest,
+TEST_F(PopulatedAppListTest,
        ItemLayersNotDestroyedDuringBoundsAnimationAfterDrag) {
   InitializeAppsGrid();
   const int kItemCount = 5;
@@ -2762,50 +2721,9 @@
   }
 }
 
-// Tests that apps grid item drag operation can continue normally after display
-// rotation (and app list config change).
-TEST_P(PopulatedAppListLegacyBehaviourTest,
-       ScreenRotationDuringAppsGridItemDrag) {
-  // Set the display dimensions so rotation also changes the app list config.
-  UpdateDisplay("1200x600");
-
-  InitializeAppsGrid();
-  PopulateApps(apps_grid_test_api_->TilesPerPageInPagedGrid(0) + 1);
-
-  AppListItemView* const dragged_view = apps_grid_view_->GetItemViewAt(0);
-
-  // Start dragging the first item.
-  ui::test::EventGenerator* event_generator = GetEventGenerator();
-  event_generator->MoveTouch(dragged_view->GetBoundsInScreen().CenterPoint());
-  event_generator->PressTouch();
-  ASSERT_TRUE(dragged_view->FireTouchDragTimerForTest());
-
-  event_generator->MoveTouch(
-      apps_grid_view_->GetItemViewAt(2)->GetBoundsInScreen().CenterPoint());
-
-  UpdateDisplay("600x1200");
-  // AppListView is usually notified of display bounds changes by
-  // AppListPresenter, though the test delegate implementation does not
-  // track display metrics changes, so OnParentWindowBoundsChanged() has to be
-  // explicitly called here.
-  app_list_view_->OnParentWindowBoundsChanged();
-
-  // End drag at the in between items 1 and 2 - note that these have been
-  // translated one slot left to fill in space left by the dragged view, so the
-  // expected drop slot is actually slot 1.
-  gfx::Point target =
-      apps_grid_view_->GetItemViewAt(2)->GetBoundsInScreen().left_center();
-  event_generator->MoveTouch(target);
-  event_generator->ReleaseTouch();
-
-  EXPECT_EQ("Item 1", apps_grid_view_->GetItemViewAt(0)->item()->id());
-  EXPECT_EQ("Item 0", apps_grid_view_->GetItemViewAt(1)->item()->id());
-  EXPECT_EQ("Item 2", apps_grid_view_->GetItemViewAt(2)->item()->id());
-}
-
 // Tests screen rotation during apps grid item drag where the drag gets
 // canceled.
-TEST_P(PopulatedAppListScreenRotationTest,
+TEST_F(PopulatedAppListScreenRotationTest,
        ScreenRotationDuringAppsGridItemDragCancelsOperation) {
   // Set the display dimensions so rotation also changes the app list config.
   UpdateDisplay("1200x600");
@@ -2838,7 +2756,7 @@
 
 // Tests screen rotation during a folder apps grid item reparent drag where the
 // drag gets canceled.
-TEST_P(PopulatedAppListScreenRotationTest,
+TEST_F(PopulatedAppListScreenRotationTest,
        ScreenRotationDuringFolderAppsGridItemDragCancelsOperation) {
   InitializeAppsGrid();
   PopulateApps(2);
@@ -2882,7 +2800,7 @@
 // Tests screen rotation during apps grid item drag where the drag gets
 // canceled after a page change. Verifies the correct page is selected for the
 // item that started the drag.
-TEST_P(PopulatedAppListScreenRotationTest,
+TEST_F(PopulatedAppListScreenRotationTest,
        ScreenRotationDuringAppsGridItemWithPageChange) {
   // Set the display dimensions so rotation also changes the app list config.
   UpdateDisplay("1200x600");
@@ -2932,7 +2850,7 @@
 // Tests screen rotation during apps grid item drag where the drag gets
 // canceled after a page change. Verifies that the correct page is selected for
 // them item that started the drag even if the item ends up in a different page.
-TEST_P(PopulatedAppListScreenRotationTest,
+TEST_F(PopulatedAppListScreenRotationTest,
        ScreenRotationDuringAppsGridItemWithPageNumberChange) {
   // Set the display dimensions so rotation also changes the app list config.
   UpdateDisplay("1200x600");
@@ -2980,153 +2898,6 @@
   EXPECT_EQ("Item 2", apps_grid_view_->GetItemViewAt(2)->item()->id());
 }
 
-// Tests screen rotation during apps grid item drag where the drag item ends up
-// in page-scroll area. Tests that the apps grid page scrolls without a crash,
-// and that releasing drag does not change the item position in the model.
-TEST_P(PopulatedAppListLegacyBehaviourTest,
-       ScreenRotationDuringAppsGridItemDragWithPageScroll) {
-  // Set the display dimensions so rotation also changes the app list config.
-  UpdateDisplay("1200x600");
-
-  InitializeAppsGrid();
-  PopulateApps(apps_grid_test_api_->TilesPerPageInPagedGrid(0) +
-               apps_grid_test_api_->TilesPerPageInPagedGrid(1));
-
-  AppListItemView* const dragged_view = apps_grid_view_->GetItemViewAt(0);
-
-  // Start dragging the first item.
-  ui::test::EventGenerator* event_generator = GetEventGenerator();
-  event_generator->MoveTouch(dragged_view->GetBoundsInScreen().CenterPoint());
-  event_generator->PressTouch();
-  ASSERT_TRUE(dragged_view->FireTouchDragTimerForTest());
-
-  // Move the item close to screen edge, so it ends up in area that triggers
-  // page scroll after rotation.
-  event_generator->MoveTouch(app_list_view_->GetBoundsInScreen().left_center() +
-                             gfx::Vector2d(64, 0));
-
-  RotateScreen();
-
-  ASSERT_EQ(2, apps_grid_view_->pagination_model()->total_pages());
-  event_generator->MoveTouchBy(0, 10);
-  EXPECT_TRUE(apps_grid_view_->FirePageFlipTimerForTest());
-  // Move the pointer away from the grid horizontally for it to get out ouf apps
-  // grid drag buffer, so the release results in a canceled drag
-  // The grid is spread out vertically so there is no area under the grid
-  // that's: in page flip area, outside of apps grid drag buffer, and outside of
-  // shelf bounds.
-  event_generator->MoveTouchBy(0, 270);
-  event_generator->ReleaseTouch();
-
-  // The model state should not have been changed.
-  EXPECT_EQ("Item 0", apps_grid_view_->GetItemViewAt(0)->item()->id());
-  EXPECT_EQ("Item 1", apps_grid_view_->GetItemViewAt(1)->item()->id());
-  EXPECT_EQ("Item 2", apps_grid_view_->GetItemViewAt(2)->item()->id());
-}
-
-// Tests screen rotation while app list folder item is in progress, and the item
-// remains in the folder bounds during the drag.
-TEST_P(PopulatedAppListLegacyBehaviourTest,
-       ScreenRotationDuringFolderItemDrag) {
-  // Set the display dimensions so rotation also changes the app list config.
-  UpdateDisplay("1200x600");
-
-  InitializeAppsGrid();
-  PopulateApps(2);
-  AppListFolderItem* folder = CreateAndPopulateFolderWithApps(3);
-  PopulateApps(10);
-
-  // Tap the folder item to show it.
-  GestureTapOn(apps_grid_view_->GetItemViewAt(2));
-  ASSERT_TRUE(AppListIsInFolderView());
-
-  // Start dragging the first item in the active folder.
-  AppListItemView* const dragged_view =
-      folder_view()->items_grid_view()->GetItemViewAt(0);
-  ui::test::EventGenerator* event_generator = GetEventGenerator();
-  event_generator->MoveTouch(dragged_view->GetBoundsInScreen().CenterPoint());
-  event_generator->PressTouch();
-  ASSERT_TRUE(dragged_view->FireTouchDragTimerForTest());
-
-  // Drag the item within the folder bounds.
-  event_generator->MoveTouch(
-      apps_grid_view_->GetItemViewAt(2)->GetBoundsInScreen().CenterPoint());
-
-  UpdateDisplay("600x1200");
-  // AppListView is usually notified of display bounds changes by
-  // AppListPresenter, though the test delegate implementation does not
-  // track display metrics changes, so OnParentWindowBoundsChanged() has to be
-  // explicitly called here.
-  app_list_view_->OnParentWindowBoundsChanged();
-
-  // The current behavior on app list bounds change is to close the active
-  // folder, canceling the drag.
-  EXPECT_FALSE(AppListIsInFolderView());
-  EXPECT_FALSE(apps_grid_view_->IsDragging());
-  EXPECT_FALSE(folder_view()->items_grid_view()->IsDragging());
-
-  EXPECT_EQ("Item 0", apps_grid_view_->GetItemViewAt(0)->item()->id());
-  EXPECT_EQ("Item 1", apps_grid_view_->GetItemViewAt(1)->item()->id());
-  EXPECT_EQ(folder->id(), apps_grid_view_->GetItemViewAt(2)->item()->id());
-  EXPECT_EQ("Item 5", apps_grid_view_->GetItemViewAt(3)->item()->id());
-}
-
-// Tests that app list folder item reparenting drag (where a folder item is
-// dragged outside the folder bounds, and dropped within the apps grid) can
-// continue normally after screen rotation.
-TEST_P(PopulatedAppListLegacyBehaviourTest,
-       ScreenRotationDuringAppsGridItemReparentDrag) {
-  UpdateDisplay("1200x600");
-
-  InitializeAppsGrid();
-  PopulateApps(2);
-  AppListFolderItem* folder = CreateAndPopulateFolderWithApps(3);
-  PopulateApps(10);
-
-  // Tap the folder item to show it.
-  GestureTapOn(apps_grid_view_->GetItemViewAt(2));
-  ASSERT_TRUE(AppListIsInFolderView());
-
-  // Start dragging the first item in the active folder.
-  AppListItemView* dragged_view =
-      folder_view()->items_grid_view()->GetItemViewAt(0);
-  AppListItem* dragged_item = dragged_view->item();
-  ui::test::EventGenerator* event_generator = GetEventGenerator();
-  event_generator->MoveTouch(dragged_view->GetBoundsInScreen().CenterPoint());
-  event_generator->PressTouch();
-  ASSERT_TRUE(dragged_view->FireTouchDragTimerForTest());
-
-  // Drag the item outside the folder bounds.
-  event_generator->MoveTouch(
-      apps_grid_view_->GetItemViewAt(1)->GetBoundsInScreen().CenterPoint());
-  event_generator->MoveTouchBy(2, 2);
-
-  // Fire reparenting timer.
-  EXPECT_TRUE(
-      folder_view()->items_grid_view()->FireFolderItemReparentTimerForTest());
-  EXPECT_FALSE(AppListIsInFolderView());
-
-  UpdateDisplay("600x1200");
-  // AppListView is usually notified of display bounds changes by
-  // AppListPresenter, though the test delegate implementation does not
-  // track display metrics changes, so OnParentWindowBoundsChanged() has to be
-  // explicitly called here.
-  app_list_view_->OnParentWindowBoundsChanged();
-
-  gfx::Point target =
-      apps_grid_view_->GetItemViewAt(1)->GetBoundsInScreen().right_center();
-  // End drag at the in between items 1 and 2.
-  event_generator->MoveTouch(target);
-  event_generator->ReleaseTouch();
-
-  // Verify the new item location within the apps grid.
-  EXPECT_EQ("Item 0", apps_grid_view_->GetItemViewAt(0)->item()->id());
-  EXPECT_EQ("Item 1", apps_grid_view_->GetItemViewAt(1)->item()->id());
-  EXPECT_EQ(dragged_item->id(),
-            apps_grid_view_->GetItemViewAt(2)->item()->id());
-  EXPECT_EQ(folder->id(), apps_grid_view_->GetItemViewAt(3)->item()->id());
-}
-
 // Tests that app list folder item reparenting drag to another folder.
 TEST_P(AppListBubbleAndTabletDragTest, AppsGridItemReparentToFolderDrag) {
   UpdateDisplay("1200x600");
@@ -3195,7 +2966,7 @@
 
 // Tests that an item can be removed just after creating a folder that contains
 // that item. See https://crbug.com/1083942
-TEST_P(PopulatedAppListTest, RemoveFolderItemAfterFolderCreation) {
+TEST_F(PopulatedAppListTest, RemoveFolderItemAfterFolderCreation) {
   InitializeAppsGrid();
   const int kItemCount = 6;
   PopulateApps(kItemCount);
@@ -3273,7 +3044,7 @@
   apps_grid_view_->GetWidget()->LayoutRootViewIfNecessary();
 }
 
-TEST_P(PopulatedAppListTest, ReparentLastFolderItemAfterFolderCreation) {
+TEST_F(PopulatedAppListTest, ReparentLastFolderItemAfterFolderCreation) {
   InitializeAppsGrid();
   const int kItemCount = 5;
   PopulateApps(kItemCount);
@@ -3348,7 +3119,7 @@
   apps_grid_view_->GetWidget()->LayoutRootViewIfNecessary();
 }
 
-TEST_P(PopulatedAppListWithVKEnabledTest,
+TEST_F(PopulatedAppListWithVKEnabledTest,
        TappingAppsGridClosesVirtualKeyboard) {
   InitializeAppsGrid();
   PopulateApps(2);
@@ -3973,7 +3744,7 @@
 
 // Tests that the touch selection menu created when tapping an open folder's
 // folder name view be interacted with.
-TEST_P(PopulatedAppListTest, TouchSelectionMenu) {
+TEST_F(PopulatedAppListTest, TouchSelectionMenu) {
   InitializeAppsGrid();
 
   AppListFolderItem* folder_item = CreateAndPopulateFolderWithApps(4);
diff --git a/ash/auth/active_session_auth_controller_impl.cc b/ash/auth/active_session_auth_controller_impl.cc
index 8848609..64e6477 100644
--- a/ash/auth/active_session_auth_controller_impl.cc
+++ b/ash/auth/active_session_auth_controller_impl.cc
@@ -12,6 +12,7 @@
 #include "ash/auth/views/auth_view_utils.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/auth/active_session_auth_controller.h"
+#include "ash/public/cpp/login_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -20,14 +21,17 @@
 #include "base/check_op.h"
 #include "base/functional/bind.h"
 #include "base/i18n/time_formatting.h"
+#include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chromeos/ash/components/cryptohome/auth_factor_conversions.h"
 #include "chromeos/ash/components/cryptohome/constants.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
 #include "chromeos/ash/components/login/auth/auth_performer.h"
+#include "chromeos/ash/components/login/auth/public/auth_callbacks.h"
 #include "chromeos/ash/components/login/auth/public/auth_session_intent.h"
 #include "chromeos/ash/components/login/auth/public/session_auth_factors.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
@@ -46,6 +50,10 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
+// Enable VLOG level 1.
+#undef ENABLED_VLOG_LEVEL
+#define ENABLED_VLOG_LEVEL 1
+
 namespace ash {
 
 namespace {
@@ -117,6 +125,15 @@
     case ActiveSessionAuthControllerImpl::ActiveSessionAuthState::
         kPinAuthSucceeded:
       return "PinAuthSucceeded";
+    case ActiveSessionAuthControllerImpl::ActiveSessionAuthState::
+        kFingerprintAuthSucceeded:
+      return "FingerprintAuthSucceeded";
+    case ActiveSessionAuthControllerImpl::ActiveSessionAuthState::
+        kFingerprintAuthSucceededWaiting:
+      return "FingerprintAuthSucceededWaiting";
+    case ActiveSessionAuthControllerImpl::ActiveSessionAuthState::
+        kCloseRequested:
+      return "CloseRequested";
   }
   NOTREACHED();
 }
@@ -177,17 +194,24 @@
 }
 
 void ActiveSessionAuthControllerImpl::TestApi::Close() {
-  controller_->Close();
+  controller_->StartClose();
 }
 
 ActiveSessionAuthControllerImpl::ActiveSessionAuthControllerImpl() = default;
 ActiveSessionAuthControllerImpl::~ActiveSessionAuthControllerImpl() = default;
 
+bool ActiveSessionAuthControllerImpl::IsSucceedState() const {
+  return state_ == ActiveSessionAuthState::kPasswordAuthSucceeded ||
+         state_ == ActiveSessionAuthState::kPinAuthSucceeded ||
+         state_ == ActiveSessionAuthState::kFingerprintAuthSucceeded ||
+         state_ == ActiveSessionAuthState::kFingerprintAuthSucceededWaiting;
+}
+
 bool ActiveSessionAuthControllerImpl::ShowAuthDialog(
     std::unique_ptr<AuthRequest> auth_request) {
   CHECK(auth_request);
-  LOG(WARNING) << "Show is requested with reason: "
-               << ReasonToString(auth_request->GetAuthReason());
+  VLOG(1) << "Show is requested with reason: "
+          << ReasonToString(auth_request->GetAuthReason());
   if (IsShown()) {
     LOG(ERROR) << "ActiveSessionAuthController widget is already exists.";
     auth_request->NotifyAuthFailure();
@@ -224,14 +248,22 @@
   return widget_ != nullptr;
 }
 
+void ActiveSessionAuthControllerImpl::SetFingerprintClient(
+    ActiveSessionFingerprintClient* fp_client) {
+  CHECK_NE(fp_client_ == nullptr, fp_client == nullptr);
+  fp_client_ = fp_client;
+}
+
 void ActiveSessionAuthControllerImpl::OnAuthSessionStarted(
     bool user_exists,
     std::unique_ptr<UserContext> user_context,
     std::optional<AuthenticationError> authentication_error) {
+  user_context_ = std::move(user_context);
+
   if (!user_exists || authentication_error.has_value()) {
     LOG(ERROR) << "Failed to start auth session, code "
                << authentication_error->get_cryptohome_code();
-    Close();
+    StartClose();
     return;
   }
 
@@ -239,7 +271,6 @@
   UserDataAuthClient::Get()->AddAuthFactorStatusUpdateObserver(this);
 
   available_factors_.Clear();
-  user_context_ = std::move(user_context);
   const auto& auth_factors = user_context_->GetAuthFactorsData();
 
   if (auth_factors.FindAnyPasswordFactor()) {
@@ -250,9 +281,108 @@
     available_factors_.Put(AuthInputType::kPin);
   }
 
+  MaybePrepareFingerprint(
+      BindOnce(&ActiveSessionAuthControllerImpl::AuthFactorsAreReady,
+               weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ActiveSessionAuthControllerImpl::MaybePrepareFingerprint(
+    AuthFactorsReadyCallback on_auth_factors_ready) {
+  // If the fingerprint factor is allowed to use by the user for the current
+  // reason then start it before initialize the UI.
+  if (fp_client_ && fp_client_->IsFingerprintAvailable(
+                        auth_request_->GetAuthReason(), account_id_)) {
+    VLOG(1) << "PrepareFingerprintAuth started.";
+    fp_client_->PrepareFingerprintAuth(
+        std::move(user_context_),
+        /* auth_ready_callback = */
+        base::BindOnce(&ActiveSessionAuthControllerImpl::OnFingerprintReady,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       std::move(on_auth_factors_ready)),
+        /* on_scan_callback = */
+        base::BindRepeating(&ActiveSessionAuthControllerImpl::OnFingerprintScan,
+                            weak_ptr_factory_.GetWeakPtr()));
+    return;
+  }
+  std::move(on_auth_factors_ready).Run(std::move(user_context_));
+}
+
+void ActiveSessionAuthControllerImpl::OnFingerprintReady(
+    AuthFactorsReadyCallback on_auth_factors_ready,
+    std::unique_ptr<UserContext> user_context,
+    std::optional<AuthenticationError> authentication_error) {
+  if (authentication_error.has_value()) {
+    LOG(ERROR) << "Failed to start fingerprint auth session - only "
+                  "non-fingerprint factors will be available.";
+  } else {
+    available_factors_.Put(AuthInputType::kFingerprint);
+  }
+  std::move(on_auth_factors_ready).Run(std::move(user_context));
+}
+
+void ActiveSessionAuthControllerImpl::AuthFactorsAreReady(
+    std::unique_ptr<UserContext> user_context) {
+  user_context_ = std::move(user_context);
   InitUi();
 }
 
+void ActiveSessionAuthControllerImpl::OnFingerprintScan(
+    const FingerprintAuthScanResult scan_result) {
+  CHECK_NE(state_, ActiveSessionAuthState::kWaitForInit);
+  // Avoid unnecessary processing if we've already initiated close.
+  if (IsSucceedState() || state_ == ActiveSessionAuthState::kCloseRequested) {
+    return;
+  }
+  switch (scan_result) {
+    case FingerprintAuthScanResult::kSuccess:
+      if (state_ == ActiveSessionAuthState::kPasswordAuthStarted ||
+          state_ == ActiveSessionAuthState::kPinAuthStarted) {
+        SetState(ActiveSessionAuthState::kFingerprintAuthSucceededWaiting);
+        // The user_context_ is not available, we have to wait for the
+        // OnAuthComplete callback to have UserContext.
+        return;
+      }
+      HandleFingerprintAuthSuccess();
+      return;
+    case FingerprintAuthScanResult::kTooManyAttempts:
+      uma_recorder_.RecordAuthFailed(AuthInputType::kFingerprint);
+
+      contents_view_->SetFingerprintState(
+          FingerprintState::DISABLED_FROM_ATTEMPTS);
+      return;
+    case FingerprintAuthScanResult::kFailed:
+      uma_recorder_.RecordAuthFailed(AuthInputType::kFingerprint);
+
+      contents_view_->NotifyFingerprintAuthFailure();
+      return;
+    case FingerprintAuthScanResult::kFatalError:
+      contents_view_->SetFingerprintState(FingerprintState::UNAVAILABLE);
+      return;
+  }
+  NOTREACHED();
+}
+
+void ActiveSessionAuthControllerImpl::OnFingerprintSuccess(
+    std::unique_ptr<UserContext> user_context,
+    std::optional<AuthenticationError> authentication_error) {
+  if (authentication_error.has_value()) {
+    LOG(ERROR) << "Authentication error during OnFingerprintSuccess code: "
+               << authentication_error->get_cryptohome_code();
+  }
+  user_context_ = std::move(user_context);
+  StartClose();
+}
+
+void ActiveSessionAuthControllerImpl::HandleFingerprintAuthSuccess() {
+  CHECK(user_context_);
+  uma_recorder_.RecordAuthSucceeded(AuthInputType::kFingerprint);
+  SetState(ActiveSessionAuthState::kFingerprintAuthSucceeded);
+  auth_performer_->AuthenticateWithLegacyFingerprint(
+      std::move(user_context_),
+      base::BindOnce(&ActiveSessionAuthControllerImpl::OnFingerprintSuccess,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
 void ActiveSessionAuthControllerImpl::InitUi() {
   auto contents_view = std::make_unique<ActiveSessionAuthView>(
       account_id_, title_, description_, available_factors_);
@@ -270,39 +400,57 @@
       ->NotifyInSessionAuthDialogShown();
 }
 
-void ActiveSessionAuthControllerImpl::Close() {
-  LOG(WARNING) << "Close with : " << ActiveSessionAuthStateToString(state_)
-               << " state.";
+void ActiveSessionAuthControllerImpl::StartClose() {
+  VLOG(1) << "Close with : " << ActiveSessionAuthStateToString(state_)
+          << " state.";
+
+  CHECK(user_context_);
+  CHECK(auth_request_);
+  CHECK(auth_performer_);
   uma_recorder_.RecordClose();
   contents_view_observer_.Reset();
   CHECK(contents_view_);
   contents_view_->RemoveObserver(this);
   contents_view_ = nullptr;
-  SetState(ActiveSessionAuthState::kWaitForInit);
 
   pending_pin_factor_status_update_.reset();
   UserDataAuthClient::Get()->RemoveAuthFactorStatusUpdateObserver(this);
-  if (auth_performer_) {
-    auth_performer_->InvalidateCurrentAttempts();
-    auth_performer_.reset();
+
+  auth_performer_->InvalidateCurrentAttempts();
+  if (fp_client_ && available_factors_.Has(AuthInputType::kFingerprint)) {
+    fp_client_->TerminateFingerprintAuth(
+        std::move(user_context_),
+        base::BindOnce(&ActiveSessionAuthControllerImpl::CompleteClose,
+                       weak_ptr_factory_.GetWeakPtr()));
+    return;
   }
+  CompleteClose(std::move(user_context_), std::nullopt);
+}
+
+void ActiveSessionAuthControllerImpl::CompleteClose(
+    std::unique_ptr<UserContext> user_context,
+    std::optional<AuthenticationError> authentication_error) {
+  user_context_ = std::move(user_context);
+  CHECK(user_context_);
+  CHECK(auth_request_);
+  auth_performer_.reset();
   auth_factor_editor_.reset();
 
+  if (IsSucceedState()) {
+    auth_request_->NotifyAuthSuccess(std::move(user_context_));
+  } else {
+    auth_request_->NotifyAuthFailure();
+    user_context_.reset();
+  }
+  auth_request_.reset();
+  available_factors_.Clear();
+
+  SetState(ActiveSessionAuthState::kWaitForInit);
+
   title_.clear();
   description_.clear();
 
   widget_.reset();
-
-  if (auth_request_) {
-    auth_request_->NotifyAuthFailure();
-    auth_request_.reset();
-  }
-
-  if (user_context_) {
-    user_context_.reset();
-  }
-
-  available_factors_.Clear();
 }
 
 void ActiveSessionAuthControllerImpl::OnViewPreferredSizeChanged(
@@ -316,6 +464,9 @@
 
 void ActiveSessionAuthControllerImpl::OnPasswordSubmit(
     const std::u16string& password) {
+  if (IsSucceedState()) {
+    return;
+  }
   SetState(ActiveSessionAuthState::kPasswordAuthStarted);
   uma_recorder_.RecordAuthStarted(AuthInputType::kPassword);
   CHECK(user_context_);
@@ -332,6 +483,9 @@
 }
 
 void ActiveSessionAuthControllerImpl::OnPinSubmit(const std::u16string& pin) {
+  if (IsSucceedState()) {
+    return;
+  }
   SetState(ActiveSessionAuthState::kPinAuthStarted);
   uma_recorder_.RecordAuthStarted(AuthInputType::kPin);
   CHECK(user_context_);
@@ -348,9 +502,20 @@
     AuthInputType input_type,
     std::unique_ptr<UserContext> user_context,
     std::optional<AuthenticationError> authentication_error) {
+  user_context_ = std::move(user_context);
+  // If fingerprint auth succeeded during wait for PIN/password authentication,
+  // handle success directly.
+  if (state_ == ActiveSessionAuthState::kFingerprintAuthSucceededWaiting) {
+    HandleFingerprintAuthSuccess();
+    return;
+  }
+  CHECK(!IsSucceedState());
+  if (state_ == ActiveSessionAuthState::kCloseRequested) {
+    StartClose();
+    return;
+  }
   if (authentication_error.has_value()) {
     uma_recorder_.RecordAuthFailed(input_type);
-    user_context_ = std::move(user_context);
     if (pending_pin_factor_status_update_.has_value()) {
       ProcessAuthFactorStatusUpdate(pending_pin_factor_status_update_.value());
       pending_pin_factor_status_update_.reset();
@@ -365,14 +530,29 @@
     SetState(input_type == AuthInputType::kPassword
                  ? ActiveSessionAuthState::kPasswordAuthSucceeded
                  : ActiveSessionAuthState::kPinAuthSucceeded);
-    auth_request_->NotifyAuthSuccess(std::move(user_context));
-    auth_request_.reset();
-    Close();
+    StartClose();
   }
 }
 
 void ActiveSessionAuthControllerImpl::OnClose() {
-  Close();
+  switch (state_) {
+    case ActiveSessionAuthState::kWaitForInit:
+      NOTREACHED();
+    case ActiveSessionAuthState::kInitialized:
+      StartClose();
+      return;
+    case ActiveSessionAuthState::kPasswordAuthStarted:
+    case ActiveSessionAuthState::kPinAuthStarted:
+      SetState(ActiveSessionAuthState::kCloseRequested);
+      return;
+    case ActiveSessionAuthState::kPasswordAuthSucceeded:
+    case ActiveSessionAuthState::kPinAuthSucceeded:
+    case ActiveSessionAuthState::kFingerprintAuthSucceeded:
+    case ActiveSessionAuthState::kFingerprintAuthSucceededWaiting:
+    case ActiveSessionAuthState::kCloseRequested:
+      return;
+  }
+  NOTREACHED();
 }
 
 bool ActiveSessionAuthControllerImpl::IsPinLocked() const {
@@ -384,10 +564,10 @@
 }
 
 void ActiveSessionAuthControllerImpl::SetState(ActiveSessionAuthState state) {
-  LOG(WARNING) << "SetState is requested from: "
-               << ActiveSessionAuthStateToString(state_)
-               << " state to : " << ActiveSessionAuthStateToString(state)
-               << " state.";
+  VLOG(1) << "SetState is requested from: "
+          << ActiveSessionAuthStateToString(state_)
+          << " state to : " << ActiveSessionAuthStateToString(state)
+          << " state.";
   switch (state) {
     case ActiveSessionAuthState::kWaitForInit:
       break;
@@ -413,23 +593,53 @@
     case ActiveSessionAuthState::kPinAuthSucceeded:
       CHECK_EQ(state_, ActiveSessionAuthState::kPinAuthStarted);
       break;
+    case ActiveSessionAuthState::kFingerprintAuthSucceeded:
+      CHECK(state_ == ActiveSessionAuthState::kInitialized ||
+            state_ == ActiveSessionAuthState::kFingerprintAuthSucceededWaiting);
+      contents_view_->SetInputEnabled(false);
+      break;
+    case ActiveSessionAuthState::kFingerprintAuthSucceededWaiting:
+      CHECK(state_ == ActiveSessionAuthState::kPasswordAuthStarted ||
+            state_ == ActiveSessionAuthState::kPinAuthStarted);
+      contents_view_->SetInputEnabled(false);
+      break;
+    case ActiveSessionAuthState::kCloseRequested:
+      CHECK(state_ == ActiveSessionAuthState::kPasswordAuthStarted ||
+            state_ == ActiveSessionAuthState::kPinAuthStarted);
+      contents_view_->SetInputEnabled(false);
+      break;
   }
   state_ = state;
 }
 
 void ActiveSessionAuthControllerImpl::OnAuthFactorStatusUpdate(
     const user_data_auth::AuthFactorStatusUpdate& update) {
-  if (update.auth_factor_with_status().auth_factor().type() ==
-      user_data_auth::AUTH_FACTOR_TYPE_PIN) {
-    if (user_context_) {
-      ProcessAuthFactorStatusUpdate(update);
-    } else {
-      if (pending_pin_factor_status_update_.has_value()) {
-        LOG(WARNING) << "Overwrite pending pin status update.";
+  switch (state_) {
+    case ActiveSessionAuthState::kInitialized:
+      // Handle the updates in the initialized state.
+      CHECK(user_context_);
+      if (update.auth_factor_with_status().auth_factor().type() ==
+          user_data_auth::AUTH_FACTOR_TYPE_PIN) {
+        ProcessAuthFactorStatusUpdate(update);
       }
-      pending_pin_factor_status_update_ = update;
-    }
+      break;
+
+    case ActiveSessionAuthState::kWaitForInit:
+    case ActiveSessionAuthState::kPasswordAuthStarted:
+    case ActiveSessionAuthState::kPinAuthStarted:
+      // TODO(b:363443439): Handle updates in these states.
+      // Currently skipping for simplicity.
+      return;
+
+    case ActiveSessionAuthState::kPasswordAuthSucceeded:
+    case ActiveSessionAuthState::kPinAuthSucceeded:
+    case ActiveSessionAuthState::kFingerprintAuthSucceeded:
+    case ActiveSessionAuthState::kFingerprintAuthSucceededWaiting:
+    case ActiveSessionAuthState::kCloseRequested:
+      // No need to handle PIN updates as dialog closing is in progress.
+      return;
   }
+  NOTREACHED();
 }
 
 void ActiveSessionAuthControllerImpl::ProcessAuthFactorStatusUpdate(
diff --git a/ash/auth/active_session_auth_controller_impl.h b/ash/auth/active_session_auth_controller_impl.h
index a7b15c2..f10569a 100644
--- a/ash/auth/active_session_auth_controller_impl.h
+++ b/ash/auth/active_session_auth_controller_impl.h
@@ -14,6 +14,8 @@
 #include "ash/auth/views/active_session_auth_view.h"
 #include "ash/auth/views/auth_common.h"
 #include "ash/public/cpp/auth/active_session_auth_controller.h"
+#include "ash/public/cpp/auth/active_session_fingerprint_client.h"
+#include "ash/public/cpp/in_session_auth_token_provider.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/ash/components/cryptohome/auth_factor.h"
@@ -82,6 +84,7 @@
   // ActiveSessionAuthController:
   bool ShowAuthDialog(std::unique_ptr<AuthRequest> auth_request) override;
   bool IsShown() const override;
+  void SetFingerprintClient(ActiveSessionFingerprintClient* fp_client) override;
 
   // views::ViewObserver:
   void OnViewPreferredSizeChanged(views::View* observed_view) override;
@@ -97,7 +100,23 @@
 
   // Actions:
   void MoveToTheCenter();
-  void Close();
+
+  // The closing process is divided into two functions to accommodate
+  // fingerprint authentication. If fingerprint authentication is active,
+  // we must terminate it asynchronously. This requires a second function
+  // (callback/ CompleteClose) to complete the closing procedure.
+  void StartClose();
+  void CompleteClose(std::unique_ptr<UserContext> user_context,
+                     std::optional<AuthenticationError> authentication_error);
+
+  // Fingerprint actions:
+  void OnFingerprintScan(const FingerprintAuthScanResult scan_result);
+  void OnFingerprintSuccess(
+      std::unique_ptr<UserContext> user_context,
+      std::optional<AuthenticationError> authentication_error);
+  void OnFingerprintTerminated(
+      std::unique_ptr<UserContext> user_context,
+      std::optional<AuthenticationError> authentication_error);
 
   // Tracks the authentication flow for the active session.
   enum class ActiveSessionAuthState {
@@ -107,14 +126,37 @@
     kPasswordAuthSucceeded,  // Successful password authentication.
     kPinAuthStarted,         // User submitted PIN, awaiting verification.
     kPinAuthSucceeded,       // Successful PIN authentication.
+    kFingerprintAuthSucceeded,         // Successful fingerprint authentication.
+    kFingerprintAuthSucceededWaiting,  // Successful fingerprint scan but
+                                       // password/PIN auth is already in
+                                       // progress, awaiting callback to get
+                                       // back user_context to start the
+                                       // authentication.
+    kCloseRequested,  // Close requested while we are waiting password/PIN
+                      // authentication callback.
     // Note: On authentication failure, the state reverts to kInitialized.
   };
 
  private:
+  using AuthFactorsReadyCallback =
+      base::OnceCallback<void(std::unique_ptr<UserContext>)>;
+
+  // Helper functions for handling the readiness of authentication factors,
+  // particularly focusing on fingerprint authentication setup and its
+  // integration into the overall authentication flow.
+  void MaybePrepareFingerprint(AuthFactorsReadyCallback on_auth_factors_ready);
+  void OnFingerprintReady(
+      AuthFactorsReadyCallback on_auth_factors_ready,
+      std::unique_ptr<UserContext> user_context,
+      std::optional<AuthenticationError> authentication_error);
+  void AuthFactorsAreReady(std::unique_ptr<UserContext> user_context);
+
   // Set the state of the class, if it necessary disable/enable the input area
   // of the UI. Validates the transitions.
   void SetState(ActiveSessionAuthState state);
 
+  bool IsSucceedState() const;
+
   // Internal methods for authentication.
   void OnAuthSessionStarted(
       bool user_exists,
@@ -123,6 +165,8 @@
   void OnAuthComplete(AuthInputType input_type,
                       std::unique_ptr<UserContext> user_context,
                       std::optional<AuthenticationError> authentication_error);
+
+  void HandleFingerprintAuthSuccess();
   void NotifySuccess(const AuthProofToken& token, base::TimeDelta timeout);
   void ProcessAuthFactorStatusUpdate(
       const user_data_auth::AuthFactorStatusUpdate& update);
@@ -163,6 +207,8 @@
       pending_pin_factor_status_update_ = std::nullopt;
   std::unique_ptr<AuthRequest> auth_request_;
 
+  raw_ptr<ActiveSessionFingerprintClient> fp_client_;
+
   ActiveSessionAuthMetricsRecorder uma_recorder_;
 
   base::WeakPtrFactory<ActiveSessionAuthControllerImpl> weak_ptr_factory_{this};
diff --git a/ash/components/arc/mojom/file_system.mojom b/ash/components/arc/mojom/file_system.mojom
index df20a6f4..c2923abf 100644
--- a/ash/components/arc/mojom/file_system.mojom
+++ b/ash/components/arc/mojom/file_system.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 25
+// Next MinVersion: 26
 
 module arc.mojom;
 
@@ -13,6 +13,7 @@
 import "ash/components/arc/mojom/video_common.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
 import "mojo/public/mojom/base/time.mojom";
+import "mojo/public/mojom/base/token.mojom";
 import "url/mojom/url.mojom";
 
 // TODO(b/193097194): Use structured types (e.g., url.mojom.Url for URLs).
@@ -314,7 +315,7 @@
   MediaStoreDownloadMetadata download;
 };
 
-// Next method ID: 13
+// Next method ID: 15
 interface FileSystemHost {
   // Returns the name of the file specified by the URL.
   // When an error occurs, returns null value.
@@ -371,6 +372,22 @@
   // with the given |metadata|.
   [MinVersion=23] OnMediaStoreUriAdded@12(
       url.mojom.Url uri, MediaStoreMetadata metadata);
+
+  // Creates a Fusebox Moniker associated with the given content URI, shares the
+  // Moniker's path with the guest via seneschal, and returns the Moniker. A
+  // null value is returned on failure. On success, the caller is responsible
+  // for releasing the Moniker by calling DestroyMoniker() after they have
+  // finished using it.
+  [MinVersion=25] CreateMoniker@13(url.mojom.Url content_uri, bool read_only) =>
+      (mojo_base.mojom.Token? moniker);
+
+  // Destroys the given Fusebox Moniker and resources accosiated with it.
+  // Returns true when the Moniker and the resources have been successfully
+  // released. Otherwise false is returned, which indicates that the given
+  // Moniker has already been destroyed via DestroyMoniker(), or it is not the
+  // one created by ArcFileSystemBridge.
+  [MinVersion=25] DestroyMoniker@14(mojo_base.mojom.Token moniker) =>
+      (bool success);
 };
 
 // The browser process is using this interface in ArcFileSystemOperationRunner,
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index f3955c80..9ac5d59 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1152,9 +1152,10 @@
 
 // Enables the kernel drivers (instead of the FUSE mounters) for the exFAT and
 // NTFS filesystems on systems that support them (b/358446133).
+// TODO(b/364409158) Remove this feature.
 BASE_FEATURE(kFilesKernelDrivers,
              "FilesKernelDrivers",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables local image search by query in the Files app.
 BASE_FEATURE(kFilesLocalImageSearch,
@@ -3266,7 +3267,7 @@
 // Enables a new Welcome Experience for first-time peripheral connections.
 BASE_FEATURE(kWelcomeExperience,
              "WelcomeExperience",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // kWelcomeExperienceTestUnsupportedDevices enables the new device Welcome
 // Experience to be tested on external devices that are not officially
diff --git a/ash/constants/ash_paths.cc b/ash/constants/ash_paths.cc
index 42ace4f..87d1b61a 100644
--- a/ash/constants/ash_paths.cc
+++ b/ash/constants/ash_paths.cc
@@ -63,6 +63,9 @@
 const base::FilePath::CharType kDevicePolicyScreensaverDataDir[] =
     FILE_PATH_LITERAL("/var/cache/managed_screensaver");
 
+const base::FilePath::CharType kDeviceLocalAccountFirstPolicyFetchFile[] =
+    FILE_PATH_LITERAL("/run/chrome/device_local_account_first_policy_fetch");
+
 bool PathProvider(int key, base::FilePath* result) {
   switch (key) {
     case FILE_DEFAULT_APP_ORDER:
@@ -80,6 +83,9 @@
     case FILE_STARTUP_CUSTOMIZATION_MANIFEST:
       *result = base::FilePath(kStartupCustomizationManifestFile);
       break;
+    case FILE_DEVICE_LOCAL_ACCOUNT_FIRST_POLICY_FETCH:
+      *result = base::FilePath(kDeviceLocalAccountFirstPolicyFetchFile);
+      break;
     case DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS:
       *result = base::FilePath(kDeviceLocalAccountExtensionDir);
       break;
diff --git a/ash/constants/ash_paths.h b/ash/constants/ash_paths.h
index 178a4b6..abb8dad 100644
--- a/ash/constants/ash_paths.h
+++ b/ash/constants/ash_paths.h
@@ -59,6 +59,10 @@
                                        // data resources are cached.
   DIR_DEVICE_POLICY_SCREENSAVER_DATA,  // Directory where the device policy
                                        // managed screensaver images are cached.
+  FILE_DEVICE_LOCAL_ACCOUNT_FIRST_POLICY_FETCH,  // File location to mark that
+                                                 // the first policy fetch for
+                                                 // device local accounts has
+                                                 // been scheduled.
   PATH_END
 };
 
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index 5e19d273..4110185 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -127,6 +127,9 @@
   if (cancel_animation_)
     cancel_animation_->End();
   drag_image_widget_.reset();
+  for (aura::client::DragDropClientObserver& observer : observers_) {
+    observer.OnDragDropClientDestroying();
+  }
 }
 
 bool DragDropController::IsDragDropCompleted() {
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc
index 4f96510..32e216d 100644
--- a/ash/drag_drop/drag_drop_controller_unittest.cc
+++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -1705,6 +1705,29 @@
   EXPECT_TRUE(GetDragImageWindow());
 }
 
+TEST_F(DragDropControllerTest, ObserverNotifiedOfDestruction) {
+  NiceMock<MockDragDropObserver> drag_drop_observer(
+      drag_drop_controller_.get());
+  EXPECT_CALL(drag_drop_observer, OnDragDropClientDestroying).WillOnce([&]() {
+    drag_drop_observer.ResetObservation();
+  });
+
+  std::unique_ptr<views::Widget> widget = CreateFramelessWidget();
+  DragTestView* drag_view = new DragTestView;
+  AddViewToWidgetAndResize(widget.get(), drag_view);
+  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
+                                     widget->GetNativeView());
+  generator.PressLeftButton();
+
+  int num_drags = 17;
+  for (int i = 0; i < num_drags; ++i) {
+    generator.MoveMouseBy(0, 1);
+  }
+
+  aura::client::SetDragDropClient(Shell::GetPrimaryRootWindow(), NULL);
+  drag_drop_controller_.reset();
+}
+
 // Verifies drag-and-drop with a data transfer policy controller.
 class DragDropControllerDlpTest : public DragDropControllerTest {
  public:
diff --git a/ash/drag_drop/mock_drag_drop_observer.cc b/ash/drag_drop/mock_drag_drop_observer.cc
index 0a759dfd..fdb0b76 100644
--- a/ash/drag_drop/mock_drag_drop_observer.cc
+++ b/ash/drag_drop/mock_drag_drop_observer.cc
@@ -15,4 +15,8 @@
 
 MockDragDropObserver::~MockDragDropObserver() = default;
 
+void MockDragDropObserver::ResetObservation() {
+  observation_.Reset();
+}
+
 }  // namespace ash
diff --git a/ash/drag_drop/mock_drag_drop_observer.h b/ash/drag_drop/mock_drag_drop_observer.h
index a97f662..101a6de 100644
--- a/ash/drag_drop/mock_drag_drop_observer.h
+++ b/ash/drag_drop/mock_drag_drop_observer.h
@@ -31,6 +31,9 @@
   MockDragDropObserver& operator=(const MockDragDropObserver&) = delete;
   ~MockDragDropObserver() override;
 
+  // Stops observing the DragDropClient.
+  void ResetObservation();
+
   // aura::client::DragDropClientObserver:
   MOCK_METHOD(void, OnDragStarted, (), (override));
   MOCK_METHOD(void,
@@ -43,6 +46,7 @@
               (override));
   MOCK_METHOD(void, OnDragCancelled, (), (override));
   MOCK_METHOD(void, OnDropCompleted, (ui::mojom::DragOperation), (override));
+  MOCK_METHOD(void, OnDragDropClientDestroying, (), (override));
 
  private:
   base::ScopedObservation<aura::client::DragDropClient,
diff --git a/ash/login/ui/lock_screen_media_view.cc b/ash/login/ui/lock_screen_media_view.cc
index 9f7bf179b..a736748 100644
--- a/ash/login/ui/lock_screen_media_view.cc
+++ b/ash/login/ui/lock_screen_media_view.cc
@@ -79,8 +79,8 @@
       hide_media_view_callback_(hide_media_view_callback) {
   // Observe power events and if created in power suspended state, post
   // OnSuspend() call to run after LockContentsView is initialized.
-  if (base::PowerMonitor::AddPowerSuspendObserverAndReturnSuspendedState(
-          this)) {
+  if (base::PowerMonitor::GetInstance()
+          ->AddPowerSuspendObserverAndReturnSuspendedState(this)) {
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
         FROM_HERE, base::BindOnce(&LockScreenMediaView::OnSuspend,
                                   weak_ptr_factory_.GetWeakPtr()));
@@ -137,7 +137,7 @@
 }
 
 LockScreenMediaView::~LockScreenMediaView() {
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/ash/picker/search/picker_search_controller.cc b/ash/picker/search/picker_search_controller.cc
index cbf505b..4d4ad53 100644
--- a/ash/picker/search/picker_search_controller.cc
+++ b/ash/picker/search/picker_search_controller.cc
@@ -187,7 +187,7 @@
   const base::TimeTicks search_start = base::TimeTicks::Now();
 
   emoji::EmojiSearchResult results = emoji_search_.SearchEmoji(
-      base::UTF16ToUTF8(query), GetLanguageCodesFromPrefs(client_->GetPrefs()));
+      query, GetLanguageCodesFromPrefs(client_->GetPrefs()));
 
   base::TimeDelta elapsed = base::TimeTicks::Now() - search_start;
   base::UmaHistogramTimes("Ash.Picker.Search.EmojiProvider.QueryTime", elapsed);
diff --git a/ash/picker/views/picker_contents_view.h b/ash/picker/views/picker_contents_view.h
index c5f71a8..aa310029 100644
--- a/ash/picker/views/picker_contents_view.h
+++ b/ash/picker/views/picker_contents_view.h
@@ -29,10 +29,13 @@
   // views::View:
   void Layout(PassKey) override;
 
-  // Adds a new page.
+  // Adds a new page. If this is the first page, then it is also set as the
+  // active page.
   template <typename T>
   T* AddPage(std::unique_ptr<T> view) {
-    view->SetVisible(false);
+    if (!page_container_->children().empty()) {
+      view->SetVisible(false);
+    }
     return page_container_->AddChildView(std::move(view));
   }
 
diff --git a/ash/picker/views/picker_contents_view_unittest.cc b/ash/picker/views/picker_contents_view_unittest.cc
index 14bab3d..d88c89d 100644
--- a/ash/picker/views/picker_contents_view_unittest.cc
+++ b/ash/picker/views/picker_contents_view_unittest.cc
@@ -31,7 +31,7 @@
   EXPECT_THAT(view->page_container_for_testing()->children(), IsEmpty());
 }
 
-TEST_F(PickerContentsViewTest, AddPageCreatesHiddenChildren) {
+TEST_F(PickerContentsViewTest, AddPageFirstChildIsVisible) {
   std::unique_ptr<views::Widget> widget =
       CreateTestWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET);
 
@@ -43,7 +43,7 @@
   EXPECT_THAT(
       view->page_container_for_testing()->children(),
       ElementsAre(
-          AllOf(page1, Pointee(Property(&views::View::GetVisible, false))),
+          AllOf(page1, Pointee(Property(&views::View::GetVisible, true))),
           AllOf(page2, Pointee(Property(&views::View::GetVisible, false)))));
 }
 
diff --git a/ash/picker/views/picker_item_view.cc b/ash/picker/views/picker_item_view.cc
index 0f272fd8..f13bc67 100644
--- a/ash/picker/views/picker_item_view.cc
+++ b/ash/picker/views/picker_item_view.cc
@@ -48,12 +48,11 @@
     : views::Button(select_item_callback),
       select_item_callback_(select_item_callback),
       focus_indicator_style_(focus_indicator_style) {
-  StyleUtil::SetUpInkDropForButton(this);
-
   switch (focus_indicator_style_) {
     case FocusIndicatorStyle::kFocusRingWithInsetGap:
       [[fallthrough]];
     case FocusIndicatorStyle::kFocusRing:
+      StyleUtil::SetUpFocusRingForView(this);
       views::FocusRing::Get(this)->SetHasFocusPredicate(
           base::BindRepeating([](const View* view) {
             const auto* v = views::AsViewClass<PickerItemView>(view);
diff --git a/ash/picker/views/picker_view.cc b/ash/picker/views/picker_view.cc
index cce48db6..0751d6eb 100644
--- a/ash/picker/views/picker_view.cc
+++ b/ash/picker/views/picker_view.cc
@@ -70,8 +70,8 @@
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/separator.h"
+#include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
-#include "ui/views/layout/flex_layout.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/view_observer.h"
 #include "ui/views/view_tracker.h"
@@ -285,12 +285,10 @@
         layout_type));
   }
 
-  SetLayoutManager(std::make_unique<views::FlexLayout>())
-      ->SetOrientation(views::LayoutOrientation::kVertical)
-      .SetCollapseMargins(true)
-      .SetIgnoreDefaultMainAxisMargins(true)
-      .SetDefault(views::kMarginsKey,
-                  gfx::Insets::VH(kVerticalPaddingBetweenPickerContainers, 0));
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::LayoutOrientation::kVertical,
+      /*inside_border_insets=*/gfx::Insets(),
+      /*between_child_spacing=*/kVerticalPaddingBetweenPickerContainers));
 
   AddMainContainerView(layout_type);
   if (base::Contains(delegate_->GetAvailableCategories(),
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index a8753db..d3a2a2a9 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -109,6 +109,7 @@
     "audio_config_service.h",
     "auth/active_session_auth_controller.cc",
     "auth/active_session_auth_controller.h",
+    "auth/active_session_fingerprint_client.h",
     "back_gesture_contextual_nudge_controller.h",
     "back_gesture_contextual_nudge_delegate.h",
     "bluetooth_config_service.cc",
diff --git a/ash/public/cpp/auth/active_session_auth_controller.h b/ash/public/cpp/auth/active_session_auth_controller.h
index 3f2ed364..c42b93ee 100644
--- a/ash/public/cpp/auth/active_session_auth_controller.h
+++ b/ash/public/cpp/auth/active_session_auth_controller.h
@@ -14,6 +14,10 @@
 
 class AuthRequest;
 
+// ActiveSessionFingerprintClient assists ActiveSessionAuthController with
+// fingerprint authentication.
+class ActiveSessionFingerprintClient;
+
 // ActiveSessionAuthController serves active session authentication requests.
 // It takes care of showing and hiding the UI and the authentication process.
 class ASH_PUBLIC_EXPORT ActiveSessionAuthController {
@@ -37,6 +41,12 @@
 
   virtual bool IsShown() const = 0;
 
+  // Sets the fingerprint client responsible for:
+  // Managing the biometrics daemon (e.g initialize it to authentication mode).
+  // Handling fingerprint authentication scan events.
+  virtual void SetFingerprintClient(
+      ActiveSessionFingerprintClient* fp_client) = 0;
+
  protected:
   ActiveSessionAuthController();
 };
diff --git a/ash/public/cpp/auth/active_session_fingerprint_client.h b/ash/public/cpp/auth/active_session_fingerprint_client.h
new file mode 100644
index 0000000..f044692
--- /dev/null
+++ b/ash/public/cpp/auth/active_session_fingerprint_client.h
@@ -0,0 +1,77 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_AUTH_ACTIVE_SESSION_FINGERPRINT_CLIENT_H_
+#define ASH_PUBLIC_CPP_AUTH_ACTIVE_SESSION_FINGERPRINT_CLIENT_H_
+
+#include <memory>
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "ash/public/cpp/auth/active_session_auth_controller.h"
+#include "ash/public/cpp/login_types.h"
+#include "base/functional/callback_forward.h"
+#include "chromeos/ash/components/login/auth/public/auth_callbacks.h"
+#include "chromeos/ash/components/login/auth/public/user_context.h"
+#include "chromeos/ash/components/osauth/public/request/auth_request.h"
+#include "components/account_id/account_id.h"
+
+namespace ash {
+
+// This enum represents the possible results of a fingerprint authentication
+// scan. It's a simplified version of the FingerprintScanResult in the
+// UserDataAuth.proto. We introduce this enum to avoid making ash public depend
+// on a proto file, and because we only need the authentication scan results
+// (not enrollment).
+enum class FingerprintAuthScanResult {
+  // The scan was successful and the fingerprint matched.
+  kSuccess,
+
+  // The scan was successful but the fingerprint didn't match.
+  kFailed,
+
+  // The scan failed due to too many failed attempts.
+  kTooManyAttempts,
+
+  // The scan failed due to a fatal error.
+  kFatalError,
+
+  kMaxValue = kFatalError,
+};
+
+using ActiveSessionFingerprintScanCallback =
+    base::RepeatingCallback<void(const FingerprintAuthScanResult)>;
+
+// ActiveSessionFingerprintClient handles fingerprint authentication requests
+// during an active user session. It bridges the ActiveSessionAuthController
+// with fingerprint storage/policy and authentication mechanisms.
+class ASH_PUBLIC_EXPORT ActiveSessionFingerprintClient {
+ public:
+  virtual ~ActiveSessionFingerprintClient() = default;
+
+  // Checks if the given user is permitted to use fingerprint authentication for
+  // the specified purpose and if they have any enrolled fingerprints.
+  virtual bool IsFingerprintAvailable(AuthRequest::Reason reason,
+                                      const AccountId& account_id) = 0;
+
+  // Prepares the biometrics daemon for authentication.
+  // - `auth_ready_callback` is invoked when the daemon is ready to process
+  // fingerprint scans.
+  // - `on_auth_success_callback` is triggered upon successful fingerprint
+  // authentication.
+  // - `on_auth_failed_callback` is called when fingerprint authentication
+  // fails.
+  virtual void PrepareFingerprintAuth(
+      std::unique_ptr<UserContext> user_context,
+      AuthOperationCallback auth_ready_callback,
+      ActiveSessionFingerprintScanCallback on_scan_callback) = 0;
+
+  // Returns the biometrics daemon to its normal state (e.g., when closing an
+  // authentication dialog).
+  virtual void TerminateFingerprintAuth(
+      std::unique_ptr<UserContext> user_context,
+      AuthOperationCallback callback) = 0;
+};
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_AUTH_ACTIVE_SESSION_FINGERPRINT_CLIENT_H_
diff --git a/ash/system/input_device_settings/input_device_settings_notification_controller_unittest.cc b/ash/system/input_device_settings/input_device_settings_notification_controller_unittest.cc
index 688bb51..245c37961 100644
--- a/ash/system/input_device_settings/input_device_settings_notification_controller_unittest.cc
+++ b/ash/system/input_device_settings/input_device_settings_notification_controller_unittest.cc
@@ -327,24 +327,27 @@
 TEST_F(InputDeviceSettingsNotificationControllerTest,
        ShowPeripheralSettingsOnCustomizationNotificationClick) {
   NotifyMouseIsCustomizable(kMouse1);
-  message_center()->ClickOnNotification("welcome_experience_1");
+  message_center()->ClickOnNotification("peripheral_customization_mouse_1");
   EXPECT_EQ(GetSystemTrayClient()->show_mouse_settings_count(), 1);
 
   NotifyGraphicsTabletIsCustomizable(kGraphicsTablet2);
-  message_center()->ClickOnNotification("welcome_experience_2");
+  message_center()->ClickOnNotification(
+      "peripheral_customization_graphics_tablet_2");
   EXPECT_EQ(GetSystemTrayClient()->show_graphics_tablet_settings_count(), 1);
 }
 
 TEST_F(InputDeviceSettingsNotificationControllerTest,
        ShowPeripheralSettingsOnCustomizationNotificationButtonClick) {
   NotifyMouseIsCustomizable(kMouse1);
-  message_center()->ClickOnNotificationButton("welcome_experience_1",
-                                              /*button_index=*/0);
+  message_center()->ClickOnNotificationButton(
+      "peripheral_customization_mouse_1",
+      /*button_index=*/0);
   EXPECT_EQ(GetSystemTrayClient()->show_mouse_settings_count(), 1);
 
   NotifyGraphicsTabletIsCustomizable(kGraphicsTablet2);
-  message_center()->ClickOnNotificationButton("welcome_experience_2",
-                                              /*button_index=*/0);
+  message_center()->ClickOnNotificationButton(
+      "peripheral_customization_graphics_tablet_2",
+      /*button_index=*/0);
   EXPECT_EQ(GetSystemTrayClient()->show_graphics_tablet_settings_count(), 1);
 }
 
@@ -467,35 +470,30 @@
 
   PrefService* prefs =
       Shell::Get()->session_controller()->GetActivePrefService();
-
-  EXPECT_TRUE(
-      prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).empty());
+  EXPECT_TRUE(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).empty());
   NotifyMouseFirstTimeConnected(*mojom_mouse);
-  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
-            1u);
+  EXPECT_EQ(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).size(), 1u);
   EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
+      base::Contains(prefs->GetList(prefs::kPeripheralNotificationMiceSeen),
                      base::Value("0001:0001")));
   NotifyMouseFirstTimeConnected(*mojom_mouse);
-  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
-            1u);
+  EXPECT_EQ(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).size(), 1u);
   EXPECT_EQ(expected_notification_count++,
             message_center()->NotificationCount());
-  EXPECT_TRUE(
-      message_center()->FindVisibleNotificationById("welcome_experience_1"));
+  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
+      "peripheral_customization_mouse_1"));
 
   mojom_mouse->id = 2;
   mojom_mouse->device_key = "0001:0002";
 
   NotifyMouseFirstTimeConnected(*mojom_mouse);
-  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
-            2u);
+  EXPECT_EQ(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).size(), 2u);
   EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
+      base::Contains(prefs->GetList(prefs::kPeripheralNotificationMiceSeen),
                      base::Value("0001:0002")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_TRUE(
-      message_center()->FindVisibleNotificationById("welcome_experience_2"));
+  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
+      "peripheral_customization_mouse_2"));
 
   mojom_mouse->id = 3;
   mojom_mouse->device_key = "0001:0003";
@@ -506,14 +504,13 @@
               mojom::CustomizableButton::kBack),
           /*remapping_action=*/nullptr));
   NotifyMouseFirstTimeConnected(*mojom_mouse);
-  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
-            3u);
+  EXPECT_EQ(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).size(), 3u);
   EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
+      base::Contains(prefs->GetList(prefs::kPeripheralNotificationMiceSeen),
                      base::Value("0001:0003")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_FALSE(
-      message_center()->FindVisibleNotificationById("welcome_experience_3"));
+  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
+      "peripheral_customization_mouse_3"));
 }
 
 TEST_F(InputDeviceSettingsNotificationControllerTest,
@@ -526,35 +523,38 @@
   PrefService* prefs =
       Shell::Get()->session_controller()->GetActivePrefService();
 
-  EXPECT_TRUE(
-      prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).empty());
+  EXPECT_TRUE(prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen)
+                  .empty());
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
-            1u);
+  EXPECT_EQ(
+      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
+      1u);
   EXPECT_EQ(expected_notification_count++,
             message_center()->NotificationCount());
-  EXPECT_TRUE(
-      message_center()->FindVisibleNotificationById("welcome_experience_1"));
+  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
+      "peripheral_customization_graphics_tablet_1"));
 
-  EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
-                     base::Value("0002:0001")));
+  EXPECT_TRUE(base::Contains(
+      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen),
+      base::Value("0002:0001")));
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
-            1u);
+  EXPECT_EQ(
+      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
+      1u);
 
   mojom_graphics_tablet->id = 2;
   mojom_graphics_tablet->device_key = "0002:0002";
 
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
-            2u);
-  EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
-                     base::Value("0002:0002")));
+  EXPECT_EQ(
+      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
+      2u);
+  EXPECT_TRUE(base::Contains(
+      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen),
+      base::Value("0002:0002")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_TRUE(
-      message_center()->FindVisibleNotificationById("welcome_experience_2"));
+  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
+      "peripheral_customization_graphics_tablet_2"));
 
   mojom_graphics_tablet->id = 3;
   mojom_graphics_tablet->device_key = "0002:0003";
@@ -566,14 +566,15 @@
           /*remapping_action=*/nullptr));
 
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
-            3u);
-  EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
-                     base::Value("0002:0003")));
+  EXPECT_EQ(
+      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
+      3u);
+  EXPECT_TRUE(base::Contains(
+      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen),
+      base::Value("0002:0003")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_FALSE(
-      message_center()->FindVisibleNotificationById("welcome_experience_3"));
+  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
+      "peripheral_customization_graphics_tablet_3"));
 
   mojom_graphics_tablet->id = 4;
   mojom_graphics_tablet->device_key = "0002:0004";
@@ -586,14 +587,15 @@
           /*remapping_action=*/nullptr));
 
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
-            4u);
-  EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
-                     base::Value("0002:0004")));
+  EXPECT_EQ(
+      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
+      4u);
+  EXPECT_TRUE(base::Contains(
+      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen),
+      base::Value("0002:0004")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_FALSE(
-      message_center()->FindVisibleNotificationById("welcome_experience_4"));
+  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
+      "peripheral_customization_graphics_tablet_4"));
 }
 
 TEST_F(InputDeviceSettingsNotificationControllerTest,
@@ -922,8 +924,8 @@
   NotifyMouseFirstTimeConnected(*mojom_mouse);
   EXPECT_EQ(expected_notification_count++,
             message_center()->NotificationCount());
-  const auto* notification =
-      message_center()->FindVisibleNotificationById("welcome_experience_1");
+  const auto* notification = message_center()->FindVisibleNotificationById(
+      "peripheral_customization_mouse_1");
   ASSERT_TRUE(notification);
   EXPECT_EQ(
       l10n_util::GetStringFUTF16(
@@ -947,8 +949,8 @@
       gfx::test::CreateImageSkia(/*width=*/300, /*height=*/300, SK_ColorRED));
   EXPECT_EQ(expected_notification_count++,
             message_center()->NotificationCount());
-  const auto* notification =
-      message_center()->FindVisibleNotificationById("welcome_experience_1");
+  const auto* notification = message_center()->FindVisibleNotificationById(
+      "peripheral_customization_mouse_1");
   EXPECT_FALSE(notification->image().IsEmpty());
 }
 
diff --git a/ash/system/power/battery_notification.cc b/ash/system/power/battery_notification.cc
index 486ab94..5d76959 100644
--- a/ash/system/power/battery_notification.cc
+++ b/ash/system/power/battery_notification.cc
@@ -186,8 +186,6 @@
       l10n_util::GetStringUTF16(enable_disable_bsm_token_optional.value())};
   rich_notification_data.buttons =
       std::vector<message_center::ButtonInfo>{bsm_button};
-  rich_notification_data.settings_button_handler =
-      message_center::SettingsButtonHandler::DELEGATE;
 }
 
 void HandlePowerNotificationButtonClick(
diff --git a/ash/webui/recorder_app_ui/mojom/recorder_app.mojom b/ash/webui/recorder_app_ui/mojom/recorder_app.mojom
index 2c18f903..b21a0c7 100644
--- a/ash/webui/recorder_app_ui/mojom/recorder_app.mojom
+++ b/ash/webui/recorder_app_ui/mojom/recorder_app.mojom
@@ -93,6 +93,16 @@
       map<string, string> fields)
       => (string? result);
 
+  // Validates the `text` is safe or not with the given `safety_feature` and
+  // `safety_info`. The `safety_info` should be the result returned by
+  // `ClassifyTextSafety()` from `on_device_model.mojom.OnDeviceModel`. Returns
+  // true if the `text` is safe.
+  ValidateSafetyResult(
+      on_device_model.mojom.SafetyFeature safety_feature,
+      string text,
+      on_device_model.mojom.SafetyInfo safety_info)
+      => (bool is_safe);
+
   // Adds a new monitor for model state change.
   // Returns the current state.
   AddModelMonitor(
diff --git a/ash/webui/recorder_app_ui/recorder_app_ui.cc b/ash/webui/recorder_app_ui/recorder_app_ui.cc
index 0eae742..2bd2d1af 100644
--- a/ash/webui/recorder_app_ui/recorder_app_ui.cc
+++ b/ash/webui/recorder_app_ui/recorder_app_ui.cc
@@ -297,6 +297,23 @@
                                         std::move(callback));
 }
 
+void RecorderAppUI::ValidateSafetyResult(
+    on_device_model::mojom::SafetyFeature safety_feature,
+    const std::string& text,
+    on_device_model::mojom::SafetyInfoPtr safety_info,
+    ValidateSafetyResultCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  EnsureOnDeviceModelService();
+
+  if (!on_device_model_service_) {
+    std::move(callback).Run(false);
+  }
+
+  on_device_model_service_->ValidateSafetyResult(
+      safety_feature, text, std::move(safety_info), std::move(callback));
+}
+
 void RecorderAppUI::Progress(double progress) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/ash/webui/recorder_app_ui/recorder_app_ui.h b/ash/webui/recorder_app_ui/recorder_app_ui.h
index 3ae47a61..1ba4d15 100644
--- a/ash/webui/recorder_app_ui/recorder_app_ui.h
+++ b/ash/webui/recorder_app_ui/recorder_app_ui.h
@@ -18,6 +18,7 @@
 #include "components/soda/soda_installer.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
+#include "services/on_device_model/public/mojom/on_device_model.mojom.h"
 #include "services/on_device_model/public/mojom/on_device_model_service.mojom.h"
 #include "ui/message_center/message_center_observer.h"
 #include "ui/webui/color_change_listener/color_change_handler.h"
@@ -109,6 +110,12 @@
                         const base::flat_map<std::string, std::string>& fields,
                         FormatModelInputCallback callback) override;
 
+  void ValidateSafetyResult(
+      on_device_model::mojom::SafetyFeature safety_feature,
+      const std::string& text,
+      on_device_model::mojom::SafetyInfoPtr safety_info,
+      ValidateSafetyResultCallback callback) override;
+
   void AddModelMonitor(
       const base::Uuid& model_id,
       ::mojo::PendingRemote<recorder_app::mojom::ModelStateMonitor> monitor,
diff --git a/ash/webui/recorder_app_ui/resources/components/components.gni b/ash/webui/recorder_app_ui/resources/components/components.gni
index 90e1d0d..dc470f73 100644
--- a/ash/webui/recorder_app_ui/resources/components/components.gni
+++ b/ash/webui/recorder_app_ui/resources/components/components.gni
@@ -4,6 +4,8 @@
 
 component_files = [
   "audio-waveform.ts",
+  "cra/cra-accordion-item.ts",
+  "cra/cra-accordion.ts",
   "cra/cra-button.ts",
   "cra/cra-dialog.ts",
   "cra/cra-dropdown.ts",
diff --git a/ash/webui/recorder_app_ui/resources/components/cra/cra-accordion-item.ts b/ash/webui/recorder_app_ui/resources/components/cra/cra-accordion-item.ts
new file mode 100644
index 0000000..9738049
--- /dev/null
+++ b/ash/webui/recorder_app_ui/resources/components/cra/cra-accordion-item.ts
@@ -0,0 +1,29 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {
+  AccordionItem,
+} from 'chrome://resources/cros_components/accordion/accordion_item.js';
+import {css} from 'chrome://resources/mwc/lit/index.js';
+
+export class CraAccordionItem extends AccordionItem {
+  static override styles = [
+    AccordionItem.styles,
+    // TODO: b/338544996 - Export the arrow via ::part, so we don't need this
+    // workaround.
+    css`
+      :host(.hide-content) .accordion-row > cros-icon-button {
+        display: none;
+      }
+    `,
+  ];
+}
+
+window.customElements.define('cra-accordion-item', CraAccordionItem);
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'cra-accordion-item': CraAccordionItem;
+  }
+}
diff --git a/ash/webui/recorder_app_ui/resources/components/cra/cra-accordion.ts b/ash/webui/recorder_app_ui/resources/components/cra/cra-accordion.ts
new file mode 100644
index 0000000..ac1afda
--- /dev/null
+++ b/ash/webui/recorder_app_ui/resources/components/cra/cra-accordion.ts
@@ -0,0 +1,27 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {
+  Accordion,
+} from 'chrome://resources/cros_components/accordion/accordion.js';
+import {css} from 'chrome://resources/mwc/lit/index.js';
+
+export class CraAccordion extends Accordion {
+  static override styles = [
+    Accordion.styles,
+    css`
+      ::slotted(cra-accordion-item:last-of-type) {
+        --cros-accordion-item-separator-display: none;
+      }
+    `,
+  ];
+}
+
+window.customElements.define('cra-accordion', CraAccordion);
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'cra-accordion': CraAccordion;
+  }
+}
diff --git a/ash/webui/recorder_app_ui/resources/components/expandable-card.ts b/ash/webui/recorder_app_ui/resources/components/expandable-card.ts
index ebbfe86..388b574 100644
--- a/ash/webui/recorder_app_ui/resources/components/expandable-card.ts
+++ b/ash/webui/recorder_app_ui/resources/components/expandable-card.ts
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 import 'chrome://resources/cros_components/checkbox/checkbox.js';
-import 'chrome://resources/cros_components/accordion/accordion.js';
-import 'chrome://resources/cros_components/accordion/accordion_item.js';
+import './cra/cra-accordion.js';
+import './cra/cra-accordion-item.js';
 import './cra/cra-icon-button.js';
 import './cra/cra-icon.js';
 
@@ -12,6 +12,7 @@
   Checkbox as CrosCheckbox,
 } from 'chrome://resources/cros_components/checkbox/checkbox.js';
 import {
+  classMap,
   css,
   CSSResultGroup,
   html,
@@ -23,7 +24,7 @@
 
 export class ExpandableCard extends ReactiveLitElement {
   static override styles: CSSResultGroup = css`
-    cros-accordion {
+    cra-accordion {
       --cros-card-background-color: none;
 
       &::part(card) {
@@ -35,8 +36,14 @@
       margin: -8px -14px;
     }
 
-    cros-accordion-item::part(content) {
-      padding: 0 14px 18px;
+    cra-accordion-item {
+      &::part(content) {
+        padding: 0 14px 18px;
+      }
+
+      &.hide-content::part(content) {
+        display: none;
+      }
     }
 
     slot[name="content"]::slotted(*) {
@@ -47,12 +54,20 @@
   static override properties: PropertyDeclarations = {
     expanded: {type: Boolean},
     disabled: {type: Boolean},
+    hideContent: {type: Boolean},
   };
 
   expanded = false;
 
   disabled = false;
 
+  /**
+   * Hides the content and the arrow. The component still emits
+   * "expandable-card-expanded" and "expandable-card-collapsed" event, but only
+   * the checkbox state would change.
+   */
+  hideContent = false;
+
   private onExpanded() {
     this.dispatchEvent(new CustomEvent('expandable-card-expanded'));
   }
@@ -75,14 +90,18 @@
   }
 
   override render(): RenderResult {
+    const classes = {
+      'hide-content': this.hideContent,
+    };
     // The whole header of cros-accordion can be focused, so tabindex=-1 is set
     // on the checkbox to make it unfocusable.
-    return html`<cros-accordion variant="compact">
-      <cros-accordion-item
+    return html`<cra-accordion variant="compact">
+      <cra-accordion-item
         ?disabled=${this.disabled}
         ?expanded=${this.shouldExpand}
         @cros-accordion-item-expanded=${this.onExpanded}
         @cros-accordion-item-collapsed=${this.onCollapsed}
+        class=${classMap(classes)}
       >
         <cros-checkbox
           slot="leading"
@@ -94,8 +113,8 @@
         </cros-checkbox>
         <slot name="header" slot="title"></slot>
         <slot name="content"></slot>
-      </cros-accordion-item>
-    </cros-accordion>`;
+      </cra-accordion-item>
+    </cra-accordion>`;
   }
 }
 
diff --git a/ash/webui/recorder_app_ui/resources/components/export-dialog.ts b/ash/webui/recorder_app_ui/resources/components/export-dialog.ts
index 6752ab0d..ad68bad 100644
--- a/ash/webui/recorder_app_ui/resources/components/export-dialog.ts
+++ b/ash/webui/recorder_app_ui/resources/components/export-dialog.ts
@@ -207,26 +207,28 @@
   }
 
   override render(): RenderResult {
-    // TODO(pihsun): Investigate why the cros-dropdown can't be closed by
-    // clicking on the select again...
     // TODO: b/344784478 - Show estimate file size.
-    const audioOptions = this.renderDropdownOptions(
-      [
-        {
-          headline: i18n.exportDialogAudioFormatWebmOption,
-          value: ExportAudioFormat.WEBM_ORIGINAL,
-        },
-      ],
-      this.exportSettings.value.audioFormat,
-    );
-
-    const transcriptionOptions = this.renderDropdownOptions(
-      [
+    const audioFormats: Array<DropdownOption<ExportAudioFormat>> = [
+      {
+        headline: i18n.exportDialogAudioFormatWebmOption,
+        value: ExportAudioFormat.WEBM_ORIGINAL,
+      },
+    ];
+    const transcriptionFormats:
+      Array<DropdownOption<ExportTranscriptionFormat>> = [
         {
           headline: i18n.exportDialogTranscriptionFormatTxtOption,
           value: ExportTranscriptionFormat.TXT,
         },
-      ],
+      ];
+
+    const audioOptions = this.renderDropdownOptions(
+      audioFormats,
+      this.exportSettings.value.audioFormat,
+    );
+
+    const transcriptionOptions = this.renderDropdownOptions(
+      transcriptionFormats,
       this.exportSettings.value.transcriptionFormat,
     );
 
@@ -235,6 +237,7 @@
       <div slot="content">
         <expandable-card
           ?expanded=${this.exportSettings.value.audio}
+          .hideContent=${audioFormats.length <= 1}
           @expandable-card-expanded=${this.enableExportAudio}
           @expandable-card-collapsed=${this.disableExportAudio}
         >
@@ -252,6 +255,7 @@
         <expandable-card
           ?expanded=${this.exportSettings.value.transcription}
           ?disabled=${!this.transcriptionAvailable.value}
+          .hideContent=${transcriptionFormats.length <= 1}
           @expandable-card-expanded=${this.enableExportTranscription}
           @expandable-card-collapsed=${this.disableExportTranscription}
         >
diff --git a/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts b/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts
index e5b989e..5eff631 100644
--- a/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts
+++ b/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts
@@ -27,6 +27,7 @@
   PageHandlerRemote,
   ResponseChunk,
   ResponseSummary,
+  SafetyFeature,
   SessionRemote,
   StreamingResponderCallbackRouter,
 } from './types.js';
@@ -39,38 +40,6 @@
 // TODO(shik): Make this configurable.
 const MAX_CONTENT_WORDS = Math.floor(((2048 - 100) / 4) * 3);
 
-/**
- * The keys are id of the safety classes.
- *
- * The safety class that each id corresponds to can be found at
- * //google3/chrome/intelligence/ondevice/data/example_text_safety.txtpb.
- *
- * TODO: b/349723775 - Adjust the threshold to the final one.
- */
-const REQUEST_SAFETY_SCORE_THRESHOLDS = new Map([
-  [4, 0.65],
-  [23, 0.65],
-]);
-
-const RESPONSE_SAFETY_SCORE_THRESHOLDS = new Map([
-  [0, 0.65],
-  [1, 0.65],
-  [2, 0.65],
-  [3, 0.65],
-  [4, 0.65],
-  [5, 0.65],
-  [6, 0.65],
-  [7, 0.65],
-  [9, 0.65],
-  [10, 0.65],
-  [11, 0.65],
-  [12, 0.8],
-  [18, 0.7],
-  [20, 0.65],
-  [21, 0.8],
-  [23, 0.65],
-]);
-
 function parseResponse(res: string): string {
   // Note this is NOT an underscore: ▁(U+2581)
   return res.replaceAll('▁', ' ').replaceAll(/\n+/g, '\n').trim();
@@ -126,20 +95,18 @@
 
   private async contentIsUnsafe(
     content: string,
-    thresholds: Map<number, number>,
+    safetyFeature: SafetyFeature,
   ): Promise<boolean> {
-    const info = await this.remote.classifyTextSafety(content);
-    const scores = info.safetyInfo?.classScores ?? null;
-    if (scores === null) {
+    const {safetyInfo} = await this.remote.classifyTextSafety(content);
+    if (safetyInfo === null) {
       return false;
     }
-    for (const [idx, threshold] of thresholds.entries()) {
-      const score = scores[idx];
-      if (score !== undefined && score >= threshold) {
-        return true;
-      }
-    }
-    return false;
+    const {isSafe} = await this.pageRemote.validateSafetyResult(
+      safetyFeature,
+      content,
+      safetyInfo,
+    );
+    return !isSafe;
   }
 
   close(): void {
@@ -168,6 +135,8 @@
    */
   protected async formatAndExecute(
     formatFeature: FormatFeature,
+    requestSafetyFeature: SafetyFeature,
+    responseSafetyFeature: SafetyFeature,
     fields: Record<string, string>,
   ): Promise<ModelResponse<string>> {
     const prompt = await this.formatInput(formatFeature, fields);
@@ -175,11 +144,11 @@
       console.error('formatInput returns null, wrong model?');
       return {kind: 'error', error: ModelResponseError.GENERAL};
     }
-    if (await this.contentIsUnsafe(prompt, REQUEST_SAFETY_SCORE_THRESHOLDS)) {
+    if (await this.contentIsUnsafe(prompt, requestSafetyFeature)) {
       return {kind: 'error', error: ModelResponseError.UNSAFE};
     }
     const result = await this.executeRaw(prompt);
-    if (await this.contentIsUnsafe(result, RESPONSE_SAFETY_SCORE_THRESHOLDS)) {
+    if (await this.contentIsUnsafe(result, responseSafetyFeature)) {
       return {kind: 'error', error: ModelResponseError.UNSAFE};
     }
     return {kind: 'success', result};
@@ -189,9 +158,14 @@
 export class SummaryModel extends OnDeviceModel<string> {
   override async execute(content: string): Promise<ModelResponse<string>> {
     content = shorten(content, MAX_CONTENT_WORDS);
-    const resp = await this.formatAndExecute(FormatFeature.kAudioSummary, {
-      transcription: content,
-    });
+    const resp = await this.formatAndExecute(
+      FormatFeature.kAudioSummary,
+      SafetyFeature.kAudioSummaryRequest,
+      SafetyFeature.kAudioSummaryResponse,
+      {
+        transcription: content,
+      },
+    );
     // TODO(pihsun): `Result` monadic helper class?
     if (resp.kind === 'error') {
       return resp;
@@ -204,9 +178,14 @@
 export class TitleSuggestionModel extends OnDeviceModel<string[]> {
   override async execute(content: string): Promise<ModelResponse<string[]>> {
     content = shorten(content, MAX_CONTENT_WORDS);
-    const resp = await this.formatAndExecute(FormatFeature.kAudioTitle, {
-      transcription: content,
-    });
+    const resp = await this.formatAndExecute(
+      FormatFeature.kAudioTitle,
+      SafetyFeature.kAudioTitleRequest,
+      SafetyFeature.kAudioTitleResponse,
+      {
+        transcription: content,
+      },
+    );
     if (resp.kind === 'error') {
       return resp;
     }
diff --git a/ash/webui/recorder_app_ui/resources/platforms/swa/types.ts b/ash/webui/recorder_app_ui/resources/platforms/swa/types.ts
index aedf7436..9dc8466 100644
--- a/ash/webui/recorder_app_ui/resources/platforms/swa/types.ts
+++ b/ash/webui/recorder_app_ui/resources/platforms/swa/types.ts
@@ -18,6 +18,7 @@
 } from '../../mojom/on_device_model.mojom-webui.js';
 export {
   FormatFeature,
+  SafetyFeature,
 } from '../../mojom/on_device_model_service.mojom-webui.js';
 export {
   type ModelState,
diff --git a/ash/wm/overview/overview_metrics.cc b/ash/wm/overview/overview_metrics.cc
index 27f1648..9a3ce83 100644
--- a/ash/wm/overview/overview_metrics.cc
+++ b/ash/wm/overview/overview_metrics.cc
@@ -21,10 +21,14 @@
 #include "base/time/time.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/presentation_time_recorder.h"
+#include "ui/display/screen.h"
 
 namespace ash {
 namespace {
 
+constexpr base::TimeDelta kPresentationTimeMinLatency = base::Milliseconds(1);
+constexpr int kPresentationTimeNumBuckets = 50;
+
 int GetNumWindowsOnAllDesks() {
   int num_windows_found = 0;
   for (const auto& desk : DesksController::Get()->desks()) {
@@ -61,9 +65,6 @@
     DeskBarVisibility desk_bar_visibility,
     const std::string& enter_metric_name,
     const std::string& exit_metric_name) {
-  constexpr base::TimeDelta kMinLatency = base::Milliseconds(1);
-  constexpr int kNumBuckets = 50;
-
   // Only record for `kShownImmediately` because that's the only case where the
   // desk bar's rendering was a part of the first frame's overall presentation
   // latency when entering overview.
@@ -71,9 +72,10 @@
     const std::optional<base::TimeDelta> enter_latency =
         enter_recorder->GetAverageLatency();
     if (enter_latency) {
-      base::UmaHistogramCustomTimes(
-          enter_metric_name, *enter_latency, kMinLatency,
-          kOverviewEnterExitPresentationMaxLatency, kNumBuckets);
+      base::UmaHistogramCustomTimes(enter_metric_name, *enter_latency,
+                                    kPresentationTimeMinLatency,
+                                    kOverviewEnterExitPresentationMaxLatency,
+                                    kPresentationTimeNumBuckets);
     }
   }
 
@@ -84,9 +86,10 @@
     const std::optional<base::TimeDelta> exit_latency =
         exit_recorder->GetAverageLatency();
     if (exit_latency) {
-      base::UmaHistogramCustomTimes(
-          exit_metric_name, *exit_latency, kMinLatency,
-          kOverviewEnterExitPresentationMaxLatency, kNumBuckets);
+      base::UmaHistogramCustomTimes(exit_metric_name, *exit_latency,
+                                    kPresentationTimeMinLatency,
+                                    kOverviewEnterExitPresentationMaxLatency,
+                                    kPresentationTimeNumBuckets);
     }
   }
 }
@@ -128,4 +131,39 @@
       kOverviewEnterExitPresentationMaxLatency * 2);
 }
 
+void RecordOverviewEnterPresentationTimeWithReason(
+    OverviewStartAction start_action,
+    base::TimeDelta presentation_time) {
+  const char* suffix = nullptr;
+  switch (start_action) {
+    case OverviewStartAction::kDevTools:
+    case OverviewStartAction::kTests:
+    case OverviewStartAction::kBentoBar_DEPRECATED:
+      suffix = "Other";
+      break;
+    case OverviewStartAction::kPine:
+      suffix = "InformedRestore";
+      break;
+    case OverviewStartAction::kSplitView:
+    case OverviewStartAction::kAccelerator:
+    case OverviewStartAction::kDragWindowFromShelf:
+    case OverviewStartAction::kExitHomeLauncher:
+    case OverviewStartAction::kOverviewButton:
+    case OverviewStartAction::kOverviewButtonLongPress:
+    case OverviewStartAction::k3FingerVerticalScroll:
+    case OverviewStartAction::kWallpaper:
+    case OverviewStartAction::kOverviewDeskSwitch:
+    case OverviewStartAction::kDeskButton:
+    case OverviewStartAction::kFasterSplitScreenSetup:
+      suffix = display::Screen::GetScreen()->InTabletMode()
+                   ? "UserInitiatedTablet"
+                   : "UserInitiatedClamshell";
+      break;
+  }
+  base::UmaHistogramCustomTimes(
+      base::StrCat({kEnterOverviewPresentationHistogram, ".", suffix}),
+      presentation_time, kPresentationTimeMinLatency,
+      kOverviewEnterExitPresentationMaxLatency, kPresentationTimeNumBuckets);
+}
+
 }  // namespace ash
diff --git a/ash/wm/overview/overview_metrics.h b/ash/wm/overview/overview_metrics.h
index 2bf205d4..f19f890 100644
--- a/ash/wm/overview/overview_metrics.h
+++ b/ash/wm/overview/overview_metrics.h
@@ -98,6 +98,17 @@
     std::unique_ptr<ui::PresentationTimeRecorder> exit_recorder,
     DeskBarVisibility desk_bar_visibility);
 
+// Records metric with format:
+// "Ash.Overview.Enter.PresentationTime.{OverviewStartReason}"
+//
+// where {OverviewStartReason} is derived from the `start_action`. This is the
+// exact same measurement as `kEnterOverviewPresentationHistogram`, but
+// segmented by different use cases that have different profiles and performance
+// characteristics.
+ASH_EXPORT void RecordOverviewEnterPresentationTimeWithReason(
+    OverviewStartAction start_action,
+    base::TimeDelta presentation_time);
+
 }  // namespace ash
 
 #endif  // ASH_WM_OVERVIEW_OVERVIEW_METRICS_H_
diff --git a/ash/wm/overview/overview_session_metrics_recorder.cc b/ash/wm/overview/overview_session_metrics_recorder.cc
index 8726d3e..b500351f 100644
--- a/ash/wm/overview/overview_session_metrics_recorder.cc
+++ b/ash/wm/overview/overview_session_metrics_recorder.cc
@@ -90,6 +90,18 @@
           kOverviewEnterExitPresentationMaxLatency,
           /*emit_trace_event=*/true);
   exit_presentation_time_recorder->RequestNext();
+
+  CHECK(enter_presentation_time_recorder_);
+  const std::optional<base::TimeDelta> enter_presentation_time =
+      enter_presentation_time_recorder_->GetAverageLatency();
+  // `enter_presentation_time` can be null if the overview session ends before
+  // the first overview frame is presented. This should be rare and is ok to
+  // ignore.
+  if (enter_presentation_time) {
+    RecordOverviewEnterPresentationTimeWithReason(start_action_,
+                                                  *enter_presentation_time);
+  }
+
   if (IsRenderingDeskBarWithMiniViews()) {
     SchedulePresentationTimeMetricsWithDeskBar(
         std::move(enter_presentation_time_recorder_),
diff --git a/ash/wm/overview/overview_session_metrics_recorder_unittest.cc b/ash/wm/overview/overview_session_metrics_recorder_unittest.cc
index 5cdd5819..921675c 100644
--- a/ash/wm/overview/overview_session_metrics_recorder_unittest.cc
+++ b/ash/wm/overview/overview_session_metrics_recorder_unittest.cc
@@ -26,18 +26,21 @@
 
 class OverviewSessionMetricsRecorderTest : public AshTestBase {
  protected:
+  void WaitForNextFramePresentation() {
+    ASSERT_TRUE(ui::WaitForNextFrameToBePresented(
+        Shell::Get()->GetPrimaryRootWindow()->GetHost()->compositor()));
+  }
+
   void EnterOverviewAndWaitForAnimation() {
     ASSERT_TRUE(EnterOverview());
     // Required for presentation time to be recorded.
-    ASSERT_TRUE(ui::WaitForNextFrameToBePresented(
-        Shell::Get()->GetPrimaryRootWindow()->GetHost()->compositor()));
+    WaitForNextFramePresentation();
     WaitForOverviewEnterAnimation();
   }
 
   void ExitOverviewAndWaitForAnimation() {
     ASSERT_TRUE(ExitOverview());
-    ASSERT_TRUE(ui::WaitForNextFrameToBePresented(
-        Shell::Get()->GetPrimaryRootWindow()->GetHost()->compositor()));
+    WaitForNextFramePresentation();
     WaitForOverviewExitAnimation();
   }
 
@@ -70,8 +73,7 @@
 TEST_F(OverviewSessionMetricsRecorderTest, DeskBarVisibilityShownImmediately) {
   ASSERT_TRUE(EnterOverview());
   // Required for presentation time to be recorded.
-  ASSERT_TRUE(ui::WaitForNextFrameToBePresented(
-      Shell::Get()->GetPrimaryRootWindow()->GetHost()->compositor()));
+  WaitForNextFramePresentation();
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   ASSERT_TRUE(overview_grid->desks_bar_view());
@@ -96,15 +98,13 @@
   ASSERT_FALSE(WindowState::Get(window2.get())->IsMaximized());
 
   ASSERT_TRUE(EnterOverview());
-  ASSERT_TRUE(ui::WaitForNextFrameToBePresented(
-      Shell::Get()->GetPrimaryRootWindow()->GetHost()->compositor()));
+  WaitForNextFramePresentation();
   const auto* overview_grid =
       GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   ASSERT_FALSE(overview_grid->desks_bar_view());
 
   WaitForOverviewEnterAnimation();
-  ASSERT_TRUE(ui::WaitForNextFrameToBePresented(
-      Shell::Get()->GetPrimaryRootWindow()->GetHost()->compositor()));
+  WaitForNextFramePresentation();
   ASSERT_TRUE(overview_grid->desks_bar_view());
 
   ExitOverviewAndWaitForAnimation();
@@ -128,4 +128,39 @@
                                        DeskBarVisibility::kNotShown, 1);
 }
 
+TEST_F(OverviewSessionMetricsRecorderTest,
+       EnterPresentationTimeSegmentedByReason) {
+  OverviewController* const controller = OverviewController::Get();
+  ASSERT_TRUE(controller->StartOverview(OverviewStartAction::kOverviewButton));
+  WaitForNextFramePresentation();
+  ExitOverviewAndWaitForAnimation();
+
+  histogram_tester_.ExpectTotalCount(
+      "Ash.Overview.Enter.PresentationTime.UserInitiatedClamshell", 1);
+
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+
+  ASSERT_TRUE(
+      controller->StartOverview(OverviewStartAction::kDragWindowFromShelf));
+  WaitForNextFramePresentation();
+  ExitOverviewAndWaitForAnimation();
+
+  histogram_tester_.ExpectTotalCount(
+      "Ash.Overview.Enter.PresentationTime.UserInitiatedTablet", 1);
+
+  ASSERT_TRUE(controller->StartOverview(OverviewStartAction::kTests));
+  WaitForNextFramePresentation();
+  ASSERT_TRUE(ExitOverview());
+
+  histogram_tester_.ExpectTotalCount(
+      "Ash.Overview.Enter.PresentationTime.Other", 1);
+
+  ASSERT_TRUE(controller->StartOverview(OverviewStartAction::kPine));
+  WaitForNextFramePresentation();
+  ASSERT_TRUE(ExitOverview());
+
+  histogram_tester_.ExpectTotalCount(
+      "Ash.Overview.Enter.PresentationTime.InformedRestore", 1);
+}
+
 }  // namespace ash
diff --git a/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn b/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
index 8c180558..4451c4e 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
+++ b/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
@@ -149,6 +149,7 @@
     "GLUE_CORE_POOLS=$glue_core_pools",
     "HAS_64_BIT_POINTERS=$has_64_bit_pointers",
     "HAS_MEMORY_TAGGING=$has_memory_tagging",
+    "IS_ANDROID=$is_android",
     "IS_CASTOS=$is_castos",
     "IS_CAST_ANDROID=$is_cast_android",
     "IS_CHROMEOS=$is_chromeos",
diff --git a/base/allocator/partition_allocator/src/partition_alloc/build_config.h b/base/allocator/partition_allocator/src/partition_alloc/build_config.h
index 410e7ec..12f5648 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/build_config.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/build_config.h
@@ -16,9 +16,8 @@
 // This files contains the following definition:
 //
 // Operating system:
-//   IS_IOS / IS_AIX / IS_ANDROID / IS_ASMJS / IS_FREEBSD / IS_FUCHSIA /
-//   IS_LINUX / IS_MAC / IS_NACL / IS_NETBSD / IS_OPENBSD / IS_QNX /
-//   IS_SOLARIS / IS_WIN
+//   IS_IOS / IS_AIX / IS_ASMJS / IS_FREEBSD / IS_FUCHSIA / IS_LINUX / IS_MAC /
+//   IS_NACL / IS_NETBSD / IS_OPENBSD / IS_QNX / IS_SOLARIS / IS_WIN
 //
 // Operating system family:
 //   IS_APPLE / IS_BSD / IS_POSIX
@@ -59,8 +58,14 @@
 #if defined(__native_client__)
 // __native_client__ must be first, so that other IS_ defines are not set.
 #define PA_IS_NACL
-#elif defined(ANDROID)
-#define PA_IS_ANDROID
+#elif PA_BUILDFLAG(IS_ANDROID)
+// The IS_ANDROID PA_BUILDFLAG macro is defined in buildflags.h.
+//
+// PartitionAlloc's embedders (Chromium, Dawn, Pdfium, Skia) define different
+// macros for Android builds: "ANDROID" or "SK_BUILD_FOR_ANDROID".
+//
+// To avoid relying on these external definitions, PartitionAlloc uses its own
+// dedicated build flag.
 #elif defined(__APPLE__)
 // Only include TargetConditionals after testing ANDROID as some Android builds
 // on the Mac have this header available and it's not needed unless the target
@@ -74,7 +79,7 @@
 #elif defined(__linux__)
 #if !PA_BUILDFLAG(IS_CHROMEOS)
 // Do not define PA_IS_LINUX on Chrome OS build.
-// The IS_CHROMEOS PA_BUILDFLAG macro is defined in chromeos_buildflags.h.
+// The IS_CHROMEOS PA_BUILDFLAG macro is defined in buildflags.h.
 #define PA_IS_LINUX
 #endif  // !PA_BUILDFLAG(IS_CHROMEOS)
 // Include a system header to pull in features.h for glibc/uclibc macros.
@@ -114,11 +119,11 @@
 #define PA_IS_BSD
 #endif
 
-#if defined(PA_IS_AIX) || defined(PA_IS_ANDROID) || defined(PA_IS_ASMJS) ||  \
-    defined(PA_IS_FREEBSD) || defined(PA_IS_IOS) || defined(PA_IS_LINUX) ||  \
-    defined(PA_IS_CHROMEOS) || defined(PA_IS_MAC) || defined(PA_IS_NACL) ||  \
-    defined(PA_IS_NETBSD) || defined(PA_IS_OPENBSD) || defined(PA_IS_QNX) || \
-    defined(PA_IS_SOLARIS) || PA_BUILDFLAG(IS_CHROMEOS)
+#if defined(PA_IS_AIX) || defined(PA_IS_ASMJS) || defined(PA_IS_FREEBSD) ||   \
+    defined(PA_IS_IOS) || defined(PA_IS_LINUX) || defined(PA_IS_CHROMEOS) ||  \
+    defined(PA_IS_MAC) || defined(PA_IS_NACL) || defined(PA_IS_NETBSD) ||     \
+    defined(PA_IS_OPENBSD) || defined(PA_IS_QNX) || defined(PA_IS_SOLARIS) || \
+    PA_BUILDFLAG(IS_ANDROID) || PA_BUILDFLAG(IS_CHROMEOS)
 #define PA_IS_POSIX
 #endif
 
@@ -388,13 +393,6 @@
 #endif
 #undef PA_IS_AIX
 
-#if defined(PA_IS_ANDROID)
-#define PA_BUILDFLAG_INTERNAL_IS_ANDROID() (1)
-#else
-#define PA_BUILDFLAG_INTERNAL_IS_ANDROID() (0)
-#endif
-#undef PA_IS_ANDROID
-
 #if defined(PA_IS_APPLE)
 #define PA_BUILDFLAG_INTERNAL_IS_APPLE() (1)
 #else
diff --git a/base/allocator/partition_allocator/src/partition_alloc/lightweight_quarantine.cc b/base/allocator/partition_allocator/src/partition_alloc/lightweight_quarantine.cc
index 535fb8473..5f8727b3 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/lightweight_quarantine.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/lightweight_quarantine.cc
@@ -59,7 +59,6 @@
 
 LightweightQuarantineBranch::~LightweightQuarantineBranch() {
   Purge();
-  slots_.clear();
 }
 
 bool LightweightQuarantineBranch::Quarantine(
@@ -72,17 +71,17 @@
   const size_t capacity_in_bytes =
       branch_capacity_in_bytes_.load(std::memory_order_relaxed);
 
+  if (capacity_in_bytes < usable_size) [[unlikely]] {
+    // Even if this branch dequarantines all entries held by it, this entry
+    // cannot fit within the capacity.
+    root_.allocator_root_.FreeNoHooksImmediate(object, slot_span, slot_start);
+    root_.quarantine_miss_count_.fetch_add(1u, std::memory_order_relaxed);
+    return false;
+  }
+
   {
     ConditionalScopedGuard guard(lock_required_, lock_);
 
-    if (capacity_in_bytes < usable_size) {
-      // Even if this branch dequarantines all entries held by it, this entry
-      // cannot fit within the capacity.
-      root_.allocator_root_.FreeNoHooksImmediate(object, slot_span, slot_start);
-      root_.quarantine_miss_count_.fetch_add(1u, std::memory_order_relaxed);
-      return false;
-    }
-
     // Dequarantine some entries as required.
     PurgeInternal(capacity_in_bytes - usable_size);
 
@@ -124,6 +123,7 @@
 void LightweightQuarantineBranch::Purge() {
   ConditionalScopedGuard guard(lock_required_, lock_);
   PurgeInternal(0);
+  PA_DCHECK(slots_.empty());
   slots_.shrink_to_fit();
 }
 
@@ -133,7 +133,9 @@
   int64_t freed_size_in_bytes = 0;
 
   // Dequarantine some entries as required.
-  while (!slots_.empty() && target_size_in_bytes < branch_size_in_bytes_) {
+  while (target_size_in_bytes < branch_size_in_bytes_) {
+    PA_DCHECK(!slots_.empty());
+
     // As quarantined entries are shuffled, picking last entry is equivalent
     // to picking random entry.
     const auto& to_free = slots_.back();
diff --git a/base/i18n/time_formatting.cc b/base/i18n/time_formatting.cc
index fb541d7..ece0989 100644
--- a/base/i18n/time_formatting.cc
+++ b/base/i18n/time_formatting.cc
@@ -363,16 +363,15 @@
   icu::UnicodeString formatted;
   icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
   if (hours != 0 || width == DurationFormatWidth::DURATION_WIDTH_NUMERIC) {
-    measure_format.formatMeasures(
-        (icu::Measure[3]){hours_measure, minutes_measure, seconds_measure}, 3,
-        formatted, ignore, status);
+    icu::Measure input_measures[3]{hours_measure, minutes_measure,
+                                   seconds_measure};
+    measure_format.formatMeasures(input_measures, 3, formatted, ignore, status);
   } else if (minutes != 0) {
-    measure_format.formatMeasures(
-        (icu::Measure[2]){minutes_measure, seconds_measure}, 2, formatted,
-        ignore, status);
+    icu::Measure input_measures[2]{minutes_measure, seconds_measure};
+    measure_format.formatMeasures(input_measures, 2, formatted, ignore, status);
   } else {
-    measure_format.formatMeasures((icu::Measure[1]){seconds_measure}, 1,
-                                  formatted, ignore, status);
+    icu::Measure input_measures[1]{seconds_measure};
+    measure_format.formatMeasures(input_measures, 1, formatted, ignore, status);
   }
   *out = i18n::UnicodeStringToString16(formatted);
   return U_SUCCESS(status);
diff --git a/base/memory/raw_ptr.md b/base/memory/raw_ptr.md
index c600dde..1b03793 100644
--- a/base/memory/raw_ptr.md
+++ b/base/memory/raw_ptr.md
@@ -60,11 +60,11 @@
 - ChromeOS (incl. Ash & Lacros)
 - macOS
 - Linux
+- Fuchsia
 
 In particular, it isn't enabled by default on:
 - iOS
 - ChromeCast
-- Fuchsia
 - Aix
 - Zos
 
diff --git a/base/power_monitor/power_monitor.cc b/base/power_monitor/power_monitor.cc
index 510c7a6b..5e1c3b9 100644
--- a/base/power_monitor/power_monitor.cc
+++ b/base/power_monitor/power_monitor.cc
@@ -16,62 +16,50 @@
 
 void PowerMonitor::Initialize(std::unique_ptr<PowerMonitorSource> source) {
   DCHECK(!IsInitialized());
-  PowerMonitor* power_monitor = GetInstance();
-  power_monitor->source_ = std::move(source);
+  source_ = std::move(source);
 
   // When a power source is associated with the power monitor, ensure the
   // initial state is propagated to observers, if needed.
-  PowerMonitor::NotifyPowerStateChange(
-      PowerMonitor::Source()->GetBatteryPowerStatus());
+  NotifyPowerStateChange(Source()->GetBatteryPowerStatus());
 
-  PowerMonitor::PowerMonitor::NotifyThermalStateChange(
-      PowerMonitor::Source()->GetCurrentThermalState());
+  NotifyThermalStateChange(Source()->GetCurrentThermalState());
 
-  PowerMonitor::PowerMonitor::NotifySpeedLimitChange(
-      PowerMonitor::Source()->GetInitialSpeedLimit());
+  NotifySpeedLimitChange(Source()->GetInitialSpeedLimit());
 }
 
-bool PowerMonitor::IsInitialized() {
-  return GetInstance()->source_.get() != nullptr;
+bool PowerMonitor::IsInitialized() const {
+  return source_ != nullptr;
 }
 
-// static
 void PowerMonitor::AddPowerSuspendObserver(PowerSuspendObserver* obs) {
-  GetInstance()->power_suspend_observers_->AddObserver(obs);
+  power_suspend_observers_->AddObserver(obs);
 }
 
-// static
 void PowerMonitor::RemovePowerSuspendObserver(PowerSuspendObserver* obs) {
-  GetInstance()->power_suspend_observers_->RemoveObserver(obs);
+  power_suspend_observers_->RemoveObserver(obs);
 }
 
-// static
 void PowerMonitor::AddPowerStateObserver(PowerStateObserver* obs) {
-  GetInstance()->power_state_observers_->AddObserver(obs);
+  power_state_observers_->AddObserver(obs);
 }
 
-// static
 void PowerMonitor::RemovePowerStateObserver(PowerStateObserver* obs) {
-  GetInstance()->power_state_observers_->RemoveObserver(obs);
+  power_state_observers_->RemoveObserver(obs);
 }
 
-// static
 void PowerMonitor::AddPowerThermalObserver(PowerThermalObserver* obs) {
-  GetInstance()->thermal_state_observers_->AddObserver(obs);
+  thermal_state_observers_->AddObserver(obs);
 }
 
-// static
 void PowerMonitor::RemovePowerThermalObserver(PowerThermalObserver* obs) {
-  GetInstance()->thermal_state_observers_->RemoveObserver(obs);
+  thermal_state_observers_->RemoveObserver(obs);
 }
 
-// static
 bool PowerMonitor::AddPowerSuspendObserverAndReturnSuspendedState(
     PowerSuspendObserver* obs) {
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->is_system_suspended_lock_);
-  power_monitor->power_suspend_observers_->AddObserver(obs);
-  return power_monitor->is_system_suspended_;
+  AutoLock auto_lock(is_system_suspended_lock_);
+  power_suspend_observers_->AddObserver(obs);
+  return is_system_suspended_;
 }
 
 // static
@@ -84,84 +72,79 @@
 PowerStateObserver::BatteryPowerStatus
 PowerMonitor::AddPowerStateObserverAndReturnBatteryPowerStatus(
     PowerStateObserver* obs) {
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->battery_power_status_lock_);
-  power_monitor->power_state_observers_->AddObserver(obs);
-  return power_monitor->battery_power_status_;
+  AutoLock auto_lock(battery_power_status_lock_);
+  power_state_observers_->AddObserver(obs);
+  return battery_power_status_;
 }
 
 // static
 PowerThermalObserver::DeviceThermalState
 PowerMonitor::AddPowerStateObserverAndReturnPowerThermalState(
     PowerThermalObserver* obs) {
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->power_thermal_state_lock_);
-  power_monitor->thermal_state_observers_->AddObserver(obs);
-  return power_monitor->power_thermal_state_;
+  AutoLock auto_lock(power_thermal_state_lock_);
+  thermal_state_observers_->AddObserver(obs);
+  return power_thermal_state_;
 }
 
-PowerMonitorSource* PowerMonitor::Source() {
-  return GetInstance()->source_.get();
+const PowerMonitorSource* PowerMonitor::Source() const {
+  return source_.get();
 }
 
-bool PowerMonitor::IsOnBatteryPower() {
+bool PowerMonitor::IsOnBatteryPower() const {
   DCHECK(IsInitialized());
   return GetBatteryPowerStatus() ==
          PowerStateObserver::BatteryPowerStatus::kBatteryPower;
 }
 
-PowerStateObserver::BatteryPowerStatus PowerMonitor::GetBatteryPowerStatus() {
+PowerStateObserver::BatteryPowerStatus PowerMonitor::GetBatteryPowerStatus()
+    const {
   DCHECK(IsInitialized());
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->battery_power_status_lock_);
-  return power_monitor->battery_power_status_;
+  AutoLock auto_lock(battery_power_status_lock_);
+  return battery_power_status_;
 }
 
-TimeTicks PowerMonitor::GetLastSystemResumeTime() {
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->is_system_suspended_lock_);
-  return power_monitor->last_system_resume_time_;
+TimeTicks PowerMonitor::GetLastSystemResumeTime() const {
+  AutoLock auto_lock(is_system_suspended_lock_);
+  return last_system_resume_time_;
 }
 
 void PowerMonitor::ShutdownForTesting() {
-  GetInstance()->source_ = nullptr;
+  source_ = nullptr;
 
-  PowerMonitor* power_monitor = GetInstance();
   {
-    AutoLock auto_lock(power_monitor->is_system_suspended_lock_);
-    power_monitor->is_system_suspended_ = false;
-    power_monitor->last_system_resume_time_ = TimeTicks();
+    AutoLock auto_lock(is_system_suspended_lock_);
+    is_system_suspended_ = false;
+    last_system_resume_time_ = TimeTicks();
   }
   {
-    AutoLock auto_lock(power_monitor->battery_power_status_lock_);
-    power_monitor->battery_power_status_ =
+    AutoLock auto_lock(battery_power_status_lock_);
+    battery_power_status_ =
         PowerStateObserver::BatteryPowerStatus::kExternalPower;
   }
   {
-    AutoLock auto_lock(power_monitor->power_thermal_state_lock_);
-    power_monitor->power_thermal_state_ =
-        PowerThermalObserver::DeviceThermalState::kUnknown;
+    AutoLock auto_lock(power_thermal_state_lock_);
+    power_thermal_state_ = PowerThermalObserver::DeviceThermalState::kUnknown;
   }
 }
 
 // static
-PowerThermalObserver::DeviceThermalState
-PowerMonitor::GetCurrentThermalState() {
+PowerThermalObserver::DeviceThermalState PowerMonitor::GetCurrentThermalState()
+    const {
   DCHECK(IsInitialized());
-  return GetInstance()->source_->GetCurrentThermalState();
+  return source_->GetCurrentThermalState();
 }
 
 // static
 void PowerMonitor::SetCurrentThermalState(
     PowerThermalObserver::DeviceThermalState state) {
   DCHECK(IsInitialized());
-  GetInstance()->source_->SetCurrentThermalState(state);
+  source_->SetCurrentThermalState(state);
 }
 
 #if BUILDFLAG(IS_ANDROID)
-int PowerMonitor::GetRemainingBatteryCapacity() {
+int PowerMonitor::GetRemainingBatteryCapacity() const {
   DCHECK(IsInitialized());
-  return PowerMonitor::Source()->GetRemainingBatteryCapacity();
+  return Source()->GetRemainingBatteryCapacity();
 }
 #endif  // BUILDFLAG(IS_ANDROID)
 
@@ -188,11 +171,10 @@
              << " battery";
   }
 
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->battery_power_status_lock_);
-  if (power_monitor->battery_power_status_ != battery_power_status) {
-    power_monitor->battery_power_status_ = battery_power_status;
-    GetInstance()->power_state_observers_->Notify(
+  AutoLock auto_lock(battery_power_status_lock_);
+  if (battery_power_status_ != battery_power_status) {
+    battery_power_status_ = battery_power_status;
+    power_state_observers_->Notify(
         FROM_HERE, &PowerStateObserver::OnBatteryPowerStatusChange,
         battery_power_status);
   }
@@ -204,13 +186,12 @@
                        TRACE_EVENT_SCOPE_PROCESS);
   DVLOG(1) << "Power Suspending";
 
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->is_system_suspended_lock_);
-  if (!power_monitor->is_system_suspended_) {
-    power_monitor->is_system_suspended_ = true;
-    power_monitor->last_system_resume_time_ = TimeTicks::Max();
-    GetInstance()->power_suspend_observers_->Notify(
-        FROM_HERE, &PowerSuspendObserver::OnSuspend);
+  AutoLock auto_lock(is_system_suspended_lock_);
+  if (!is_system_suspended_) {
+    is_system_suspended_ = true;
+    last_system_resume_time_ = TimeTicks::Max();
+    power_suspend_observers_->Notify(FROM_HERE,
+                                     &PowerSuspendObserver::OnSuspend);
   }
 }
 
@@ -222,13 +203,12 @@
 
   TimeTicks resume_time = TimeTicks::Now();
 
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->is_system_suspended_lock_);
-  if (power_monitor->is_system_suspended_) {
-    power_monitor->is_system_suspended_ = false;
-    power_monitor->last_system_resume_time_ = resume_time;
-    GetInstance()->power_suspend_observers_->Notify(
-        FROM_HERE, &PowerSuspendObserver::OnResume);
+  AutoLock auto_lock(is_system_suspended_lock_);
+  if (is_system_suspended_) {
+    is_system_suspended_ = false;
+    last_system_resume_time_ = resume_time;
+    power_suspend_observers_->Notify(FROM_HERE,
+                                     &PowerSuspendObserver::OnResume);
   }
 }
 
@@ -238,11 +218,10 @@
   DVLOG(1) << "ThermalStateChange: "
            << PowerMonitorSource::DeviceThermalStateToString(new_state);
 
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->power_thermal_state_lock_);
-  if (power_monitor->power_thermal_state_ != new_state) {
-    power_monitor->power_thermal_state_ = new_state;
-    GetInstance()->thermal_state_observers_->Notify(
+  AutoLock auto_lock(power_thermal_state_lock_);
+  if (power_thermal_state_ != new_state) {
+    power_thermal_state_ = new_state;
+    thermal_state_observers_->Notify(
         FROM_HERE, &PowerThermalObserver::OnThermalStateChange, new_state);
   }
 }
@@ -251,11 +230,10 @@
   DCHECK(IsInitialized());
   DVLOG(1) << "SpeedLimitChange: " << speed_limit;
 
-  PowerMonitor* power_monitor = GetInstance();
-  AutoLock auto_lock(power_monitor->power_thermal_state_lock_);
-  if (power_monitor->speed_limit_ != speed_limit) {
-    power_monitor->speed_limit_ = speed_limit;
-    GetInstance()->thermal_state_observers_->Notify(
+  AutoLock auto_lock(power_thermal_state_lock_);
+  if (speed_limit_ != speed_limit) {
+    speed_limit_ = speed_limit;
+    thermal_state_observers_->Notify(
         FROM_HERE, &PowerThermalObserver::OnSpeedLimitChange, speed_limit);
   }
 }
diff --git a/base/power_monitor/power_monitor.h b/base/power_monitor/power_monitor.h
index 732d0bb7..ace480ff 100644
--- a/base/power_monitor/power_monitor.h
+++ b/base/power_monitor/power_monitor.h
@@ -26,11 +26,13 @@
 // of test contexts where the PowerMonitor global is never created.
 class BASE_EXPORT PowerMonitor {
  public:
+  static PowerMonitor* GetInstance();
+
   // Initializes global PowerMonitor state. Takes ownership of |source|, which
   // will be leaked on process teardown. May only be called once. Not threadsafe
   // - no other PowerMonitor methods may be called on any thread while calling
   // Initialize(). |source| must not be nullptr.
-  static void Initialize(std::unique_ptr<PowerMonitorSource> source);
+  void Initialize(std::unique_ptr<PowerMonitorSource> source);
 
   PowerMonitor(const PowerMonitor&) = delete;
   PowerMonitor& operator=(const PowerMonitor&) = delete;
@@ -38,7 +40,7 @@
   // Returns true if Initialize() has been called. Safe to call on any thread,
   // but must not be called while Initialize() or ShutdownForTesting() is being
   // invoked.
-  static bool IsInitialized();
+  bool IsInitialized() const;
 
   // Add and remove an observer.
   // Can be called from any thread. |observer| is notified on the sequence
@@ -47,12 +49,12 @@
   //
   // It is safe to add observers before the PowerMonitor is initialized. It is
   // safe to remove an observer even if it was not added as an observer.
-  static void AddPowerSuspendObserver(PowerSuspendObserver* observer);
-  static void RemovePowerSuspendObserver(PowerSuspendObserver* observer);
-  static void AddPowerStateObserver(PowerStateObserver* observer);
-  static void RemovePowerStateObserver(PowerStateObserver* observer);
-  static void AddPowerThermalObserver(PowerThermalObserver* observer);
-  static void RemovePowerThermalObserver(PowerThermalObserver* observer);
+  void AddPowerSuspendObserver(PowerSuspendObserver* observer);
+  void RemovePowerSuspendObserver(PowerSuspendObserver* observer);
+  void AddPowerStateObserver(PowerStateObserver* observer);
+  void RemovePowerStateObserver(PowerStateObserver* observer);
+  void AddPowerThermalObserver(PowerThermalObserver* observer);
+  void RemovePowerThermalObserver(PowerThermalObserver* observer);
 
   // Atomically add a PowerSuspendObserver and read the current power suspended
   // state. This variant must be used to avoid race between adding an observer
@@ -61,54 +63,53 @@
   //    if (PowerMonitor::IsSystemSuspended()) { ... }
   //
   // Returns true if the system is currently suspended.
-  static bool AddPowerSuspendObserverAndReturnSuspendedState(
+  bool AddPowerSuspendObserverAndReturnSuspendedState(
       PowerSuspendObserver* observer);
   // Returns true if the system is on-battery.
-  static bool AddPowerStateObserverAndReturnOnBatteryState(
+  bool AddPowerStateObserverAndReturnOnBatteryState(
       PowerStateObserver* observer);
-  static PowerStateObserver::BatteryPowerStatus
+  PowerStateObserver::BatteryPowerStatus
   AddPowerStateObserverAndReturnBatteryPowerStatus(
       PowerStateObserver* observer);
   // Returns the power thermal state.
-  static PowerThermalObserver::DeviceThermalState
+  PowerThermalObserver::DeviceThermalState
   AddPowerStateObserverAndReturnPowerThermalState(
       PowerThermalObserver* observer);
 
   // Is the computer currently on battery power. May only be called if the
   // PowerMonitor has been initialized.
-  static bool IsOnBatteryPower();
+  bool IsOnBatteryPower() const;
 
   // Returns the current state of the battery power, that can be unknown if the
   // value isn't initialized yet. May only be called if the PowerMonitor has
   // been initialized.
-  static PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus();
+  PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus() const;
 
   // Returns the time of the last system resume. If no system suspend/resume was
   // observed, returns an empty time. If the system is currently suspended,
   // returns TimeTicks::Max().
-  static TimeTicks GetLastSystemResumeTime();
+  TimeTicks GetLastSystemResumeTime() const;
 
   // Read the current DeviceThermalState if known. Can be called on any thread.
   // May only be called if the PowerMonitor has been initialized.
-  static PowerThermalObserver::DeviceThermalState GetCurrentThermalState();
+  PowerThermalObserver::DeviceThermalState GetCurrentThermalState() const;
 
   // Update the result of thermal state.
-  static void SetCurrentThermalState(
-      PowerThermalObserver::DeviceThermalState state);
+  void SetCurrentThermalState(PowerThermalObserver::DeviceThermalState state);
 
 #if BUILDFLAG(IS_ANDROID)
   // Read and return the current remaining battery capacity (microampere-hours).
   // Only supported with a device power source (i.e. not in child processes in
   // Chrome) and on devices with Android >= Lollipop as well as a power supply
   // that supports this counter. Returns 0 if unsupported.
-  static int GetRemainingBatteryCapacity();
+  int GetRemainingBatteryCapacity() const;
 #endif  // BUILDFLAG(IS_ANDROID)
 
   // Uninitializes the PowerMonitor. Should be called at the end of any unit
   // test that mocks out the PowerMonitor, to avoid affecting subsequent tests.
   // There must be no live observers when invoked. Safe to call even if the
   // PowerMonitor hasn't been initialized.
-  static void ShutdownForTesting();
+  void ShutdownForTesting();
 
  private:
   friend class PowerMonitorSource;
@@ -117,27 +118,25 @@
   PowerMonitor();
   ~PowerMonitor();
 
-  static PowerMonitorSource* Source();
+  const PowerMonitorSource* Source() const;
 
-  static void NotifyPowerStateChange(bool on_battery_power);
-  static void NotifyPowerStateChange(
+  void NotifyPowerStateChange(bool on_battery_power);
+  void NotifyPowerStateChange(
       PowerStateObserver::BatteryPowerStatus battery_power_status);
-  static void NotifySuspend();
-  static void NotifyResume();
-  static void NotifyThermalStateChange(
+  void NotifySuspend();
+  void NotifyResume();
+  void NotifyThermalStateChange(
       PowerThermalObserver::DeviceThermalState new_state);
-  static void NotifySpeedLimitChange(int speed_limit);
-
-  static PowerMonitor* GetInstance();
+  void NotifySpeedLimitChange(int speed_limit);
 
   bool is_system_suspended_ GUARDED_BY(is_system_suspended_lock_) = false;
-  Lock is_system_suspended_lock_;
+  mutable Lock is_system_suspended_lock_;
   TimeTicks last_system_resume_time_ GUARDED_BY(is_system_suspended_lock_);
 
   PowerStateObserver::BatteryPowerStatus battery_power_status_
       GUARDED_BY(battery_power_status_lock_) =
           PowerStateObserver::BatteryPowerStatus::kExternalPower;
-  Lock battery_power_status_lock_;
+  mutable Lock battery_power_status_lock_;
 
   PowerThermalObserver::DeviceThermalState power_thermal_state_
       GUARDED_BY(power_thermal_state_lock_) =
diff --git a/base/power_monitor/power_monitor_device_source.cc b/base/power_monitor/power_monitor_device_source.cc
index 740c6e9..03486eb5 100644
--- a/base/power_monitor/power_monitor_device_source.cc
+++ b/base/power_monitor/power_monitor_device_source.cc
@@ -8,7 +8,7 @@
 
 namespace base {
 
-bool PowerMonitorDeviceSource::IsOnBatteryPower() {
+bool PowerMonitorDeviceSource::IsOnBatteryPower() const {
   return GetBatteryPowerStatus() ==
          PowerStateObserver::BatteryPowerStatus::kBatteryPower;
 }
diff --git a/base/power_monitor/power_monitor_device_source.h b/base/power_monitor/power_monitor_device_source.h
index d57b511..02a55c7 100644
--- a/base/power_monitor/power_monitor_device_source.h
+++ b/base/power_monitor/power_monitor_device_source.h
@@ -62,7 +62,8 @@
 
   // These two methods is used for handling thermal state update requests, such
   // as asking for initial state when starting lisitening to thermal change.
-  PowerThermalObserver::DeviceThermalState GetCurrentThermalState() override;
+  PowerThermalObserver::DeviceThermalState GetCurrentThermalState()
+      const override;
   void SetCurrentThermalState(
       PowerThermalObserver::DeviceThermalState state) override;
 #endif
@@ -109,29 +110,31 @@
   // Platform-specific method to check whether the system is currently
   // running on battery power.  Returns true if running on batteries,
   // false otherwise.
-  bool IsOnBatteryPower() final;
+  bool IsOnBatteryPower() const final;
 
   // Platform-specific method to check whether the system is currently
   // running on battery power. Returns kBatteryPower if running on battery,
   // kExternalPower if running on external power or kUnknown if the power
   // state is unknown (for example, during early process lifetime when the
   // state hasn't been obtained yet).
-  PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus();
+  PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus() const;
 
 #if BUILDFLAG(IS_ANDROID)
-  PowerThermalObserver::DeviceThermalState GetCurrentThermalState() override;
-  int GetRemainingBatteryCapacity() override;
+  PowerThermalObserver::DeviceThermalState GetCurrentThermalState()
+      const override;
+  int GetRemainingBatteryCapacity() const override;
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_WIN)
   // PowerMonitorSource:
-  int GetInitialSpeedLimit() override;
+  int GetInitialSpeedLimit() const override;
 #endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_MAC)
   // PowerMonitorSource:
-  PowerThermalObserver::DeviceThermalState GetCurrentThermalState() override;
-  int GetInitialSpeedLimit() override;
+  PowerThermalObserver::DeviceThermalState GetCurrentThermalState()
+      const override;
+  int GetInitialSpeedLimit() const override;
 
   // Retrieves the current battery state to update `is_on_battery_`.
   void GetBatteryState();
diff --git a/base/power_monitor/power_monitor_device_source_android.cc b/base/power_monitor/power_monitor_device_source_android.cc
index 2395be4..891b1653 100644
--- a/base/power_monitor/power_monitor_device_source_android.cc
+++ b/base/power_monitor/power_monitor_device_source_android.cc
@@ -83,20 +83,20 @@
 }  // namespace android
 
 PowerStateObserver::BatteryPowerStatus
-PowerMonitorDeviceSource::GetBatteryPowerStatus() {
+PowerMonitorDeviceSource::GetBatteryPowerStatus() const {
   JNIEnv* env = jni_zero::AttachCurrentThread();
   int battery_power =
       base::android::Java_PowerMonitor_getBatteryPowerStatus(env);
   return static_cast<PowerStateObserver::BatteryPowerStatus>(battery_power);
 }
 
-int PowerMonitorDeviceSource::GetRemainingBatteryCapacity() {
+int PowerMonitorDeviceSource::GetRemainingBatteryCapacity() const {
   JNIEnv* env = jni_zero::AttachCurrentThread();
   return base::android::Java_PowerMonitor_getRemainingBatteryCapacity(env);
 }
 
 PowerThermalObserver::DeviceThermalState
-PowerMonitorDeviceSource::GetCurrentThermalState() {
+PowerMonitorDeviceSource::GetCurrentThermalState() const {
   JNIEnv* env = jni_zero::AttachCurrentThread();
   return android::MapToDeviceThermalState(
       android::Java_PowerMonitor_getCurrentThermalStatus(env));
diff --git a/base/power_monitor/power_monitor_device_source_chromeos.cc b/base/power_monitor/power_monitor_device_source_chromeos.cc
index 24487ba..68c4f14b 100644
--- a/base/power_monitor/power_monitor_device_source_chromeos.cc
+++ b/base/power_monitor/power_monitor_device_source_chromeos.cc
@@ -38,23 +38,24 @@
 }
 
 PowerStateObserver::BatteryPowerStatus
-PowerMonitorDeviceSource::GetBatteryPowerStatus() {
+PowerMonitorDeviceSource::GetBatteryPowerStatus() const {
   return g_battery_power_status;
 }
 
 // static
 void PowerMonitorDeviceSource::ThermalEventReceived(
     PowerThermalObserver::DeviceThermalState state) {
-  if (!PowerMonitor::IsInitialized()) {
-    PowerMonitor::Initialize(std::make_unique<PowerMonitorDeviceSource>());
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  if (!power_monitor->IsInitialized()) {
+    power_monitor->Initialize(std::make_unique<PowerMonitorDeviceSource>());
   }
-  PowerMonitor::SetCurrentThermalState(state);
+  power_monitor->SetCurrentThermalState(state);
 
   ProcessThermalEvent(state);
 }
 
 PowerThermalObserver::DeviceThermalState
-PowerMonitorDeviceSource::GetCurrentThermalState() {
+PowerMonitorDeviceSource::GetCurrentThermalState() const {
   return current_thermal_state_;
 }
 
diff --git a/base/power_monitor/power_monitor_device_source_ios.mm b/base/power_monitor/power_monitor_device_source_ios.mm
index 5b1abad..9dbbd82 100644
--- a/base/power_monitor/power_monitor_device_source_ios.mm
+++ b/base/power_monitor/power_monitor_device_source_ios.mm
@@ -11,7 +11,7 @@
 namespace base {
 
 PowerStateObserver::BatteryPowerStatus
-PowerMonitorDeviceSource::GetBatteryPowerStatus() {
+PowerMonitorDeviceSource::GetBatteryPowerStatus() const {
 #if TARGET_IPHONE_SIMULATOR
   return PowerStateObserver::BatteryPowerStatus::kExternalPower;
 #else
diff --git a/base/power_monitor/power_monitor_device_source_mac.mm b/base/power_monitor/power_monitor_device_source_mac.mm
index aa24838..c34efcf 100644
--- a/base/power_monitor/power_monitor_device_source_mac.mm
+++ b/base/power_monitor/power_monitor_device_source_mac.mm
@@ -20,11 +20,11 @@
 namespace base {
 
 PowerThermalObserver::DeviceThermalState
-PowerMonitorDeviceSource::GetCurrentThermalState() {
+PowerMonitorDeviceSource::GetCurrentThermalState() const {
   return thermal_state_observer_->GetCurrentThermalState();
 }
 
-int PowerMonitorDeviceSource::GetInitialSpeedLimit() {
+int PowerMonitorDeviceSource::GetInitialSpeedLimit() const {
   return thermal_state_observer_->GetCurrentSpeedLimit();
 }
 
@@ -96,7 +96,7 @@
 }
 
 PowerStateObserver::BatteryPowerStatus
-PowerMonitorDeviceSource::GetBatteryPowerStatus() {
+PowerMonitorDeviceSource::GetBatteryPowerStatus() const {
   return battery_power_status_;
 }
 
diff --git a/base/power_monitor/power_monitor_device_source_stub.cc b/base/power_monitor/power_monitor_device_source_stub.cc
index 3a91d32e..a876688 100644
--- a/base/power_monitor/power_monitor_device_source_stub.cc
+++ b/base/power_monitor/power_monitor_device_source_stub.cc
@@ -8,7 +8,7 @@
 namespace base {
 
 PowerStateObserver::BatteryPowerStatus
-PowerMonitorDeviceSource::GetBatteryPowerStatus() {
+PowerMonitorDeviceSource::GetBatteryPowerStatus() const {
   return PowerStateObserver::BatteryPowerStatus::kExternalPower;
 }
 
diff --git a/base/power_monitor/power_monitor_device_source_win.cc b/base/power_monitor/power_monitor_device_source_win.cc
index 630da2c43..bdd5c6b 100644
--- a/base/power_monitor/power_monitor_device_source_win.cc
+++ b/base/power_monitor/power_monitor_device_source_win.cc
@@ -76,7 +76,7 @@
 }
 
 PowerStateObserver::BatteryPowerStatus
-PowerMonitorDeviceSource::GetBatteryPowerStatus() {
+PowerMonitorDeviceSource::GetBatteryPowerStatus() const {
   SYSTEM_POWER_STATUS status;
   if (!::GetSystemPowerStatus(&status)) {
     DPLOG(ERROR) << "GetSystemPowerStatus failed";
@@ -87,7 +87,7 @@
              : PowerStateObserver::BatteryPowerStatus::kExternalPower;
 }
 
-int PowerMonitorDeviceSource::GetInitialSpeedLimit() {
+int PowerMonitorDeviceSource::GetInitialSpeedLimit() const {
   // Returns the maximum value once at start. Subsequent actual values will be
   // provided asynchronously via callbacks instead.
   return PowerThermalObserver::kSpeedLimitMax;
diff --git a/base/power_monitor/power_monitor_source.cc b/base/power_monitor/power_monitor_source.cc
index cf79bc4..fa9eadb 100644
--- a/base/power_monitor/power_monitor_source.cc
+++ b/base/power_monitor/power_monitor_source.cc
@@ -15,11 +15,11 @@
 PowerMonitorSource::~PowerMonitorSource() = default;
 
 PowerThermalObserver::DeviceThermalState
-PowerMonitorSource::GetCurrentThermalState() {
+PowerMonitorSource::GetCurrentThermalState() const {
   return PowerThermalObserver::DeviceThermalState::kUnknown;
 }
 
-int PowerMonitorSource::GetInitialSpeedLimit() {
+int PowerMonitorSource::GetInitialSpeedLimit() const {
   return PowerThermalObserver::kSpeedLimitMax;
 }
 
@@ -27,43 +27,47 @@
     PowerThermalObserver::DeviceThermalState state) {}
 
 #if BUILDFLAG(IS_ANDROID)
-int PowerMonitorSource::GetRemainingBatteryCapacity() {
+int PowerMonitorSource::GetRemainingBatteryCapacity() const {
   return 0;
 }
 #endif  // BUILDFLAG(IS_ANDROID)
 
 // static
 void PowerMonitorSource::ProcessPowerEvent(PowerEvent event_id) {
-  if (!PowerMonitor::IsInitialized())
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  if (!power_monitor->IsInitialized()) {
     return;
+  }
 
   switch (event_id) {
     case POWER_STATE_EVENT:
-      PowerMonitor::NotifyPowerStateChange(
-          PowerMonitor::Source()->GetBatteryPowerStatus());
+      power_monitor->NotifyPowerStateChange(
+          power_monitor->Source()->GetBatteryPowerStatus());
       break;
       case RESUME_EVENT:
-        PowerMonitor::NotifyResume();
-      break;
+        power_monitor->NotifyResume();
+        break;
       case SUSPEND_EVENT:
-        PowerMonitor::NotifySuspend();
-      break;
+        power_monitor->NotifySuspend();
+        break;
   }
 }
 
 // static
 void PowerMonitorSource::ProcessThermalEvent(
     PowerThermalObserver::DeviceThermalState new_thermal_state) {
-  if (!PowerMonitor::IsInitialized())
-    return;
-  PowerMonitor::NotifyThermalStateChange(new_thermal_state);
+  if (auto* power_monitor = base::PowerMonitor::GetInstance();
+      power_monitor->IsInitialized()) {
+    power_monitor->NotifyThermalStateChange(new_thermal_state);
+  }
 }
 
 // static
 void PowerMonitorSource::ProcessSpeedLimitEvent(int speed_limit) {
-  if (!PowerMonitor::IsInitialized())
-    return;
-  PowerMonitor::NotifySpeedLimitChange(speed_limit);
+  if (auto* power_monitor = base::PowerMonitor::GetInstance();
+      power_monitor->IsInitialized()) {
+    power_monitor->NotifySpeedLimitChange(speed_limit);
+  }
 }
 
 // static
diff --git a/base/power_monitor/power_monitor_source.h b/base/power_monitor/power_monitor_source.h
index 2fc3206d..afd944d 100644
--- a/base/power_monitor/power_monitor_source.h
+++ b/base/power_monitor/power_monitor_source.h
@@ -31,14 +31,15 @@
 
   // Reads the current DeviceThermalState, if available on the platform.
   // Otherwise, returns kUnknown.
-  virtual PowerThermalObserver::DeviceThermalState GetCurrentThermalState();
+  virtual PowerThermalObserver::DeviceThermalState GetCurrentThermalState()
+      const;
 
   // Reads the initial operating system CPU speed limit, if available on the
   // platform. Otherwise returns PowerThermalObserver::kSpeedLimitMax.
   // Only called on the main thread in PowerMonitor::Initialize().
   // The actual speed limit value will be updated asynchronously via the
   // ProcessSpeedLimitEvent() if/when the value changes.
-  virtual int GetInitialSpeedLimit();
+  virtual int GetInitialSpeedLimit() const;
 
   // Update the result of thermal state.
   virtual void SetCurrentThermalState(
@@ -46,9 +47,9 @@
 
   // Platform-specific method to check whether the system is currently
   // running on battery power.
-  virtual bool IsOnBatteryPower() = 0;
+  virtual bool IsOnBatteryPower() const = 0;
 
-  PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus() {
+  PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus() const {
     return IsOnBatteryPower()
                ? PowerStateObserver::BatteryPowerStatus::kBatteryPower
                : PowerStateObserver::BatteryPowerStatus::kExternalPower;
@@ -56,7 +57,7 @@
 
 #if BUILDFLAG(IS_ANDROID)
   // Read and return the current remaining battery capacity (microampere-hours).
-  virtual int GetRemainingBatteryCapacity();
+  virtual int GetRemainingBatteryCapacity() const;
 #endif  // BUILDFLAG(IS_ANDROID)
 
   static const char* DeviceThermalStateToString(
diff --git a/base/power_monitor/power_monitor_unittest.cc b/base/power_monitor/power_monitor_unittest.cc
index 2e118aa..44e1677 100644
--- a/base/power_monitor/power_monitor_unittest.cc
+++ b/base/power_monitor/power_monitor_unittest.cc
@@ -40,10 +40,11 @@
   PowerMonitorInitialize();
 
   PowerMonitorTestObserver observers[kObservers];
+  auto* power_monitor = PowerMonitor::GetInstance();
   for (auto& index : observers) {
-    PowerMonitor::AddPowerSuspendObserver(&index);
-    PowerMonitor::AddPowerStateObserver(&index);
-    PowerMonitor::AddPowerThermalObserver(&index);
+    power_monitor->AddPowerSuspendObserver(&index);
+    power_monitor->AddPowerStateObserver(&index);
+    power_monitor->AddPowerThermalObserver(&index);
   }
 
   // Sending resume when not suspended should have no effect.
@@ -122,15 +123,16 @@
             PowerThermalObserver::DeviceThermalState::kFair);
 
   for (auto& index : observers) {
-    PowerMonitor::RemovePowerSuspendObserver(&index);
-    PowerMonitor::RemovePowerStateObserver(&index);
-    PowerMonitor::RemovePowerThermalObserver(&index);
+    power_monitor->RemovePowerSuspendObserver(&index);
+    power_monitor->RemovePowerStateObserver(&index);
+    power_monitor->RemovePowerThermalObserver(&index);
   }
 }
 
 TEST_F(PowerMonitorTest, ThermalThrottling) {
   PowerMonitorTestObserver observer;
-  PowerMonitor::AddPowerThermalObserver(&observer);
+  auto* power_monitor = PowerMonitor::GetInstance();
+  power_monitor->AddPowerThermalObserver(&observer);
 
   PowerMonitorInitialize();
 
@@ -147,20 +149,21 @@
     EXPECT_EQ(observer.last_thermal_state(), state);
   }
 
-  PowerMonitor::RemovePowerThermalObserver(&observer);
+  power_monitor->RemovePowerThermalObserver(&observer);
 }
 
 TEST_F(PowerMonitorTest, AddPowerSuspendObserverBeforeAndAfterInitialization) {
   PowerMonitorTestObserver observer1;
   PowerMonitorTestObserver observer2;
+  auto* power_monitor = PowerMonitor::GetInstance();
 
   // An observer is added before the PowerMonitor initialization.
-  PowerMonitor::AddPowerSuspendObserver(&observer1);
+  power_monitor->AddPowerSuspendObserver(&observer1);
 
   PowerMonitorInitialize();
 
   // An observer is added after the PowerMonitor initialization.
-  PowerMonitor::AddPowerSuspendObserver(&observer2);
+  power_monitor->AddPowerSuspendObserver(&observer2);
 
   // Simulate suspend/resume notifications.
   source().GenerateSuspendEvent();
@@ -173,21 +176,22 @@
   EXPECT_EQ(observer1.resumes(), 1);
   EXPECT_EQ(observer2.resumes(), 1);
 
-  PowerMonitor::RemovePowerSuspendObserver(&observer1);
-  PowerMonitor::RemovePowerSuspendObserver(&observer2);
+  power_monitor->RemovePowerSuspendObserver(&observer1);
+  power_monitor->RemovePowerSuspendObserver(&observer2);
 }
 
 TEST_F(PowerMonitorTest, AddPowerStateObserverBeforeAndAfterInitialization) {
   PowerMonitorTestObserver observer1;
   PowerMonitorTestObserver observer2;
+  auto* power_monitor = PowerMonitor::GetInstance();
 
   // An observer is added before the PowerMonitor initialization.
-  PowerMonitor::AddPowerStateObserver(&observer1);
+  power_monitor->AddPowerStateObserver(&observer1);
 
   PowerMonitorInitialize();
 
   // An observer is added after the PowerMonitor initialization.
-  PowerMonitor::AddPowerStateObserver(&observer2);
+  power_monitor->AddPowerStateObserver(&observer2);
 
   // Simulate power state transitions (e.g. battery on/off).
   EXPECT_EQ(observer1.power_state_changes(), 0);
@@ -201,41 +205,43 @@
   EXPECT_EQ(observer1.power_state_changes(), 2);
   EXPECT_EQ(observer2.power_state_changes(), 2);
 
-  PowerMonitor::RemovePowerStateObserver(&observer1);
-  PowerMonitor::RemovePowerStateObserver(&observer2);
+  power_monitor->RemovePowerStateObserver(&observer1);
+  power_monitor->RemovePowerStateObserver(&observer2);
 }
 
 TEST_F(PowerMonitorTest, SuspendStateReturnedFromAddObserver) {
   PowerMonitorTestObserver observer1;
   PowerMonitorTestObserver observer2;
+  auto* power_monitor = PowerMonitor::GetInstance();
 
   PowerMonitorInitialize();
 
-  EXPECT_FALSE(
-      PowerMonitor::AddPowerSuspendObserverAndReturnSuspendedState(&observer1));
+  EXPECT_FALSE(power_monitor->AddPowerSuspendObserverAndReturnSuspendedState(
+      &observer1));
 
   source().GenerateSuspendEvent();
 
-  EXPECT_TRUE(
-      PowerMonitor::AddPowerSuspendObserverAndReturnSuspendedState(&observer2));
+  EXPECT_TRUE(power_monitor->AddPowerSuspendObserverAndReturnSuspendedState(
+      &observer2));
 
   EXPECT_EQ(observer1.suspends(), 1);
   EXPECT_EQ(observer2.suspends(), 0);
   EXPECT_EQ(observer1.resumes(), 0);
   EXPECT_EQ(observer2.resumes(), 0);
 
-  PowerMonitor::RemovePowerSuspendObserver(&observer1);
-  PowerMonitor::RemovePowerSuspendObserver(&observer2);
+  power_monitor->RemovePowerSuspendObserver(&observer1);
+  power_monitor->RemovePowerSuspendObserver(&observer2);
 }
 
 TEST_F(PowerMonitorTest, PowerStateReturnedFromAddObserver) {
   PowerMonitorTestObserver observer1;
   PowerMonitorTestObserver observer2;
+  auto* power_monitor = PowerMonitor::GetInstance();
 
   PowerMonitorInitialize();
 
   // An observer is added before the on-battery notification.
-  EXPECT_NE(PowerMonitor::AddPowerStateObserverAndReturnBatteryPowerStatus(
+  EXPECT_NE(power_monitor->AddPowerStateObserverAndReturnBatteryPowerStatus(
                 &observer1),
             PowerStateObserver::BatteryPowerStatus::kBatteryPower);
 
@@ -243,15 +249,15 @@
       PowerStateObserver::BatteryPowerStatus::kBatteryPower);
 
   // An observer is added after the on-battery notification.
-  EXPECT_EQ(PowerMonitor::AddPowerStateObserverAndReturnBatteryPowerStatus(
+  EXPECT_EQ(power_monitor->AddPowerStateObserverAndReturnBatteryPowerStatus(
                 &observer2),
             PowerStateObserver::BatteryPowerStatus::kBatteryPower);
 
   EXPECT_EQ(observer1.power_state_changes(), 1);
   EXPECT_EQ(observer2.power_state_changes(), 0);
 
-  PowerMonitor::RemovePowerStateObserver(&observer1);
-  PowerMonitor::RemovePowerStateObserver(&observer2);
+  power_monitor->RemovePowerStateObserver(&observer1);
+  power_monitor->RemovePowerStateObserver(&observer2);
 }
 
 }  // namespace test
diff --git a/base/power_monitor/speed_limit_observer_win.cc b/base/power_monitor/speed_limit_observer_win.cc
index 20c1ea3..1ad2547b 100644
--- a/base/power_monitor/speed_limit_observer_win.cc
+++ b/base/power_monitor/speed_limit_observer_win.cc
@@ -91,7 +91,7 @@
   timer_.Stop();
 }
 
-int SpeedLimitObserverWin::GetCurrentSpeedLimit() {
+int SpeedLimitObserverWin::GetCurrentSpeedLimit() const {
   const int kSpeedLimitMax = PowerThermalObserver::kSpeedLimitMax;
 
   int idleness_percent = 0;
@@ -172,7 +172,7 @@
 #endif  // BUILDFLAG(ENABLE_BASE_TRACING)
 }
 
-float SpeedLimitObserverWin::EstimateThrottlingLevel() {
+float SpeedLimitObserverWin::EstimateThrottlingLevel() const {
   float throttling_level = 0.f;
 
   // Populate the PROCESSOR_POWER_INFORMATION structures for all logical CPUs
diff --git a/base/power_monitor/speed_limit_observer_win.h b/base/power_monitor/speed_limit_observer_win.h
index 8c06b8bc..ad22307 100644
--- a/base/power_monitor/speed_limit_observer_win.h
+++ b/base/power_monitor/speed_limit_observer_win.h
@@ -34,9 +34,9 @@
   ~SpeedLimitObserverWin();
 
  private:
-  int GetCurrentSpeedLimit();
+  int GetCurrentSpeedLimit() const;
   void OnTimerTick();
-  float EstimateThrottlingLevel();
+  float EstimateThrottlingLevel() const;
 
   size_t num_cpus() const { return num_cpus_; }
 
@@ -54,7 +54,7 @@
   // sample rate is one sample per seconds but the existing choice is rather
   // ad-hoc and not based on any deeper analysis into exact frequency
   // characteristics of the underlying process.
-  MovingAverage<int, int64_t> moving_average_;
+  mutable MovingAverage<int, int64_t> moving_average_;
   // Max speed-limit value is 100 (%) and it is also used in cases where the
   // native Windows API(s) fail.
   int speed_limit_ = PowerThermalObserver::kSpeedLimitMax;
diff --git a/base/power_monitor/thermal_state_observer_mac.h b/base/power_monitor/thermal_state_observer_mac.h
index a6710e0b..0be65556 100644
--- a/base/power_monitor/thermal_state_observer_mac.h
+++ b/base/power_monitor/thermal_state_observer_mac.h
@@ -37,7 +37,7 @@
   ~ThermalStateObserverMac();
 
   PowerThermalObserver::DeviceThermalState GetCurrentThermalState();
-  int GetCurrentSpeedLimit();
+  int GetCurrentSpeedLimit() const;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(ThermalStateObserverMacTest, StateChange);
diff --git a/base/power_monitor/thermal_state_observer_mac.mm b/base/power_monitor/thermal_state_observer_mac.mm
index ea51a1ca..76d7bac 100644
--- a/base/power_monitor/thermal_state_observer_mac.mm
+++ b/base/power_monitor/thermal_state_observer_mac.mm
@@ -109,7 +109,7 @@
   return NSProcessInfoThermalStateToDeviceThermalState(nsinfo_state);
 }
 
-int ThermalStateObserverMac::GetCurrentSpeedLimit() {
+int ThermalStateObserverMac::GetCurrentSpeedLimit() const {
   apple::ScopedCFTypeRef<CFDictionaryRef> dictionary;
   IOReturn result = IOPMCopyCPUPowerStatus(dictionary.InitializeInto());
   if (result != kIOReturnSuccess) {
diff --git a/base/scoped_multi_source_observation.h b/base/scoped_multi_source_observation.h
index 5614fea1..c9a1cff 100644
--- a/base/scoped_multi_source_observation.h
+++ b/base/scoped_multi_source_observation.h
@@ -91,6 +91,10 @@
   // Returns the number of sources being observed.
   size_t GetSourcesCount() const { return sources_.size(); }
 
+  // Returns a pointer to the observer that observes the sources.
+  Observer* observer() { return observer_; }
+  const Observer* observer() const { return observer_; }
+
   // Returns the sources being observed. Note: It is invalid to add or remove
   // sources while iterating on it.
   const std::vector<raw_ptr<Source>>& sources() const { return sources_; }
diff --git a/base/scoped_observation.h b/base/scoped_observation.h
index 77eb671..9063329 100644
--- a/base/scoped_observation.h
+++ b/base/scoped_observation.h
@@ -125,6 +125,10 @@
     return source_ == source;
   }
 
+  // Gets a pointer to the observer that observes the source.
+  Observer* GetObserver() { return observer_; }
+  const Observer* GetObserver() const { return observer_; }
+
   // Gets a pointer to the observed source, or nullptr if no source is being
   // observed.
   Source* GetSource() { return source_; }
diff --git a/base/task/sequence_manager/thread_controller_power_monitor.cc b/base/task/sequence_manager/thread_controller_power_monitor.cc
index ea2778d2..b98754173 100644
--- a/base/task/sequence_manager/thread_controller_power_monitor.cc
+++ b/base/task/sequence_manager/thread_controller_power_monitor.cc
@@ -28,18 +28,19 @@
 ThreadControllerPowerMonitor::ThreadControllerPowerMonitor() = default;
 
 ThreadControllerPowerMonitor::~ThreadControllerPowerMonitor() {
-  PowerMonitor::RemovePowerSuspendObserver(this);
+  PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 }
 
 void ThreadControllerPowerMonitor::BindToCurrentThread() {
   // Occasionally registration happens twice (i.e. when the
   // ThreadController::SetDefaultTaskRunner() re-initializes the
   // ThreadController).
+  auto* power_monitor = PowerMonitor::GetInstance();
   if (is_observer_registered_)
-    PowerMonitor::RemovePowerSuspendObserver(this);
+    power_monitor->RemovePowerSuspendObserver(this);
 
   // Register the observer to deliver notifications on the current thread.
-  PowerMonitor::AddPowerSuspendObserver(this);
+  power_monitor->AddPowerSuspendObserver(this);
   is_observer_registered_ = true;
 }
 
diff --git a/base/test/power_monitor_test.cc b/base/test/power_monitor_test.cc
index be3f7d2a..467d91a1 100644
--- a/base/test/power_monitor_test.cc
+++ b/base/test/power_monitor_test.cc
@@ -19,9 +19,10 @@
   ~PowerMonitorTestSource() override = default;
 
   // Retrieve current states.
-  PowerThermalObserver::DeviceThermalState GetCurrentThermalState() override;
-  PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus();
-  bool IsOnBatteryPower() override;
+  PowerThermalObserver::DeviceThermalState GetCurrentThermalState()
+      const override;
+  PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus() const;
+  bool IsOnBatteryPower() const override;
 
   // Sends asynchronous notifications to registered observers.
   void Suspend();
@@ -51,7 +52,7 @@
 };
 
 PowerThermalObserver::DeviceThermalState
-PowerMonitorTestSource::GetCurrentThermalState() {
+PowerMonitorTestSource::GetCurrentThermalState() const {
   return current_thermal_state_;
 }
 
@@ -98,11 +99,11 @@
 }
 
 PowerStateObserver::BatteryPowerStatus
-PowerMonitorTestSource::GetBatteryPowerStatus() {
+PowerMonitorTestSource::GetBatteryPowerStatus() const {
   return test_power_status_;
 }
 
-bool PowerMonitorTestSource::IsOnBatteryPower() {
+bool PowerMonitorTestSource::IsOnBatteryPower() const {
   return test_power_status_ ==
          PowerStateObserver::BatteryPowerStatus::kBatteryPower;
 }
@@ -123,24 +124,25 @@
 ScopedPowerMonitorTestSource::ScopedPowerMonitorTestSource() {
   auto power_monitor_test_source = std::make_unique<PowerMonitorTestSource>();
   power_monitor_test_source_ = power_monitor_test_source.get();
-  base::PowerMonitor::Initialize(std::move(power_monitor_test_source));
+  base::PowerMonitor::GetInstance()->Initialize(
+      std::move(power_monitor_test_source));
 }
 
 ScopedPowerMonitorTestSource::~ScopedPowerMonitorTestSource() {
-  base::PowerMonitor::ShutdownForTesting();
+  base::PowerMonitor::GetInstance()->ShutdownForTesting();
 }
 
 PowerThermalObserver::DeviceThermalState
-ScopedPowerMonitorTestSource::GetCurrentThermalState() {
+ScopedPowerMonitorTestSource::GetCurrentThermalState() const {
   return power_monitor_test_source_->GetCurrentThermalState();
 }
 
-bool ScopedPowerMonitorTestSource::IsOnBatteryPower() {
+bool ScopedPowerMonitorTestSource::IsOnBatteryPower() const {
   return power_monitor_test_source_->IsOnBatteryPower();
 }
 
 PowerStateObserver::BatteryPowerStatus
-ScopedPowerMonitorTestSource::GetBatteryPowerStatus() {
+ScopedPowerMonitorTestSource::GetBatteryPowerStatus() const {
   return power_monitor_test_source_->GetBatteryPowerStatus();
 }
 
diff --git a/base/test/power_monitor_test.h b/base/test/power_monitor_test.h
index 32e92c6..338c616f 100644
--- a/base/test/power_monitor_test.h
+++ b/base/test/power_monitor_test.h
@@ -37,9 +37,9 @@
       delete;
 
   // Retrieve current states.
-  PowerThermalObserver::DeviceThermalState GetCurrentThermalState();
-  bool IsOnBatteryPower();
-  PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus();
+  PowerThermalObserver::DeviceThermalState GetCurrentThermalState() const;
+  bool IsOnBatteryPower() const;
+  PowerStateObserver::BatteryPowerStatus GetBatteryPowerStatus() const;
 
   // Sends asynchronous notifications to registered observers.
   void Suspend();
diff --git a/base/threading/hang_watcher.cc b/base/threading/hang_watcher.cc
index 80a3c57..35ddd39a 100644
--- a/base/threading/hang_watcher.cc
+++ b/base/threading/hang_watcher.cc
@@ -561,7 +561,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(hang_watcher_thread_checker_);
 
   const TimeTicks last_system_power_resume_time =
-      PowerMonitor::GetLastSystemResumeTime();
+      PowerMonitor::GetInstance()->GetLastSystemResumeTime();
   if (last_system_power_resume_time.is_null())
     return "Never suspended";
   if (last_system_power_resume_time == TimeTicks::Max())
diff --git a/base/timer/hi_res_timer_manager_win.cc b/base/timer/hi_res_timer_manager_win.cc
index 12f4badf..4483638a 100644
--- a/base/timer/hi_res_timer_manager_win.cc
+++ b/base/timer/hi_res_timer_manager_win.cc
@@ -40,10 +40,11 @@
   // we won't receive power state change callbacks and
   // hi_res_clock_available_ will remain at its initial value.
   if (HighResolutionTimerAllowed()) {
-    DCHECK(PowerMonitor::IsInitialized());
-    PowerMonitor::AddPowerSuspendObserver(this);
+    auto* power_monitor = base::PowerMonitor::GetInstance();
+    DCHECK(power_monitor->IsInitialized());
+    power_monitor->AddPowerSuspendObserver(this);
     const bool on_battery =
-        PowerMonitor::AddPowerStateObserverAndReturnOnBatteryState(this);
+        power_monitor->AddPowerStateObserverAndReturnOnBatteryState(this);
     UseHiResClock(!on_battery);
 
     // Start polling the high resolution timer usage.
@@ -55,8 +56,9 @@
 
 HighResolutionTimerManager::~HighResolutionTimerManager() {
   if (HighResolutionTimerAllowed()) {
-    PowerMonitor::RemovePowerSuspendObserver(this);
-    PowerMonitor::RemovePowerStateObserver(this);
+    auto* power_monitor = base::PowerMonitor::GetInstance();
+    power_monitor->RemovePowerSuspendObserver(this);
+    power_monitor->RemovePowerStateObserver(this);
     UseHiResClock(false);
   }
 }
diff --git a/base/timer/wall_clock_timer.cc b/base/timer/wall_clock_timer.cc
index 8e6814a..90847b8 100644
--- a/base/timer/wall_clock_timer.cc
+++ b/base/timer/wall_clock_timer.cc
@@ -51,14 +51,14 @@
 
 void WallClockTimer::AddObserver() {
   if (!observer_added_) {
-    PowerMonitor::AddPowerSuspendObserver(this);
+    PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
     observer_added_ = true;
   }
 }
 
 void WallClockTimer::RemoveObserver() {
   if (observer_added_) {
-    PowerMonitor::RemovePowerSuspendObserver(this);
+    PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
     observer_added_ = false;
   }
 }
diff --git a/base/tracing/protos/chrome_track_event.proto b/base/tracing/protos/chrome_track_event.proto
index 50e2b2d..9b307ac8 100644
--- a/base/tracing/protos/chrome_track_event.proto
+++ b/base/tracing/protos/chrome_track_event.proto
@@ -1397,7 +1397,7 @@
     STEP_RECEIVE_COMPOSITOR_FRAME = 5;
     STEP_RECEIVE_BEGIN_FRAME = 6;
     STEP_RECEIVE_BEGIN_FRAME_DISCARD = 7;
-    STEP_SEND_BEGIN_MAIN_FRAME = 8;
+    STEP_SEND_BEGIN_MAIN_FRAME = 8 [deprecated = true];
     STEP_SUBMIT_COMPOSITOR_FRAME = 9;
     STEP_SURFACE_AGGREGATION = 10;
     STEP_SEND_BUFFER_SWAP = 11;
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 21bd25a..85dec342 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -3174,7 +3174,7 @@
       }
 
       if (invoker.use_turbine) {
-        _turbine_jar_path = "//third_party/turbine/turbine.jar"
+        _turbine_jar_path = "//third_party/turbine/cipd/turbine.jar"
         inputs += [ _turbine_jar_path ]
         outputs += [ invoker.generated_jar_path ]
         args += [
diff --git a/build/config/fuchsia/BUILD.gn b/build/config/fuchsia/BUILD.gn
index 3da287e..21e9b21 100644
--- a/build/config/fuchsia/BUILD.gn
+++ b/build/config/fuchsia/BUILD.gn
@@ -30,49 +30,21 @@
     "//build/util/lib/",
     "//third_party/fuchsia-sdk/sdk/.build-id/",
     "//third_party/fuchsia-sdk/sdk/meta/manifest.json",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/ffx",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/ffx-meta.json",
-
-    # The following folder contains standalone binaries running as ffx plugins.
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/ffx_tools/",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/fvm",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/fvm-meta.json",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/merkleroot",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/merkleroot-meta.json",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/pm",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/pm-meta.json",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/symbolizer",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/symbolizer-meta.json",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/zbi",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/zbi-meta.json",
+    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/",
   ]
 
   if (fuchsia_additional_boot_images == []) {
     data += [ "${boot_image_root}" ]
-  }
-
-  foreach(fuchsia_additional_boot_image, fuchsia_additional_boot_images) {
-    data += [ "${fuchsia_additional_boot_image}/" ]
-  }
-
-  if (test_isolate_uses_emulator) {
-    if (test_host_cpu == "x64") {
-      data += [
-        "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/aemu_internal",
-        "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/aemu_internal-meta.json",
-        "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/qemu_internal",
-        "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/qemu_internal-meta.json",
-      ]
-    } else if (test_host_cpu == "arm64") {
-      data += [
-        "//third_party/qemu-${host_os}-${test_host_cpu}/",
-
-        # TODO(crbug.com/42050489): remove when ffx has native support
-        # for starting emulator on arm64 host.
-        "//third_party/fuchsia-sdk/sdk/tools/x64/qemu_internal-meta.json",
-      ]
+  } else {
+    # We never need to use both qemu images and device images.
+    foreach(fuchsia_additional_boot_image, fuchsia_additional_boot_images) {
+      data += [ "${fuchsia_additional_boot_image}/" ]
     }
   }
+
+  if (test_isolate_uses_emulator && test_host_cpu == "arm64") {
+    data += [ "//third_party/qemu-${host_os}-${test_host_cpu}/" ]
+  }
 }
 
 # Copy the loader to place it at the expected path in the final package.
diff --git a/build/config/siso/reproxy.star b/build/config/siso/reproxy.star
index df6934c..16f229a 100644
--- a/build/config/siso/reproxy.star
+++ b/build/config/siso/reproxy.star
@@ -65,6 +65,13 @@
             "exec_timeout": "4m",
             "reclient_timeout": "8m",
         })
+    if runtime.os == "darwin":
+        # Mac gets timeouts occasionally on large input uploads (likely due to large invalidations)
+        # b/356981080
+        rw_opts.update({
+            "exec_timeout": "3m",
+            "reclient_timeout": "6m",
+        })
 
     # Command line options are the highest priority.
     rw_opts.update(rw_cmd_opts)
diff --git a/build/whitespace_file.txt b/build/whitespace_file.txt
index 9a34ff2..6764012 100644
--- a/build/whitespace_file.txt
+++ b/build/whitespace_file.txt
@@ -226,3 +226,5 @@
 while the later Toki Pona Dictionary (referred to as ku) lists 137 "essential" words and a
 number of less-used ones. Its words are easy to pronounce across language backgrounds,
 which allows it to serve as a bridge of sorts for people of different cultures.
+
+New :)
\ No newline at end of file
diff --git a/cc/input/browser_controls_offset_manager.cc b/cc/input/browser_controls_offset_manager.cc
index 67ec77de..59fbe05e 100644
--- a/cc/input/browser_controls_offset_manager.cc
+++ b/cc/input/browser_controls_offset_manager.cc
@@ -588,6 +588,18 @@
     // Ticking the animation might reset it if it's at the final value.
     bottom_min_height_change_in_progress_ =
         bottom_controls_animation_.IsInitialized();
+
+    // When shrinking the bottom controls, there may be a remaining offset
+    // mistake if the min height was decreased by more than the height was. This
+    // can be fixed by simply "resetting" the offset to the final minHeight
+    // value at the end of the animation. This only applies to shrinking
+    // animations, since this adjustment happens at the beginning for growing
+    // animations. This is done to avoid the bottom controls "lagging behind"
+    // the changes to the web content and exposing a blank space right above the
+    // bottom controls.
+    if (!bottom_min_height_change_in_progress_) {
+      bottom_controls_min_height_offset_ = BottomControlsMinHeight();
+    }
   }
 
   gfx::Vector2dF scroll_delta(0.f, top_offset_delta);
diff --git a/cc/input/browser_controls_offset_manager_unittest.cc b/cc/input/browser_controls_offset_manager_unittest.cc
index 6771347f..d0d68150 100644
--- a/cc/input/browser_controls_offset_manager_unittest.cc
+++ b/cc/input/browser_controls_offset_manager_unittest.cc
@@ -1428,5 +1428,63 @@
   EXPECT_FLOAT_EQ(1.f, manager->BottomControlsShownRatio());
 }
 
+TEST(BrowserControlsOffsetManagerTest, MinHeightDecreasedByMoreThanHeight) {
+  MockBrowserControlsOffsetManagerClient client(150.f, 0.5f, 0.5f);
+  BrowserControlsOffsetManager* manager = client.manager();
+
+  // Set a starting height without animation.
+  client.SetBrowserControlsParams(
+      {/* top_controls_height */ 80,
+       /* top_controls_min_height */ 0,
+       /* bottom_controls_height */ 200,
+       /* bottom_controls_min_height */ 200,
+       /* animate_browser_controls_height_changes */ false,
+       /* browser_controls_shrink_blink_size */ false});
+  EXPECT_FLOAT_EQ(80.f, manager->TopControlsHeight());
+  EXPECT_FLOAT_EQ(1.f, manager->TopControlsShownRatio());
+  EXPECT_FLOAT_EQ(200.f, manager->BottomControlsHeight());
+  EXPECT_FLOAT_EQ(200.f, manager->BottomControlsMinHeight());
+  EXPECT_FLOAT_EQ(200.f, manager->BottomControlsMinHeightOffset());
+  EXPECT_FLOAT_EQ(1.f, manager->BottomControlsShownRatio());
+  EXPECT_FALSE(manager->HasAnimation());
+
+  client.SetBrowserControlsParams(
+      {/* top_controls_height */ 80,
+       /* top_controls_min_height */ 0,
+       /* bottom_controls_height */ 50,
+       /* bottom_controls_min_height */ 0,
+       /* animate_browser_controls_height_changes */ true,
+       /* browser_controls_shrink_blink_size */ false});
+  EXPECT_FLOAT_EQ(80.f, manager->TopControlsHeight());
+  EXPECT_FLOAT_EQ(1.f, manager->TopControlsShownRatio());
+  EXPECT_FLOAT_EQ(50.f, manager->BottomControlsHeight());
+  EXPECT_FLOAT_EQ(0.f, manager->BottomControlsMinHeight());
+  // The min height offset should still be at the full value, as the animation
+  // hasn't yet started.
+  EXPECT_FLOAT_EQ(200.f, manager->BottomControlsMinHeightOffset());
+  // With the animation, the shown ratio won't change just yet. With a
+  // transition from 200 -> 50, 400% is currently showing.
+  EXPECT_FLOAT_EQ(4.0f, manager->BottomControlsShownRatio());
+  EXPECT_TRUE(manager->HasAnimation());
+
+  base::TimeTicks time = base::TimeTicks::Now();
+  // First animate will establish the animation.
+  float previous = manager->BottomControlsShownRatio();
+  manager->Animate(time);
+  EXPECT_EQ(manager->BottomControlsShownRatio(), previous);
+
+  while (manager->HasAnimation()) {
+    previous = manager->BottomControlsShownRatio();
+    time = base::Microseconds(100) + time;
+    manager->Animate(time);
+    EXPECT_LT(manager->BottomControlsShownRatio(), previous);
+  }
+  EXPECT_FALSE(manager->HasAnimation());
+  // Controls should be fully shown when the animation ends.
+  EXPECT_FLOAT_EQ(1.f, manager->BottomControlsShownRatio());
+  // Ensure that the min height offset has fully shrunk.
+  EXPECT_FLOAT_EQ(0.f, manager->BottomControlsMinHeightOffset());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 5e0a44d..e149469 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -61,7 +61,7 @@
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/include/core/SkTypeface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/size.h"
diff --git a/cc/paint/image_transfer_cache_entry.cc b/cc/paint/image_transfer_cache_entry.cc
index ef27cdc9..ae2b837 100644
--- a/cc/paint/image_transfer_cache_entry.cc
+++ b/cc/paint/image_transfer_cache_entry.cc
@@ -26,9 +26,9 @@
 #include "third_party/skia/include/core/SkPixmap.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrYUVABackendTextures.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrYUVABackendTextures.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/graphite/Image.h"
 #include "third_party/skia/include/gpu/graphite/Recorder.h"
diff --git a/cc/paint/image_transfer_cache_entry.h b/cc/paint/image_transfer_cache_entry.h
index 1358fec..f01ffc71 100644
--- a/cc/paint/image_transfer_cache_entry.h
+++ b/cc/paint/image_transfer_cache_entry.h
@@ -18,7 +18,7 @@
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/private/SkGainmapInfo.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/hdr_metadata.h"
diff --git a/cc/paint/image_transfer_cache_entry_unittest.cc b/cc/paint/image_transfer_cache_entry_unittest.cc
index 7c5ffbeb..a0d9c2f 100644
--- a/cc/paint/image_transfer_cache_entry_unittest.cc
+++ b/cc/paint/image_transfer_cache_entry_unittest.cc
@@ -34,14 +34,14 @@
 #include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index 901e5c1..2715aa47 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -60,8 +60,8 @@
 #include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/graphite/Context.h"
 #include "third_party/skia/include/gpu/graphite/GraphiteTypes.h"
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index 0151915d..9ae3db31 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -25,7 +25,7 @@
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSize.h"
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 
 namespace cc {
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index ee3fbed..593d5cb 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -24,7 +24,7 @@
 #include "cc/paint/paint_record.h"
 #include "cc/paint/scoped_raster_flags.h"
 #include "cc/paint/skottie_serialization_history.h"
-#include "third_party/skia/include/gpu/GrRecordingContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrRecordingContext.h"
 #include "third_party/skia/include/gpu/graphite/Recorder.h"
 
 namespace cc {
diff --git a/cc/paint/paint_op_raster_fuzzer.cc b/cc/paint/paint_op_raster_fuzzer.cc
index d1fc9fa..def3b5c2 100644
--- a/cc/paint/paint_op_raster_fuzzer.cc
+++ b/cc/paint/paint_op_raster_fuzzer.cc
@@ -26,7 +26,7 @@
 #include "gpu/command_buffer/service/service_font_manager.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 
 struct Environment {
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc
index e5b084c5..469ecec 100644
--- a/cc/paint/skia_paint_canvas.cc
+++ b/cc/paint/skia_paint_canvas.cc
@@ -29,8 +29,8 @@
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/include/docs/SkPDFDocument.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrRecordingContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrRecordingContext.h"
 #include "third_party/skia/src/core/SkCanvasPriv.h"
 
 namespace cc {
diff --git a/cc/paint/transfer_cache_fuzzer.cc b/cc/paint/transfer_cache_fuzzer.cc
index 3bf5700..8f34017 100644
--- a/cc/paint/transfer_cache_fuzzer.cc
+++ b/cc/paint/transfer_cache_fuzzer.cc
@@ -15,7 +15,7 @@
 #include "cc/test/transfer_cache_test_helper.h"
 #include "components/viz/test/test_context_provider.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 struct Environment {
   Environment() { logging::SetMinLogLevel(logging::LOGGING_FATAL); }
diff --git a/cc/raster/playback_image_provider_unittest.cc b/cc/raster/playback_image_provider_unittest.cc
index 0fb3eeb..82086aeac 100644
--- a/cc/raster/playback_image_provider_unittest.cc
+++ b/cc/raster/playback_image_provider_unittest.cc
@@ -17,8 +17,8 @@
 #include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSize.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 
 namespace cc {
 namespace {
diff --git a/cc/raster/raster_buffer_provider_perftest.cc b/cc/raster/raster_buffer_provider_perftest.cc
index 20b5ff1..c2966f3e 100644
--- a/cc/raster/raster_buffer_provider_perftest.cc
+++ b/cc/raster/raster_buffer_provider_perftest.cc
@@ -7,6 +7,8 @@
 #pragma allow_unsafe_buffers
 #endif
 
+#include "cc/raster/raster_buffer_provider.h"
+
 #include <stddef.h>
 #include <stdint.h>
 
@@ -19,7 +21,6 @@
 #include "cc/raster/bitmap_raster_buffer_provider.h"
 #include "cc/raster/gpu_raster_buffer_provider.h"
 #include "cc/raster/one_copy_raster_buffer_provider.h"
-#include "cc/raster/raster_buffer_provider.h"
 #include "cc/raster/raster_query_queue.h"
 #include "cc/raster/synchronous_task_graph_runner.h"
 #include "cc/raster/zero_copy_raster_buffer_provider.h"
@@ -40,7 +41,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_result_reporter.h"
 #include "third_party/khronos/GLES2/gl2.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 namespace cc {
 namespace {
diff --git a/cc/resources/ui_resource_bitmap.cc b/cc/resources/ui_resource_bitmap.cc
index 7451e50..09ebfa3 100644
--- a/cc/resources/ui_resource_bitmap.cc
+++ b/cc/resources/ui_resource_bitmap.cc
@@ -18,8 +18,8 @@
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkMallocPixelRef.h"
 #include "third_party/skia/include/core/SkPixelRef.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrRecordingContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrRecordingContext.h"
 
 namespace cc {
 namespace {
diff --git a/cc/slim/frame_sink_impl.cc b/cc/slim/frame_sink_impl.cc
index c918b0b5..33743ba0 100644
--- a/cc/slim/frame_sink_impl.cc
+++ b/cc/slim/frame_sink_impl.cc
@@ -35,7 +35,7 @@
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "third_party/skia/include/core/SkAlphaType.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/color_space.h"
 
diff --git a/cc/test/skia_common.cc b/cc/test/skia_common.cc
index 8123eac..100451d 100644
--- a/cc/test/skia_common.cc
+++ b/cc/test/skia_common.cc
@@ -33,7 +33,7 @@
 #include "third_party/skia/include/core/SkPixmap.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/skia_conversions.h"
diff --git a/cc/test/test_skcanvas.cc b/cc/test/test_skcanvas.cc
index 9ac5222..44021ea0 100644
--- a/cc/test/test_skcanvas.cc
+++ b/cc/test/test_skcanvas.cc
@@ -4,7 +4,7 @@
 
 #include "cc/test/test_skcanvas.h"
 
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 
 namespace cc {
 SaveCountingCanvas::SaveCountingCanvas() : SkNoDrawCanvas(100, 100) {}
diff --git a/cc/test/test_skcanvas.h b/cc/test/test_skcanvas.h
index 5d2228b2..1454a1ba 100644
--- a/cc/test/test_skcanvas.h
+++ b/cc/test/test_skcanvas.h
@@ -8,7 +8,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkPath.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/utils/SkNoDrawCanvas.h"
 
 namespace cc {
diff --git a/cc/test/transfer_cache_test_helper.h b/cc/test/transfer_cache_test_helper.h
index 2fd692f..e819dfb 100644
--- a/cc/test/transfer_cache_test_helper.h
+++ b/cc/test/transfer_cache_test_helper.h
@@ -15,7 +15,7 @@
 #include "base/memory/raw_ptr.h"
 #include "cc/paint/transfer_cache_deserialize_helper.h"
 #include "cc/paint/transfer_cache_serialize_helper.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 namespace cc {
 
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 404faa20..b29d7d1 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -67,12 +67,12 @@
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrYUVABackendTextures.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrYUVABackendTextures.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/skia_conversions.h"
diff --git a/cc/tiles/gpu_image_decode_cache.h b/cc/tiles/gpu_image_decode_cache.h
index 7c8b5f7..db83e96f 100644
--- a/cc/tiles/gpu_image_decode_cache.h
+++ b/cc/tiles/gpu_image_decode_cache.h
@@ -37,7 +37,7 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 
 namespace viz {
 class RasterContextProvider;
diff --git a/cc/tiles/gpu_image_decode_cache_perftest.cc b/cc/tiles/gpu_image_decode_cache_perftest.cc
index fba8345..5c57d46 100644
--- a/cc/tiles/gpu_image_decode_cache_perftest.cc
+++ b/cc/tiles/gpu_image_decode_cache_perftest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "cc/tiles/gpu_image_decode_cache.h"
+
 #include <memory>
 #include <vector>
 
@@ -9,7 +11,6 @@
 #include "cc/paint/draw_image.h"
 #include "cc/paint/paint_image_builder.h"
 #include "cc/raster/tile_task.h"
-#include "cc/tiles/gpu_image_decode_cache.h"
 #include "components/viz/test/test_in_process_context_provider.h"
 #include "gpu/command_buffer/client/raster_interface.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,7 +23,7 @@
 #include "third_party/skia/include/core/SkSize.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 
 namespace cc {
diff --git a/cc/tiles/gpu_image_decode_cache_unittest.cc b/cc/tiles/gpu_image_decode_cache_unittest.cc
index f44137b..2d5ae50 100644
--- a/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -58,8 +58,8 @@
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
 #include "third_party/skia/include/effects/SkHighContrastFilter.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 
 using testing::_;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index a6e4e06..5d4ef1a2 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -140,7 +140,7 @@
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.pbzero.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/display_color_spaces.h"
 #include "ui/gfx/geometry/point_conversions.h"
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index d741063..30d157d 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -95,7 +95,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkPicture.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gfx/animation/keyframe/timing_function.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index 6ce1d2d..95d99a2 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -740,15 +740,6 @@
       (args.frame_id.sequence_number & 0xffffffff);
   host_impl_->WillSendBeginMainFrame();
   {
-    TRACE_EVENT(
-        "viz,benchmark,graphics.pipeline", "Graphics.Pipeline",
-        perfetto::Flow::Global(args.trace_id), [&](perfetto::EventContext ctx) {
-          auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
-          auto* data = event->set_chrome_graphics_pipeline();
-          data->set_step(perfetto::protos::pbzero::ChromeGraphicsPipeline::
-                             StepName::STEP_SEND_BEGIN_MAIN_FRAME);
-          data->set_display_trace_id(args.trace_id);
-        });
     TRACE_EVENT_WITH_FLOW0("viz,benchmark",
                            "MainFrame.SendBeginMainFrameOnImpl",
                            TRACE_ID_LOCAL(begin_main_frame_state->trace_id),
diff --git a/chrome/VERSION b/chrome/VERSION
index 97a2caed..0e6823f0 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=130
 MINOR=0
-BUILD=6697
+BUILD=6698
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 8f560d9..c0076b8 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -3739,6 +3739,7 @@
     "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java",
     "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java",
     "java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java",
+    "java/src/org/chromium/chrome/browser/customtabs/CustomTabAuthUrlHeuristics.java",
     "java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java",
     "java/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorder.java",
     "java/src/org/chromium/chrome/browser/customtabs/features/TabInteractionRecorder.java",
diff --git a/chrome/android/features/tab_ui/java/res/layout/bottom_tab_strip_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_strip_toolbar.xml
index c86240c..ec88d623 100644
--- a/chrome/android/features/tab_ui/java/res/layout/bottom_tab_strip_toolbar.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_strip_toolbar.xml
@@ -11,7 +11,6 @@
     android:id="@+id/tab_group_ui_toolbar_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    xmlns:tools="http://schemas.android.com/tools"
     android:background="?attr/colorSurface">
     <FrameLayout
         android:layout_width="match_parent"
@@ -27,7 +26,7 @@
                 style="@style/BottomToolbarButton"
                 android:src="@drawable/ic_expand_less_black_24dp"
                 app:tint="@color/default_icon_color_tint_list"
-                tools:ignore="ContentDescription" />
+                android:contentDescription="@string/accessibility_bottom_tab_strip_expand_tab_sheet" />
             <FrameLayout
                 android:id="@+id/toolbar_container_view"
                 android:layout_width="0dp"
@@ -46,7 +45,7 @@
             android:layout_width="@dimen/tab_strip_fading_edge_width"
             android:src="@drawable/tab_strip_fading_edge_start"
             android:layout_marginStart="@dimen/tab_strip_button_width"
-            tools:ignore="contentDescription"/>
+            android:importantForAccessibility="no" />
         <org.chromium.ui.widget.ChromeImageView
             android:id="@+id/tab_strip_fading_edge_end"
             android:layout_height="match_parent"
@@ -54,6 +53,6 @@
             android:src="@drawable/tab_strip_fading_edge_end"
             android:layout_marginEnd="@dimen/tab_strip_button_width"
             android:layout_gravity="bottom|end"
-            tools:ignore="contentDescription"/>
+            android:importantForAccessibility="no" />
     </FrameLayout>
 </org.chromium.chrome.browser.tasks.tab_management.TabGroupUiToolbarView>
diff --git a/chrome/android/features/tab_ui/java/res/layout/dynamic_bottom_tab_strip_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/dynamic_bottom_tab_strip_toolbar.xml
index 4283aef..d7efcd775 100644
--- a/chrome/android/features/tab_ui/java/res/layout/dynamic_bottom_tab_strip_toolbar.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/dynamic_bottom_tab_strip_toolbar.xml
@@ -11,7 +11,6 @@
     android:id="@+id/tab_group_ui_toolbar_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    xmlns:tools="http://schemas.android.com/tools"
     android:background="?attr/colorSurface">
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/main_content"
@@ -71,14 +70,21 @@
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintVertical_bias="0.5"
             app:layout_constraintStart_toEndOf="@+id/toolbar_container_view"
-            app:layout_constraintEnd_toStartOf="@+id/toolbar_show_group_dialog_button"
+            app:layout_constraintEnd_toStartOf="@+id/toolbar_group_button_barrier"
             android:contentDescription="@string/bottom_tab_grid_new_tab"
             app:tint="@color/default_icon_color_tint_list" />
 
         <!--
-          TODO(crbug.com/362280397): add a guideline and make in the "leftmost"
-          of the following view and the container for the avatars.
+          Only one of referenced IDs will be visible at a time. Use a barrier to
+          make constraints work on the new tab button.
         -->
+        <androidx.constraintlayout.widget.Barrier
+            android:id="@+id/toolbar_group_button_barrier"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:barrierDirection="start"
+            app:constraint_referenced_ids="toolbar_show_group_dialog_button,toolbar_image_tiles_container" />
+
         <org.chromium.ui.widget.ChromeImageView
             android:id="@+id/toolbar_show_group_dialog_button"
             style="@style/BottomToolbarButton"
@@ -88,6 +94,17 @@
             app:layout_constraintVertical_bias="0.5"
             app:layout_constraintEnd_toEndOf="parent"
             app:tint="@color/default_icon_color_tint_list"
-            tools:ignore="ContentDescription" />
+            android:contentDescription="@string/accessibility_bottom_tab_strip_expand_tab_sheet" />
+
+        <FrameLayout
+            android:id="@+id/toolbar_image_tiles_container"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:minWidth="@dimen/min_touch_target_size"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.5"
+            app:layout_constraintEnd_toEndOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
 </org.chromium.chrome.browser.tasks.tab_management.TabGroupUiToolbarView>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageService.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageService.java
index a7afade5..e501da2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageService.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageService.java
@@ -212,6 +212,10 @@
 
     @Override
     public View getCustomView() {
+        // Before returning the view remove it from its parent. This is safe because this will only
+        // be called when freshly binding to a new custom message card and there will only ever be
+        // one message card around. Do this when building the message card each time.
+        removeCustomCardViewFromParent();
         return mCustomCardView;
     }
 
@@ -256,16 +260,6 @@
     }
 
     @Override
-    public void onRemoveAllAppendedMessage() {
-        if (mCustomCardView == null) return;
-        // When messages are removed, detach the custom view.
-        ViewParent parent = mCustomCardView.getParent();
-        if (parent != null) {
-            ((ViewGroup) parent).removeView(mCustomCardView);
-        }
-    }
-
-    @Override
     public void addObserver(MessageService.MessageObserver obs) {
         super.addObserver(obs);
         maybeSendMessageToQueue();
@@ -345,4 +339,13 @@
     Callback<Integer> getTabCountObserverForTesting() {
         return mTabCountObserver;
     }
+
+    private void removeCustomCardViewFromParent() {
+        if (mCustomCardView == null) return;
+        // When messages are removed, detach the custom view.
+        ViewParent parent = mCustomCardView.getParent();
+        if (parent != null) {
+            ((ViewGroup) parent).removeView(mCustomCardView);
+        }
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index 31907ab2..15a06e1e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -7,7 +7,6 @@
 import android.app.Activity;
 import android.graphics.Rect;
 import android.util.Size;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -237,13 +236,8 @@
             if (isDataSharingAndroidEnabled) {
                 FrameLayout imageTilesContainer =
                         toolbarView.findViewById(R.id.image_tiles_container);
-                View imageTilesView = mSharedImageTilesCoordinator.getView();
-                var layoutParams =
-                        new FrameLayout.LayoutParams(
-                                FrameLayout.LayoutParams.WRAP_CONTENT,
-                                FrameLayout.LayoutParams.WRAP_CONTENT,
-                                Gravity.CENTER);
-                imageTilesContainer.addView(imageTilesView, layoutParams);
+                TabUiUtils.attachSharedImageTilesCoordinatorToFrameLayout(
+                        mSharedImageTilesCoordinator, imageTilesContainer);
             }
 
             mModelChangeProcessor =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index bfa9643..6d107f24 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -951,6 +951,7 @@
                 });
     }
 
+    // TODO(crbug.com/361139300): Dynamically drive updates by observing the backend.
     private void updateShareData() {
         boolean isIncognitoBranded = mCurrentTabModelFilterSupplier.get().isIncognitoBranded();
         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING)
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
index 4e9b44c7..04ac799 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -8,9 +8,11 @@
 import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
+import android.widget.FrameLayout;
 
 import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
 import org.chromium.base.TraceEvent;
@@ -20,7 +22,11 @@
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
+import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory;
 import org.chromium.chrome.browser.data_sharing.DataSharingTabManager;
+import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesColor;
+import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesCoordinator;
+import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesType;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -35,6 +41,7 @@
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
+import org.chromium.components.data_sharing.DataSharingService;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -99,6 +106,7 @@
             mScrimCoordinator = scrimCoordinator;
             mOmniboxFocusStateSupplier = omniboxFocusStateSupplier;
             mModel = new PropertyModel(TabGroupUiProperties.ALL_KEYS);
+
             @LayoutRes
             int layoutId =
                     ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING)
@@ -201,6 +209,23 @@
                 mTabGridDialogControllerSupplier = null;
             }
 
+            @Nullable SharedImageTilesCoordinator sharedImageTilesCoordinator = null;
+            if (ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING)) {
+                DataSharingService dataSharingService =
+                        DataSharingServiceFactory.getForProfile(
+                                mTabModelSelector.getModel(/* incognito= */ false).getProfile());
+                sharedImageTilesCoordinator =
+                        new SharedImageTilesCoordinator(
+                                activity,
+                                SharedImageTilesType.CLICKABLE,
+                                SharedImageTilesColor.DYNAMIC,
+                                dataSharingService);
+                FrameLayout container =
+                        mToolbarView.findViewById(R.id.toolbar_image_tiles_container);
+                TabUiUtils.attachSharedImageTilesCoordinatorToFrameLayout(
+                        sharedImageTilesCoordinator, container);
+            }
+
             mMediator =
                     new TabGroupUiMediator(
                             mActivity,
@@ -214,7 +239,8 @@
                             mLayoutStateProviderSupplier,
                             mIncognitoStateProvider,
                             mTabGridDialogControllerSupplier,
-                            mOmniboxFocusStateSupplier);
+                            mOmniboxFocusStateSupplier,
+                            sharedImageTilesCoordinator);
 
             TabGroupUtils.startObservingForCreationIPH();
         }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
index 4cb2e163..9c4e80d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
@@ -10,7 +10,6 @@
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
 import org.chromium.base.CallbackController;
@@ -20,13 +19,19 @@
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplier;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesCoordinator;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.layouts.LayoutType;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabSelectionType;
+import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures;
+import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.browser.tab_ui.TabContentManager;
 import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider;
 import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider.IncognitoStateObserver;
@@ -40,10 +45,10 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator.DialogController;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator.BottomControlsVisibilityController;
-import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.embedder_support.util.UrlConstants;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -88,6 +93,10 @@
     private final ObservableSupplier<Boolean> mOmniboxFocusStateSupplier;
     private final ObservableSupplierImpl<Boolean> mHandleBackPressChangedSupplier;
 
+    // These should only be used when regular (non-incognito) tabs are set in the model.
+    private final @Nullable SharedImageTilesCoordinator mSharedImageTilesCoordinator;
+    private final @Nullable TabGroupSyncService mTabGroupSyncService;
+
     private CallbackController mCallbackController = new CallbackController();
     private final LayoutStateObserver mLayoutStateObserver;
     private LayoutStateProvider mLayoutStateProvider;
@@ -114,42 +123,8 @@
             @Nullable
                     LazyOneshotSupplier<TabGridDialogMediator.DialogController>
                             dialogControllerSupplier,
-            ObservableSupplier<Boolean> omniboxFocusStateSupplier) {
-        this(
-                context,
-                visibilityController,
-                handleBackPressChangedSupplier,
-                resetHandler,
-                model,
-                tabModelSelector,
-                tabContentManager,
-                tabCreatorManager,
-                layoutStateProviderSupplier,
-                incognitoStateProvider,
-                dialogControllerSupplier,
-                omniboxFocusStateSupplier,
-                SemanticColorUtils.getDialogBgColor(context),
-                context.getColor(org.chromium.chrome.R.color.dialog_bg_color_dark_baseline));
-    }
-
-    @VisibleForTesting
-    TabGroupUiMediator(
-            Context context,
-            BottomControlsVisibilityController visibilityController,
-            ObservableSupplierImpl<Boolean> handleBackPressChangedSupplier,
-            ResetHandler resetHandler,
-            PropertyModel model,
-            TabModelSelector tabModelSelector,
-            TabContentManager tabContentManager,
-            TabCreatorManager tabCreatorManager,
-            OneshotSupplier<LayoutStateProvider> layoutStateProviderSupplier,
-            IncognitoStateProvider incognitoStateProvider,
-            @Nullable
-                    LazyOneshotSupplier<TabGridDialogMediator.DialogController>
-                            dialogControllerSupplier,
             ObservableSupplier<Boolean> omniboxFocusStateSupplier,
-            @ColorInt int primaryBackgroundColor,
-            @ColorInt int incognitoBackgroundColor) {
+            SharedImageTilesCoordinator sharedImageTilesCoordinator) {
         mContext = context;
         mResetHandler = resetHandler;
         mModel = model;
@@ -160,8 +135,16 @@
         mIncognitoStateProvider = incognitoStateProvider;
         mTabGridDialogControllerSupplier = dialogControllerSupplier;
         mOmniboxFocusStateSupplier = omniboxFocusStateSupplier;
-        mPrimaryBackgroundColor = primaryBackgroundColor;
-        mIncognitoBackgroundColor = incognitoBackgroundColor;
+        mSharedImageTilesCoordinator = sharedImageTilesCoordinator;
+        mPrimaryBackgroundColor = SemanticColorUtils.getDialogBgColor(context);
+        mIncognitoBackgroundColor = context.getColor(R.color.dialog_bg_color_dark_baseline);
+
+        Profile originalProfile = mTabModelSelector.getModel(/* incongito= */ false).getProfile();
+        if (TabGroupSyncFeatures.isTabGroupSyncEnabled(originalProfile)) {
+            mTabGroupSyncService = TabGroupSyncServiceFactory.getForProfile(originalProfile);
+        } else {
+            mTabGroupSyncService = null;
+        }
 
         var layoutStateProvider = layoutStateProviderSupplier.get();
         if (layoutStateProvider != null
@@ -379,7 +362,7 @@
     }
 
     private void setupToolbarButtons() {
-        View.OnClickListener showGroupDialogButtonOnClickListener =
+        View.OnClickListener showGroupDialogOnClickListener =
                 view -> {
                     // Don't handle taps until fully visible and done animating.
                     @Nullable DialogController controller = getTabGridDialogControllerIfExists();
@@ -397,8 +380,8 @@
                     RecordUserAction.record("TabGroup.ExpandedFromStrip.TabGridDialog");
                 };
         mModel.set(
-                TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER,
-                showGroupDialogButtonOnClickListener);
+                TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER,
+                showGroupDialogOnClickListener);
 
         View.OnClickListener newTabButtonOnClickListener =
                 view -> {
@@ -420,12 +403,6 @@
                 };
         mModel.set(
                 TabGroupUiProperties.NEW_TAB_BUTTON_ON_CLICK_LISTENER, newTabButtonOnClickListener);
-
-        String showGroupDialogButtonContentDescription =
-                mContext.getString(R.string.accessibility_bottom_tab_strip_expand_tab_sheet);
-        mModel.set(
-                TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_CONTENT_DESCRIPTION,
-                showGroupDialogButtonContentDescription);
     }
 
     /**
@@ -445,6 +422,7 @@
             id = Tab.INVALID_TAB_ID;
         }
         Tab tab = mTabModelSelector.getTabById(id);
+        updateShareData(id);
         if (tab == null || !getCurrentTabGroupModelFilter().isTabInTabGroup(tab)) {
             mResetHandler.resetStripWithListOfTabs(null);
             mIsTabGroupUiVisible = false;
@@ -466,10 +444,48 @@
         mVisibilityController.setBottomControlsVisible(mIsTabGroupUiVisible);
     }
 
+    // TODO(crbug.com/362280397): Dynamically drive updates by observing the backend.
+    private void updateShareData(int tabId) {
+        boolean isIncognitoSelected = mTabModelSelector.isIncognitoSelected();
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING)
+                || isIncognitoSelected
+                || tabId == Tab.INVALID_TAB_ID) {
+            clearCollaborationId();
+            return;
+        }
+
+        assert mSharedImageTilesCoordinator != null;
+        @Nullable
+        String collaborationId =
+                TabShareUtils.getCollaborationIdOrNull(
+                        tabId,
+                        mTabModelSelector.getModel(/* incognito= */ false),
+                        mTabGroupSyncService);
+        if (TabShareUtils.isCollaborationIdValid(collaborationId)) {
+            mSharedImageTilesCoordinator.updateCollaborationId(collaborationId);
+
+            // TODO(crbug.com/363043430): Per UX spec the image tiles should remain gone until
+            // they contain at least one non-owner avatar. Fix this.
+            mModel.set(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_VISIBLE, false);
+            mModel.set(TabGroupUiProperties.IMAGE_TILES_CONTAINER_VISIBLE, true);
+        } else {
+            clearCollaborationId();
+        }
+    }
+
+    private void clearCollaborationId() {
+        mModel.set(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_VISIBLE, true);
+        mModel.set(TabGroupUiProperties.IMAGE_TILES_CONTAINER_VISIBLE, false);
+        if (mSharedImageTilesCoordinator != null) {
+            mSharedImageTilesCoordinator.updateCollaborationId(/* collaborationId= */ null);
+        }
+    }
+
     /**
      * Get a list of tabs to show based on a tab ID. When tab group is enabled, it will return all
      * tabs that are in the same group with target tab.
-     * @param id  The ID of the tab that will be used to decide the list of tabs to show.
+     *
+     * @param id The ID of the tab that will be used to decide the list of tabs to show.
      */
     private List<Tab> getTabsToShowForId(int id) {
         return getCurrentTabGroupModelFilter().getRelatedTabList(id);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java
index 8dd3290..f6c9be1 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java
@@ -12,8 +12,7 @@
 /** {@link PropertyKey} list for the TabGroupUi. */
 class TabGroupUiProperties {
     public static final PropertyModel.WritableObjectPropertyKey<OnClickListener>
-            SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER =
-                    new PropertyModel.WritableObjectPropertyKey<>();
+            SHOW_GROUP_DIALOG_ON_CLICK_LISTENER = new PropertyModel.WritableObjectPropertyKey<>();
     public static final PropertyModel.WritableObjectPropertyKey<OnClickListener>
             NEW_TAB_BUTTON_ON_CLICK_LISTENER = new PropertyModel.WritableObjectPropertyKey<>();
     public static final PropertyModel.WritableBooleanPropertyKey IS_MAIN_CONTENT_VISIBLE =
@@ -22,6 +21,10 @@
             new PropertyModel.WritableBooleanPropertyKey();
     public static final PropertyModel.WritableIntPropertyKey BACKGROUND_COLOR =
             new PropertyModel.WritableIntPropertyKey();
+    public static final PropertyModel.WritableBooleanPropertyKey SHOW_GROUP_DIALOG_BUTTON_VISIBLE =
+            new PropertyModel.WritableBooleanPropertyKey();
+    public static final PropertyModel.WritableBooleanPropertyKey IMAGE_TILES_CONTAINER_VISIBLE =
+            new PropertyModel.WritableBooleanPropertyKey();
 
     /**
      * Integer, but not {@link PropertyModel.WritableIntPropertyKey} so that we can force update on
@@ -30,18 +33,15 @@
     public static final PropertyModel.WritableObjectPropertyKey<Integer> INITIAL_SCROLL_INDEX =
             new PropertyModel.WritableObjectPropertyKey<>(true);
 
-    public static final PropertyModel.WritableObjectPropertyKey<String>
-            SHOW_GROUP_DIALOG_BUTTON_CONTENT_DESCRIPTION =
-                    new PropertyModel.WritableObjectPropertyKey<>();
-
     public static final PropertyKey[] ALL_KEYS =
             new PropertyKey[] {
-                SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER,
+                SHOW_GROUP_DIALOG_ON_CLICK_LISTENER,
                 NEW_TAB_BUTTON_ON_CLICK_LISTENER,
                 IS_MAIN_CONTENT_VISIBLE,
                 IS_INCOGNITO,
                 BACKGROUND_COLOR,
+                SHOW_GROUP_DIALOG_BUTTON_VISIBLE,
+                IMAGE_TILES_CONTAINER_VISIBLE,
                 INITIAL_SCROLL_INDEX,
-                SHOW_GROUP_DIALOG_BUTTON_CONTENT_DESCRIPTION,
             };
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
index 3f579e2..38cb0bd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -13,6 +13,7 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.ColorRes;
+import androidx.annotation.Nullable;
 import androidx.core.content.ContextCompat;
 import androidx.core.graphics.drawable.DrawableCompat;
 import androidx.core.widget.ImageViewCompat;
@@ -28,6 +29,7 @@
     private ChromeImageView mFadingEdgeEnd;
     private ViewGroup mContainerView;
     private ViewGroup mMainContent;
+    private @Nullable FrameLayout mImageTilesContainer;
 
     public TabGroupUiToolbarView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -43,12 +45,21 @@
         mFadingEdgeEnd = findViewById(R.id.tab_strip_fading_edge_end);
         mContainerView = findViewById(R.id.toolbar_container_view);
         mMainContent = findViewById(R.id.main_content);
+        mImageTilesContainer = findViewById(R.id.toolbar_image_tiles_container);
     }
 
     void setShowGroupDialogButtonOnClickListener(OnClickListener listener) {
         mShowGroupDialogButton.setOnClickListener(listener);
     }
 
+    void setImageTilesContainerOnClickListener(OnClickListener listener) {
+        if (mImageTilesContainer == null) return;
+
+        // TODO(crbug.com/362280397): Possibly attach to a child view instead for ripple and instead
+        // add a valid TouchDelegate.
+        mImageTilesContainer.setOnClickListener(listener);
+    }
+
     void setNewTabButtonOnClickListener(OnClickListener listener) {
         mNewTabButton.setOnClickListener(listener);
     }
@@ -94,13 +105,13 @@
         DrawableCompat.setTint(getBackground(), color);
     }
 
-    /** Set the content description of the show group dialog button. */
-    void setShowGroupDialogButtonContentDescription(String string) {
-        mShowGroupDialogButton.setContentDescription(string);
+    void setShowGroupDialogButtonVisible(boolean visible) {
+        mShowGroupDialogButton.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
-    /** Set the content description of the new tab button. */
-    void setNewTabButtonContentDescription(String string) {
-        mNewTabButton.setContentDescription(string);
+    void setImageTilesContainerVisible(boolean visible) {
+        if (mImageTilesContainer == null) return;
+
+        mImageTilesContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java
index 7539d22..7202915 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java
@@ -5,12 +5,13 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.BACKGROUND_COLOR;
+import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.IMAGE_TILES_CONTAINER_VISIBLE;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.INITIAL_SCROLL_INDEX;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.IS_INCOGNITO;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.IS_MAIN_CONTENT_VISIBLE;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.NEW_TAB_BUTTON_ON_CLICK_LISTENER;
-import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_CONTENT_DESCRIPTION;
-import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER;
+import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_VISIBLE;
+import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER;
 
 import android.view.View;
 
@@ -41,9 +42,11 @@
      * @param propertyKey The key for the property to update for.
      */
     public static void bind(PropertyModel model, ViewHolder viewHolder, PropertyKey propertyKey) {
-        if (SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER == propertyKey) {
+        if (SHOW_GROUP_DIALOG_ON_CLICK_LISTENER == propertyKey) {
             viewHolder.toolbarView.setShowGroupDialogButtonOnClickListener(
-                    model.get(SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER));
+                    model.get(SHOW_GROUP_DIALOG_ON_CLICK_LISTENER));
+            viewHolder.toolbarView.setImageTilesContainerOnClickListener(
+                    model.get(SHOW_GROUP_DIALOG_ON_CLICK_LISTENER));
         } else if (NEW_TAB_BUTTON_ON_CLICK_LISTENER == propertyKey) {
             viewHolder.toolbarView.setNewTabButtonOnClickListener(
                     model.get(NEW_TAB_BUTTON_ON_CLICK_LISTENER));
@@ -53,6 +56,12 @@
             viewHolder.toolbarView.setIsIncognito(model.get(IS_INCOGNITO));
         } else if (BACKGROUND_COLOR == propertyKey) {
             viewHolder.toolbarView.setContentBackgroundColor(model.get(BACKGROUND_COLOR));
+        } else if (SHOW_GROUP_DIALOG_BUTTON_VISIBLE == propertyKey) {
+            viewHolder.toolbarView.setShowGroupDialogButtonVisible(
+                    model.get(SHOW_GROUP_DIALOG_BUTTON_VISIBLE));
+        } else if (IMAGE_TILES_CONTAINER_VISIBLE == propertyKey) {
+            viewHolder.toolbarView.setImageTilesContainerVisible(
+                    model.get(IMAGE_TILES_CONTAINER_VISIBLE));
         } else if (INITIAL_SCROLL_INDEX == propertyKey) {
             int index = (Integer) model.get(INITIAL_SCROLL_INDEX);
             LinearLayoutManager manager =
@@ -61,9 +70,6 @@
                     manager.findLastVisibleItemPosition() - manager.findFirstVisibleItemPosition();
             // Try to scroll to a state where the selected tab is in the middle of the strip.
             manager.scrollToPositionWithOffset(index - showingItemsCount / 2, 0);
-        } else if (SHOW_GROUP_DIALOG_BUTTON_CONTENT_DESCRIPTION == propertyKey) {
-            viewHolder.toolbarView.setShowGroupDialogButtonContentDescription(
-                    model.get(SHOW_GROUP_DIALOG_BUTTON_CONTENT_DESCRIPTION));
         }
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java
index 95df84c..732fa16 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java
@@ -92,7 +92,7 @@
                 @Override
                 public void willCloseTab(Tab tab, boolean didCloseAlone) {
                     if (mCurrentTabModelFilterSupplier.get().getTabModel().getCount() == 1) {
-                        removeAllAppendedMessagePostAnimation();
+                        removeAllAppendedMessage();
                     } else if (mPriceMessageService != null
                             && mPriceMessageService.getBindingTabId() == tab.getId()) {
                         removePriceWelcomeMessage();
@@ -546,19 +546,6 @@
         }
     }
 
-    private void removeAllAppendedMessagePostAnimation() {
-        TabListCoordinator tabListCoordinator = mTabListCoordinatorSupplier.get();
-        if (tabListCoordinator == null) return;
-
-        tabListCoordinator.runOnItemAnimatorFinished(
-                () -> {
-                    if (mTabListCoordinatorSupplier.get() != tabListCoordinator) {
-                        return;
-                    }
-                    removeAllAppendedMessage();
-                });
-    }
-
     /**
      * Restore all the message items that should show. Right now this is only used to restore
      * message items when the closure of the last tab in tab switcher is undone.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManagerUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManagerUnitTest.java
index b8b97b2..312298a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManagerUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManagerUnitTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -13,8 +14,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import static org.chromium.ui.test.util.MockitoHelper.doCallback;
-
 import android.app.Activity;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -121,13 +120,6 @@
         doReturn(mProfile).when(mTabModel).getProfile();
         mCurrentTabModelFilterSupplier.set(mTabModelFilter);
 
-        doCallback(
-                        (Runnable r) -> {
-                            r.run();
-                        })
-                .when(mTabListCoordinator)
-                .runOnItemAnimatorFinished(any());
-
         mActivityScenarioRule.getScenario().onActivity(this::onActivityReady);
     }
 
@@ -200,12 +192,11 @@
         // Mock that mTab1 is not the only tab in the current tab model and it will be closed.
         doReturn(2).when(mTabModel).getCount();
         mTabModelObserverCaptor.getValue().willCloseTab(mTab1, true);
-        verify(mTabListCoordinator, never()).runOnItemAnimatorFinished(any());
+        verify(mTabListCoordinator, never()).removeSpecialListItem(anyInt(), anyInt());
 
         // Mock that mTab1 is the only tab in the current tab model and it will be closed.
         doReturn(1).when(mTabModel).getCount();
         mTabModelObserverCaptor.getValue().willCloseTab(mTab1, true);
-        verify(mTabListCoordinator).runOnItemAnimatorFinished(any());
 
         verify(mTabListCoordinator)
                 .removeSpecialListItem(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
index ddbdf75..a4bf434 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
@@ -5,11 +5,15 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
 
 import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory;
+import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesCoordinator;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
@@ -262,4 +266,21 @@
                     }
                 });
     }
+
+    /**
+     * Attaches an {@link SharedImageTilesCoordinator} to a {@link FrameLayout}.
+     *
+     * @param sharedImageTilesCoordinator The {@link SharedImageTilesCoordinator} to attach.
+     * @param container The {@link FrameLayout} to attach to.
+     */
+    public static void attachSharedImageTilesCoordinatorToFrameLayout(
+            SharedImageTilesCoordinator sharedImageTilesCoordinator, FrameLayout container) {
+        View imageTilesView = sharedImageTilesCoordinator.getView();
+        var layoutParams =
+                new FrameLayout.LayoutParams(
+                        FrameLayout.LayoutParams.WRAP_CONTENT,
+                        FrameLayout.LayoutParams.WRAP_CONTENT,
+                        Gravity.CENTER);
+        container.addView(imageTilesView, layoutParams);
+    }
 }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java
index 4b7dea2..bd330e3 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java
@@ -7,7 +7,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.content.res.ColorStateList;
@@ -43,6 +42,7 @@
     private ImageView mNewTabButton;
     private ViewGroup mContainerView;
     private View mMainContent;
+    private FrameLayout mImageTilesContainer;
 
     private PropertyModel mModel;
     private PropertyModelChangeProcessor mMCP;
@@ -58,7 +58,7 @@
                             (TabGroupUiToolbarView)
                                     LayoutInflater.from(getActivity())
                                             .inflate(
-                                                    R.layout.bottom_tab_strip_toolbar,
+                                                    R.layout.dynamic_bottom_tab_strip_toolbar,
                                                     parentView,
                                                     false);
                     mShowGroupDialogButton =
@@ -66,6 +66,8 @@
                     mNewTabButton = toolbarView.findViewById(R.id.toolbar_new_tab_button);
                     mContainerView = toolbarView.findViewById(R.id.toolbar_container_view);
                     mMainContent = toolbarView.findViewById(R.id.main_content);
+                    mImageTilesContainer =
+                            toolbarView.findViewById(R.id.toolbar_image_tiles_container);
                     RecyclerView recyclerView =
                             (TabListRecyclerView)
                                     LayoutInflater.from(getActivity())
@@ -98,35 +100,52 @@
     @Test
     @UiThreadTest
     @SmallTest
-    public void testSetShowGroupDialogButtonOnClickListener() {
-        AtomicBoolean leftButtonClicked = new AtomicBoolean();
-        leftButtonClicked.set(false);
+    public void testSetShowGroupDialogOnClickListener() {
+        AtomicBoolean clicked = new AtomicBoolean();
+        clicked.set(false);
         mShowGroupDialogButton.performClick();
-        assertFalse(leftButtonClicked.get());
+        assertFalse(clicked.get());
 
         mModel.set(
-                TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER,
-                (View view) -> leftButtonClicked.set(true));
+                TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER,
+                (View view) -> clicked.set(true));
 
         mShowGroupDialogButton.performClick();
-        assertTrue(leftButtonClicked.get());
+        assertTrue(clicked.get());
     }
 
     @Test
     @UiThreadTest
     @SmallTest
     public void testSetNewTabButtonOnClickListener() {
-        AtomicBoolean rightButtonClicked = new AtomicBoolean();
-        rightButtonClicked.set(false);
+        AtomicBoolean clicked = new AtomicBoolean();
+        clicked.set(false);
         mNewTabButton.performClick();
-        assertFalse(rightButtonClicked.get());
+        assertFalse(clicked.get());
 
         mModel.set(
                 TabGroupUiProperties.NEW_TAB_BUTTON_ON_CLICK_LISTENER,
-                (View view) -> rightButtonClicked.set(true));
+                (View view) -> clicked.set(true));
 
         mNewTabButton.performClick();
-        assertTrue(rightButtonClicked.get());
+        assertTrue(clicked.get());
+    }
+
+    @Test
+    @UiThreadTest
+    @SmallTest
+    public void testSetImageTilesContainerOnClickListener() {
+        AtomicBoolean clicked = new AtomicBoolean();
+        clicked.set(false);
+        mImageTilesContainer.performClick();
+        assertFalse(clicked.get());
+
+        mModel.set(
+                TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER,
+                (View view) -> clicked.set(true));
+
+        mImageTilesContainer.performClick();
+        assertTrue(clicked.get());
     }
 
     @Test
@@ -171,12 +190,22 @@
     @Test
     @UiThreadTest
     @SmallTest
-    public void testSetShowGroupDialogButtonContentDescription() {
-        assertNull(mShowGroupDialogButton.getContentDescription());
+    public void testShowGroupDialogButtonVisibility() {
+        mModel.set(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_VISIBLE, false);
+        assertEquals(View.GONE, mShowGroupDialogButton.getVisibility());
 
-        String string = "show group dialog button content";
-        mModel.set(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_CONTENT_DESCRIPTION, string);
+        mModel.set(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_VISIBLE, true);
+        assertEquals(View.VISIBLE, mShowGroupDialogButton.getVisibility());
+    }
 
-        assertEquals(string, mShowGroupDialogButton.getContentDescription());
+    @Test
+    @UiThreadTest
+    @SmallTest
+    public void testImageTilesContainerVisibility() {
+        mModel.set(TabGroupUiProperties.IMAGE_TILES_CONTAINER_VISIBLE, true);
+        assertEquals(View.VISIBLE, mImageTilesContainer.getVisibility());
+
+        mModel.set(TabGroupUiProperties.IMAGE_TILES_CONTAINER_VISIBLE, false);
+        assertEquals(View.GONE, mImageTilesContainer.getVisibility());
     }
 }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageServiceUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageServiceUnitTest.java
index c705e05..fae2a1a 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageServiceUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageServiceUnitTest.java
@@ -19,6 +19,7 @@
 import static org.chromium.chrome.browser.tasks.tab_management.ArchivedTabsCardViewProperties.NUMBER_OF_ARCHIVED_TABS;
 
 import android.app.Activity;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
@@ -191,11 +192,13 @@
     @Test
     public void testCustomViewDetached() {
         createArchivedTabsMessageService();
-        mRootView.addView(mArchivedTabsMessageService.getCustomView());
-        assertNotNull(mArchivedTabsMessageService.getCustomView().getParent());
+        View customView = mArchivedTabsMessageService.getCustomView();
+        mRootView.addView(customView);
+        assertNotNull(customView.getParent());
 
-        mArchivedTabsMessageService.onRemoveAllAppendedMessage();
-        assertNull(mArchivedTabsMessageService.getCustomView().getParent());
+        // Getting the custom view should reset the parent.
+        mArchivedTabsMessageService.getCustomView();
+        assertNull(customView.getParent());
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
index 88d1a6c..5c5eeee 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
@@ -9,6 +9,8 @@
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -19,13 +21,14 @@
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Color;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -33,29 +36,43 @@
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 
 import org.chromium.base.Callback;
+import org.chromium.base.Token;
 import org.chromium.base.supplier.LazyOneshotSupplier;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory;
+import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesCoordinator;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.layouts.LayoutType;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tab.TabSelectionType;
+import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures;
+import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeaturesJni;
+import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.browser.tab_ui.TabContentManager;
 import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider;
 import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider.IncognitoStateObserver;
@@ -68,7 +85,11 @@
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
-import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
+import org.chromium.components.data_sharing.DataSharingService;
+import org.chromium.components.tab_group_sync.LocalTabGroupId;
+import org.chromium.components.tab_group_sync.SavedTabGroup;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -80,6 +101,7 @@
 @SuppressWarnings({"ResultOfMethodCallIgnored", "ArraysAsListWithZeroOrOneArgument", "unchecked"})
 @RunWith(BaseRobolectricTestRunner.class)
 @LooperMode(LooperMode.Mode.LEGACY)
+@EnableFeatures(ChromeFeatureList.DATA_SHARING)
 public class TabGroupUiMediatorUnitTest {
     private static final int TAB1_ID = 456;
     private static final int TAB2_ID = 789;
@@ -91,33 +113,46 @@
     private static final int TAB1_ROOT_ID = TAB1_ID;
     private static final int TAB2_ROOT_ID = TAB2_ID;
     private static final int TAB3_ROOT_ID = TAB2_ID;
-    private static final int PRIMARY_BACKGROUND_COLOR = Color.WHITE;
-    private static final int INCOGNITO_BACKGROUND_COLOR = Color.GRAY;
+    private static final String COLLABORATION_ID1 = "A";
+    private static final String GROUP_TITLE = "My Group";
+    private static final Token TAB2_GROUP_ID = new Token(1L, 2L);
 
-    @Mock BottomControlsCoordinator.BottomControlsVisibilityController mVisibilityController;
-    @Mock TabGroupUiMediator.ResetHandler mResetHandler;
-    @Mock TabModelSelectorImpl mTabModelSelector;
-    @Mock TabContentManager mTabContentManager;
-    @Mock TabCreatorManager mTabCreatorManager;
-    @Mock TabCreator mTabCreator;
-    @Mock LayoutStateProvider mLayoutManager;
-    @Mock IncognitoStateProvider mIncognitoStateProvider;
-    @Mock TabModel mTabModel;
-    @Mock View mView;
-    @Mock TabModelFilterProvider mTabModelFilterProvider;
-    @Mock TabGroupModelFilter mTabGroupModelFilter;
-    @Mock TabGridDialogMediator.DialogController mTabGridDialogController;
-    @Mock Context mContext;
-    @Mock ObservableSupplier<Boolean> mOmniboxFocusStateSupplier;
-    @Mock private Resources mResources;
+    @Rule public JniMocker mJniMocker = new JniMocker();
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock private Profile mProfile;
+    @Mock private TabGroupSyncFeatures.Natives mTabGroupSyncFeaturesJniMock;
+    @Mock private TabGroupSyncService mTabGroupSyncService;
+    @Mock private DataSharingService mDataSharingService;
+
+    @Mock
+    private BottomControlsCoordinator.BottomControlsVisibilityController mVisibilityController;
+
+    @Mock private TabGroupUiMediator.ResetHandler mResetHandler;
+    @Mock private TabModelSelectorImpl mTabModelSelector;
+    @Mock private TabContentManager mTabContentManager;
+    @Mock private TabCreatorManager mTabCreatorManager;
+    @Mock private TabCreator mTabCreator;
+    @Mock private LayoutStateProvider mLayoutManager;
+    @Mock private IncognitoStateProvider mIncognitoStateProvider;
+    @Mock private TabModel mTabModel;
+    @Mock private View mView;
+    @Mock private TabModelFilterProvider mTabModelFilterProvider;
+    @Mock private TabGroupModelFilter mTabGroupModelFilter;
+    @Mock private TabGridDialogMediator.DialogController mTabGridDialogController;
+    @Mock private ObservableSupplier<Boolean> mOmniboxFocusStateSupplier;
+    @Mock private SharedImageTilesCoordinator mSharedImageTilesCoordinator;
     @Mock private ObservableSupplierImpl<TabModel> mTabModelSupplier;
     @Captor private ArgumentCaptor<Callback<TabModel>> mTabModelSupplierObserverCaptor;
-    @Captor ArgumentCaptor<TabModelObserver> mTabModelObserverArgumentCaptor;
-    @Captor ArgumentCaptor<LayoutStateObserver> mLayoutStateObserverCaptor;
-    @Captor ArgumentCaptor<IncognitoStateObserver> mIncognitoStateObserverArgumentCaptor;
-    @Captor ArgumentCaptor<TabGroupModelFilterObserver> mTabGroupModelFilterObserverArgumentCaptor;
-    @Captor ArgumentCaptor<TabObserver> mTabObserverCaptor;
-    @Captor ArgumentCaptor<Callback<Boolean>> mOmniboxFocusObserverCaptor;
+    @Captor private ArgumentCaptor<TabModelObserver> mTabModelObserverArgumentCaptor;
+    @Captor private ArgumentCaptor<LayoutStateObserver> mLayoutStateObserverCaptor;
+    @Captor private ArgumentCaptor<IncognitoStateObserver> mIncognitoStateObserverArgumentCaptor;
+
+    @Captor
+    private ArgumentCaptor<TabGroupModelFilterObserver> mTabGroupModelFilterObserverArgumentCaptor;
+
+    @Captor private ArgumentCaptor<TabObserver> mTabObserverCaptor;
+    @Captor private ArgumentCaptor<Callback<Boolean>> mOmniboxFocusObserverCaptor;
 
     private final ObservableSupplierImpl<Boolean> mHandleBackPressChangedSupplier =
             new ObservableSupplierImpl<>();
@@ -126,6 +161,9 @@
     private final ObservableSupplierImpl<Boolean> mTabGridDialogShowingOrAnimationSupplier =
             new ObservableSupplierImpl<>();
 
+    private Context mContext;
+    private int mPrimaryBackgroundColor;
+    private int mIncognitoBackgroundColor;
     private Tab mTab1;
     private Tab mTab2;
     private Tab mTab3;
@@ -204,17 +242,13 @@
                         mIncognitoStateProvider,
                         mDialogControllerSupplier,
                         mOmniboxFocusStateSupplier,
-                        PRIMARY_BACKGROUND_COLOR,
-                        INCOGNITO_BACKGROUND_COLOR);
+                        mSharedImageTilesCoordinator);
 
         if (currentTab == null) {
             verifyNeverReset();
             return;
         }
 
-        // Verify strip button content description setup.
-        verify(mContext).getString(R.string.accessibility_bottom_tab_strip_expand_tab_sheet);
-
         verify(mTabModelSupplier).addObserver(mTabModelSupplierObserverCaptor.capture());
 
         // Verify strip initial reset.
@@ -226,26 +260,45 @@
         }
     }
 
+    private void setupSyncedGroup(boolean isShared) {
+        SavedTabGroup savedTabGroup = new SavedTabGroup();
+        savedTabGroup.title = GROUP_TITLE;
+        savedTabGroup.collaborationId = isShared ? COLLABORATION_ID1 : null;
+        when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup);
+    }
+
+    /**
+     * After setUp(), tabModel has 3 tabs in the following order: mTab1, mTab2 and mTab3. If
+     * TabGroup is enabled, mTab2 and mTab3 are in a group. Each test must call
+     * initAndAssertProperties(selectedTab) first, with selectedTab being the currently selected tab
+     * when the TabGroupUiMediator is created.
+     */
     @Before
     public void setUp() {
-        // After setUp(), tabModel has 3 tabs in the following order: mTab1, mTab2 and mTab3. If
-        // TabGroup is enabled, mTab2 and mTab3 are in a group. Each test must call
-        // initAndAssertProperties(selectedTab) first, with selectedTab being the currently selected
-        // tab when the TabGroupUiMediator is created.
-        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        Resources resources = spy(mContext.getResources());
+        when(resources.getInteger(R.integer.min_screen_width_bucket)).thenReturn(1);
+        when(mContext.getResources()).thenReturn(resources);
+        mPrimaryBackgroundColor = SemanticColorUtils.getDialogBgColor(mContext);
+        mIncognitoBackgroundColor = mContext.getColor(R.color.dialog_bg_color_dark_baseline);
 
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mResources.getInteger(org.chromium.ui.R.integer.min_screen_width_bucket))
-                .thenReturn(1);
+        mJniMocker.mock(TabGroupSyncFeaturesJni.TEST_HOOKS, mTabGroupSyncFeaturesJniMock);
+        doReturn(true).when(mTabGroupSyncFeaturesJniMock).isTabGroupSyncEnabled(mProfile);
+        TabGroupSyncServiceFactory.setForTesting(mTabGroupSyncService);
+        DataSharingServiceFactory.setForTesting(mDataSharingService);
 
         // Set up Tabs
         mTab1 = prepareTab(TAB1_ID, TAB1_ROOT_ID);
         mTab2 = prepareTab(TAB2_ID, TAB2_ROOT_ID);
+        when(mTab2.getTabGroupId()).thenReturn(TAB2_GROUP_ID);
         mTab3 = prepareTab(TAB3_ID, TAB3_ROOT_ID);
+        when(mTab3.getTabGroupId()).thenReturn(TAB2_GROUP_ID);
         mTabGroup1 = new ArrayList<>(Arrays.asList(mTab1));
         mTabGroup2 = new ArrayList<>(Arrays.asList(mTab2, mTab3));
 
         // Setup TabModel.
+        when(mTabModel.getProfile()).thenReturn(mProfile);
+        when(mTabModelSelector.isIncognitoSelected()).thenReturn(false);
         doReturn(mTabModel).when(mTabModel).getComprehensiveModel();
         doReturn(mTabModel).when(mTabModelSelector).getModel(false);
         doReturn(false).when(mTabModel).isIncognito();
@@ -255,6 +308,9 @@
         doReturn(mTab1).when(mTabModel).getTabAt(0);
         doReturn(mTab2).when(mTabModel).getTabAt(1);
         doReturn(mTab3).when(mTabModel).getTabAt(2);
+        when(mTabModel.getTabById(TAB1_ID)).thenReturn(mTab1);
+        when(mTabModel.getTabById(TAB2_ID)).thenReturn(mTab2);
+        when(mTabModel.getTabById(TAB3_ID)).thenReturn(mTab3);
         doReturn(POSITION1).when(mTabModel).indexOf(mTab1);
         doReturn(POSITION2).when(mTabModel).indexOf(mTab2);
         doReturn(POSITION3).when(mTabModel).indexOf(mTab3);
@@ -344,7 +400,7 @@
         initAndAssertProperties(mTab2);
 
         View.OnClickListener listener =
-                mModel.get(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER);
+                mModel.get(TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER);
         assertThat(listener, instanceOf(View.OnClickListener.class));
 
         listener.onClick(mView);
@@ -360,7 +416,7 @@
         mTabGridDialogShowingOrAnimationSupplier.set(true);
 
         View.OnClickListener listener =
-                mModel.get(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER);
+                mModel.get(TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER);
         assertThat(listener, instanceOf(View.OnClickListener.class));
 
         listener.onClick(mView);
@@ -977,7 +1033,7 @@
     public void switchTabModel_UiVisible_TabGroup() {
         initAndAssertProperties(mTab1);
         assertThat(mTabGroupUiMediator.getIsShowingOverViewModeForTesting(), equalTo(false));
-        TabModel incognitoTabModel = prepareIncognitoTabModel();
+        prepareIncognitoTabModel();
 
         // Mock that tab2 is selected after tab model switch, and tab2 is in a group.
         doReturn(TAB2_ID).when(mTabModelSelector).getCurrentTabId();
@@ -990,7 +1046,7 @@
     public void switchTabModel_UiNotVisible_TabGroup() {
         initAndAssertProperties(mTab1);
         assertThat(mTabGroupUiMediator.getIsShowingOverViewModeForTesting(), equalTo(false));
-        TabModel incognitoTabModel = prepareIncognitoTabModel();
+        prepareIncognitoTabModel();
 
         // Mock that tab1 is selected after tab model switch, and tab1 is a single tab.
         doReturn(TAB1_ID).when(mTabModelSelector).getCurrentTabId();
@@ -999,8 +1055,6 @@
         verifyResetStrip(false, null);
     }
 
-    /*********************** Class common tests *************************/
-
     @Test
     public void incognitoChange() {
         initAndAssertProperties(mTab1);
@@ -1011,20 +1065,20 @@
         assertThat(mModel.get(TabGroupUiProperties.IS_INCOGNITO), equalTo(true));
         assertThat(
                 mModel.get(TabGroupUiProperties.BACKGROUND_COLOR),
-                equalTo(INCOGNITO_BACKGROUND_COLOR));
+                equalTo(mIncognitoBackgroundColor));
         mVisibilityControllerInOrder
                 .verify(mVisibilityController)
-                .setBottomControlsColor(INCOGNITO_BACKGROUND_COLOR);
+                .setBottomControlsColor(mIncognitoBackgroundColor);
 
         mIncognitoStateObserverArgumentCaptor.getValue().onIncognitoStateChanged(false);
 
         assertThat(mModel.get(TabGroupUiProperties.IS_INCOGNITO), equalTo(false));
         assertThat(
                 mModel.get(TabGroupUiProperties.BACKGROUND_COLOR),
-                equalTo(PRIMARY_BACKGROUND_COLOR));
+                equalTo(mPrimaryBackgroundColor));
         mVisibilityControllerInOrder
                 .verify(mVisibilityController)
-                .setBottomControlsColor(PRIMARY_BACKGROUND_COLOR);
+                .setBottomControlsColor(mPrimaryBackgroundColor);
     }
 
     @Test
@@ -1032,12 +1086,12 @@
         initAndAssertProperties(mTab3);
         View.OnClickListener listener = v -> {};
 
-        mModel.set(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER, null);
+        mModel.set(TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER, null);
 
-        mModel.set(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER, listener);
+        mModel.set(TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER, listener);
 
         assertThat(
-                mModel.get(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_ON_CLICK_LISTENER),
+                mModel.get(TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER),
                 equalTo(listener));
     }
 
@@ -1068,4 +1122,40 @@
         mOmniboxFocusObserverCaptor.getValue().onResult(false);
         verifyResetStrip(true, mTabGroup2);
     }
+
+    @Test
+    public void testImageTiles_NoGroup() {
+        reset(mSharedImageTilesCoordinator);
+        setupSyncedGroup(/* isShared= */ true);
+
+        initAndAssertProperties(mTab1);
+
+        assertTrue(mModel.get(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_VISIBLE));
+        assertFalse(mModel.get(TabGroupUiProperties.IMAGE_TILES_CONTAINER_VISIBLE));
+        verify(mSharedImageTilesCoordinator).updateCollaborationId(null);
+    }
+
+    @Test
+    public void testImageTiles_CollaborationId() {
+        reset(mSharedImageTilesCoordinator);
+        setupSyncedGroup(/* isShared= */ true);
+
+        initAndAssertProperties(mTab2);
+
+        assertFalse(mModel.get(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_VISIBLE));
+        assertTrue(mModel.get(TabGroupUiProperties.IMAGE_TILES_CONTAINER_VISIBLE));
+        verify(mSharedImageTilesCoordinator).updateCollaborationId(COLLABORATION_ID1);
+    }
+
+    @Test
+    public void testImageTiles_NoCollaborationId() {
+        reset(mSharedImageTilesCoordinator);
+        setupSyncedGroup(/* isShared= */ false);
+
+        initAndAssertProperties(mTab2);
+
+        assertTrue(mModel.get(TabGroupUiProperties.SHOW_GROUP_DIALOG_BUTTON_VISIBLE));
+        assertFalse(mModel.get(TabGroupUiProperties.IMAGE_TILES_CONTAINER_VISIBLE));
+        verify(mSharedImageTilesCoordinator).updateCollaborationId(null);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
index 8d0b73fd..aee3b16 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
@@ -637,8 +637,12 @@
             String url, boolean usePrefetchProxy, @Nullable String verifiedSourceOrigin) {
         try (TraceEvent e = TraceEvent.scoped("WarmupManager.startPrefetchFromCCT")) {
             ThreadUtils.assertOnUiThread();
-            if (!ChromeFeatureList.sCctNavigationalPrefetch.isEnabled()) {
-                Log.e(TAG, "Prefetch failed because CCTNavigationalPrefetch is not enabled.");
+            if (!ChromeFeatureList.sPrefetchBrowserInitiatedTriggers.isEnabled()
+                    || !ChromeFeatureList.sCctNavigationalPrefetch.isEnabled()) {
+                Log.w(
+                        TAG,
+                        "Prefetch failed because PrefetchBrowserInitiatedTriggers and/or"
+                                + " CCTNavigationalPrefetch is not enabled.");
                 return;
             }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/CompositorButton.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/CompositorButton.java
index 75d474af..0d204d19 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/CompositorButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/CompositorButton.java
@@ -9,10 +9,16 @@
 import android.graphics.RectF;
 import android.util.FloatProperty;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutView;
 import org.chromium.ui.MotionEventUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * {@link CompositorButton} keeps track of state for buttons that are rendered in the compositor.
  */
@@ -34,18 +40,14 @@
                 }
             };
 
-    /** Handler for click actions on VirtualViews. */
-    public interface CompositorOnClickHandler {
-        /**
-         * Handles the click action.
-         *
-         * @param time The time of the click action.
-         */
-        void onClick(long time);
+    @IntDef({ButtonType.NEW_TAB, ButtonType.INCOGNITO_SWITCHER, ButtonType.TAB_CLOSE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ButtonType {
+        int NEW_TAB = 0;
+        int INCOGNITO_SWITCHER = 1;
+        int TAB_CLOSE = 2;
     }
 
-    private final CompositorOnClickHandler mClickHandler;
-
     protected int mResource;
     protected int mBackgroundResource;
 
@@ -58,6 +60,9 @@
     private boolean mIsPressedFromMouse;
     private boolean mIsHovered;
     private String mAccessibilityDescriptionIncognito = "";
+    // @StripLayoutView the button was embedded in. Null if it's not a child view.
+    @Nullable private final StripLayoutView mParentView;
+    private final @ButtonType int mType;
 
     /**
      * Default constructor for {@link CompositorButton}
@@ -68,20 +73,25 @@
      * @param clickHandler The action to be performed on click.
      */
     public CompositorButton(
-            Context context, float width, float height, CompositorOnClickHandler clickHandler) {
-        super(false);
+            Context context,
+            @ButtonType int type,
+            StripLayoutView parentView,
+            float width,
+            float height,
+            StripLayoutViewOnClickHandler clickHandler) {
+        super(false, clickHandler);
         mDrawBounds.set(0, 0, width, height);
 
+        mType = type;
         mOpacity = 1.f;
         mIsPressed = false;
+        mParentView = parentView;
         setVisible(true);
 
         Resources res = context.getResources();
         float sPxToDp = 1.0f / res.getDisplayMetrics().density;
         float clickSlop = res.getDimension(R.dimen.compositor_button_slop) * sPxToDp;
         setTouchTargetInsets(-clickSlop, -clickSlop, -clickSlop, -clickSlop);
-
-        mClickHandler = clickHandler;
     }
 
     /**
@@ -124,9 +134,11 @@
         return super.checkClickedOrHovered(x, y);
     }
 
-    @Override
-    public void handleClick(long time) {
-        mClickHandler.onClick(time);
+    /**
+     * @return Parent view this button is embedded in.
+     */
+    public @Nullable StripLayoutView getParentView() {
+        return mParentView;
     }
 
     /**
@@ -151,6 +163,13 @@
     }
 
     /**
+     * @return Type for this button.
+     */
+    public @ButtonType int getType() {
+        return mType;
+    }
+
+    /**
      * @return The pressed state of the button.
      */
     public boolean isPressed() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/TintedCompositorButton.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/TintedCompositorButton.java
index c790be7..51e1f6f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/TintedCompositorButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/TintedCompositorButton.java
@@ -11,6 +11,8 @@
 import androidx.annotation.DrawableRes;
 import androidx.appcompat.content.res.AppCompatResources;
 
+import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutView;
+
 /** Class for a CompositorButton that uses tint instead of multiple drawable resources. */
 public class TintedCompositorButton extends CompositorButton {
     private Context mContext;
@@ -31,19 +33,14 @@
     private @ColorInt int mApsBackgroundIncognitoPressedTint;
 
     public TintedCompositorButton(
-            Context context, float width, float height, CompositorOnClickHandler clickHandler) {
-        super(context, width, height, clickHandler);
-
-        mContext = context;
-    }
-
-    public TintedCompositorButton(
             Context context,
+            @ButtonType int type,
+            StripLayoutView parentView,
             float width,
             float height,
-            CompositorOnClickHandler clickHandler,
+            StripLayoutViewOnClickHandler clickHandler,
             @DrawableRes int resource) {
-        super(context, width, height, clickHandler);
+        super(context, type, parentView, width, height, clickHandler);
         mContext = context;
         mResource = resource;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java
index 67ea8cd..d6199b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java
@@ -43,7 +43,7 @@
     }
 
     /** Delegate for additional group title functionality. */
-    public interface StripLayoutGroupTitleDelegate {
+    public interface StripLayoutGroupTitleDelegate extends StripLayoutViewOnClickHandler {
         /**
          * Releases the resources associated with this group indicator.
          *
@@ -57,13 +57,6 @@
          * @param groupTitle This group indicator.
          */
         void rebuildResourcesForGroupTitle(StripLayoutGroupTitle groupTitle);
-
-        /**
-         * Handles group title click action.
-         *
-         * @param groupTitle The group title that was clicked.
-         */
-        void handleGroupTitleClick(StripLayoutGroupTitle groupTitle);
     }
 
     /** A property for animations to use for changing the width of the bottom indicator. */
@@ -121,7 +114,7 @@
             StripLayoutGroupTitleDelegate delegate,
             boolean incognito,
             int rootId) {
-        super(incognito);
+        super(incognito, delegate);
         assert rootId != Tab.INVALID_TAB_ID : "Tried to create a group title for an invalid group.";
         mRootId = rootId;
         mContext = context;
@@ -153,11 +146,6 @@
         return false;
     }
 
-    @Override
-    public void handleClick(long time) {
-        mDelegate.handleGroupTitleClick(this);
-    }
-
     /**
      * @return DrawX accounting for padding.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index 6b16b32..8e5a0e5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -52,11 +52,11 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
 import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton;
-import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.CompositorOnClickHandler;
+import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.ButtonType;
 import org.chromium.chrome.browser.compositor.layouts.components.TintedCompositorButton;
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackScroller;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutGroupTitle.StripLayoutGroupTitleDelegate;
-import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutTab.StripLayoutTabDelegate;
+import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutView.StripLayoutViewOnClickHandler;
 import org.chromium.chrome.browser.compositor.overlays.strip.TabLoadTracker.TabLoadTrackerCallback;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -107,7 +107,8 @@
  *
  * <p>The stacking and visual behavior is driven by setting a {@link StripStacker}.
  */
-public class StripLayoutHelper implements StripLayoutTabDelegate, StripLayoutGroupTitleDelegate {
+public class StripLayoutHelper
+        implements StripLayoutGroupTitleDelegate, StripLayoutViewOnClickHandler {
     // Drag Constants
     private static final int REORDER_SCROLL_NONE = 0;
     private static final int REORDER_SCROLL_LEFT = 1;
@@ -531,21 +532,16 @@
         mManagerHost = managerHost;
         mUpdateHost = updateHost;
         mRenderHost = renderHost;
-        CompositorOnClickHandler newTabClickHandler =
-                new CompositorOnClickHandler() {
-                    @Override
-                    public void onClick(long time) {
-                        handleNewTabClick();
-                    }
-                };
 
         // Set new tab button background resource.
         mNewTabButton =
                 new TintedCompositorButton(
                         context,
+                        ButtonType.NEW_TAB,
+                        null,
                         NEW_TAB_BUTTON_BACKGROUND_WIDTH_DP,
                         NEW_TAB_BUTTON_BACKGROUND_HEIGHT_DP,
-                        newTabClickHandler,
+                        this,
                         R.drawable.ic_new_tab_button);
         mNewTabButton.setBackgroundResourceId(R.drawable.bg_circle_tab_strip_button);
 
@@ -2127,31 +2123,6 @@
                 hovered ? FOLIO_DETACHED_BOTTOM_MARGIN_DP : FOLIO_ATTACHED_BOTTOM_MARGIN_DP);
     }
 
-    private void handleNewTabClick() {
-        if (mModel == null) return;
-
-        if (!mModel.isIncognito()) mModel.commitAllTabClosures();
-        mTabCreator.launchNtp();
-    }
-
-    @Override
-    public void handleCloseButtonClick(final StripLayoutTab tab, long time) {
-        // Placeholder tabs are expected to have invalid tab ids.
-        if (tab == null || tab.isDying() || tab.getTabId() == Tab.INVALID_TAB_ID) return;
-
-        int tabId = tab.getTabId();
-        int rootId = getTabById(tabId).getRootId();
-        if (isLastTabInGroup(tabId) && !mIncognito) {
-            showDeleteGroupDialogAndProcessTabAction(
-                    rootId,
-                    /* draggingLastTabOffStrip= */ false,
-                    /* closeTab= */ true,
-                    () -> handleCloseTab(tab, time));
-        } else {
-            handleCloseTab(tab, time);
-        }
-    }
-
     private void handleCloseTab(final StripLayoutTab tab, long time) {
         mMultiStepTabCloseAnimRunning = false;
         finishAnimationsAndPushTabUpdates();
@@ -2262,18 +2233,6 @@
                 });
     }
 
-    @Override
-    public void handleTabClick(StripLayoutTab tab) {
-        if (tab == null || tab.isDying()) return;
-
-        int newIndex = TabModelUtils.getTabIndexById(mModel, tab.getTabId());
-
-        // Early return, since placeholder tabs are known to not have tab ids.
-        if (newIndex == Tab.INVALID_TAB_ID) return;
-
-        TabModelUtils.setIndex(mModel, newIndex);
-    }
-
     /**
      * Called on click. This is called before the onUpOrCancel event.
      *
@@ -2285,46 +2244,9 @@
      */
     public void click(long time, float x, float y, boolean fromMouse, int buttons) {
         resetResizeTimeout(false);
-
-        if (mNewTabButton.click(x, y, fromMouse, buttons)) {
-            RecordUserAction.record("MobileToolbarNewTab");
-            mNewTabButton.handleClick(time);
-            return;
-        }
-
-        final StripLayoutView clickedView = getViewAtPositionX(x, true);
+        StripLayoutView clickedView = determineClickedView(x, y, fromMouse, buttons);
         if (clickedView == null) return;
-        if (clickedView instanceof StripLayoutTab clickedTab) {
-            if (clickedTab.isDying()) return;
-            if (clickedTab.checkCloseHitTest(x, y)
-                    || (fromMouse && (buttons & MotionEvent.BUTTON_TERTIARY) != 0)) {
-                RecordUserAction.record("MobileToolbarCloseTab");
-                clickedTab.getCloseButton().handleClick(time);
-            } else {
-                RecordUserAction.record("MobileTabSwitched.TabletTabStrip");
-                recordTabSwitchTimeHistogram();
-                clickedTab.handleClick(time);
-            }
-        } else if (clickedView instanceof StripLayoutGroupTitle clickedGroupTitle) {
-            clickedGroupTitle.handleClick(time);
-        }
-    }
-
-    private void recordTabSwitchTimeHistogram() {
-        if (mTabScrollStartTime == null || mMostRecentTabScroll == null) return;
-
-        long endTime = SystemClock.elapsedRealtime();
-        long duration = endTime - mTabScrollStartTime;
-        long timeFromLastInteraction = endTime - mMostRecentTabScroll;
-
-        // Discard sample if last scroll was over the max allowed interval.
-        if (timeFromLastInteraction <= TAB_SWITCH_METRICS_MAX_ALLOWED_SCROLL_INTERVAL) {
-            RecordHistogram.recordMediumTimesHistogram(
-                    "Android.TabStrip.TimeToSwitchTab", duration);
-        }
-
-        mTabScrollStartTime = null;
-        mMostRecentTabScroll = null;
+        clickedView.handleClick(time);
     }
 
     /**
@@ -2347,6 +2269,103 @@
         mOnDownWithButtonPrimary = false;
     }
 
+    /** Handle view click * */
+    @Override
+    public void onClick(long time, StripLayoutView view) {
+        if (view instanceof StripLayoutTab tab) {
+            handleTabClick(tab);
+        } else if (view instanceof StripLayoutGroupTitle groupTitle) {
+            handleGroupTitleClick(groupTitle);
+        } else if (view instanceof CompositorButton button) {
+            if (button.getType() == ButtonType.NEW_TAB) {
+                handleNewTabClick();
+            } else if (button.getType() == ButtonType.TAB_CLOSE) {
+                handleCloseButtonClick((StripLayoutTab) button.getParentView(), time);
+            }
+        }
+    }
+
+    private void handleTabClick(StripLayoutTab tab) {
+        if (tab == null || tab.isDying()) return;
+        RecordUserAction.record("MobileTabSwitched.TabletTabStrip");
+        recordTabSwitchTimeHistogram();
+
+        int newIndex = TabModelUtils.getTabIndexById(mModel, tab.getTabId());
+
+        // Early return, since placeholder tabs are known to not have tab ids.
+        if (newIndex == Tab.INVALID_TAB_ID) return;
+
+        TabModelUtils.setIndex(mModel, newIndex);
+    }
+
+    private void handleGroupTitleClick(StripLayoutGroupTitle groupTitle) {
+        if (!ChromeFeatureList.sTabStripGroupCollapse.isEnabled()) return;
+        if (groupTitle == null) return;
+
+        int rootId = groupTitle.getRootId();
+        boolean isCollapsed = mTabGroupModelFilter.getTabGroupCollapsed(rootId);
+        assert isCollapsed == groupTitle.isCollapsed();
+
+        mTabGroupModelFilter.setTabGroupCollapsed(rootId, !isCollapsed);
+        RecordHistogram.recordBooleanHistogram("Android.TabStrip.TabGroupCollapsed", !isCollapsed);
+    }
+
+    private void handleNewTabClick() {
+        if (mModel == null) return;
+
+        RecordUserAction.record("MobileToolbarNewTab");
+        if (!mModel.isIncognito()) mModel.commitAllTabClosures();
+        mTabCreator.launchNtp();
+    }
+
+    @VisibleForTesting
+    void handleCloseButtonClick(final StripLayoutTab tab, long time) {
+        // Placeholder tabs are expected to have invalid tab ids.
+        if (tab == null || tab.isDying() || tab.getTabId() == Tab.INVALID_TAB_ID) return;
+        RecordUserAction.record("MobileToolbarCloseTab");
+        int tabId = tab.getTabId();
+        int rootId = getTabById(tabId).getRootId();
+        if (isLastTabInGroup(tabId) && !mIncognito) {
+            showDeleteGroupDialogAndProcessTabAction(
+                    rootId,
+                    /* draggingLastTabOffStrip= */ false,
+                    /* closeTab= */ true,
+                    () -> handleCloseTab(tab, time));
+        } else {
+            handleCloseTab(tab, time);
+        }
+    }
+
+    private StripLayoutView determineClickedView(float x, float y, boolean fromMouse, int buttons) {
+        if (mNewTabButton.click(x, y, fromMouse, buttons)) return mNewTabButton;
+        StripLayoutView view = getViewAtPositionX(x, true);
+        if (view instanceof StripLayoutTab clickedTab) {
+            if ((clickedTab.checkCloseHitTest(x, y)
+                    || (fromMouse && (buttons & MotionEvent.BUTTON_TERTIARY) != 0))) {
+                return clickedTab.getCloseButton();
+            }
+            return clickedTab;
+        }
+        return view;
+    }
+
+    private void recordTabSwitchTimeHistogram() {
+        if (mTabScrollStartTime == null || mMostRecentTabScroll == null) return;
+
+        long endTime = SystemClock.elapsedRealtime();
+        long duration = endTime - mTabScrollStartTime;
+        long timeFromLastInteraction = endTime - mMostRecentTabScroll;
+
+        // Discard sample if last scroll was over the max allowed interval.
+        if (timeFromLastInteraction <= TAB_SWITCH_METRICS_MAX_ALLOWED_SCROLL_INTERVAL) {
+            RecordHistogram.recordMediumTimesHistogram(
+                    "Android.TabStrip.TimeToSwitchTab", duration);
+        }
+
+        mTabScrollStartTime = null;
+        mMostRecentTabScroll = null;
+    }
+
     /**
      * @return Whether or not the tabs are moving.
      */
@@ -2575,19 +2594,6 @@
         updateTabGroupCollapsed(groupTitle, isCollapsed, true);
     }
 
-    @Override
-    public void handleGroupTitleClick(StripLayoutGroupTitle groupTitle) {
-        if (!ChromeFeatureList.sTabStripGroupCollapse.isEnabled()) return;
-        if (groupTitle == null) return;
-
-        int rootId = groupTitle.getRootId();
-        boolean isCollapsed = mTabGroupModelFilter.getTabGroupCollapsed(rootId);
-        assert isCollapsed == groupTitle.isCollapsed();
-
-        mTabGroupModelFilter.setTabGroupCollapsed(rootId, !isCollapsed);
-        RecordHistogram.recordBooleanHistogram("Android.TabStrip.TabGroupCollapsed", !isCollapsed);
-    }
-
     private Animator updateTabCollapsed(StripLayoutTab tab, boolean isCollapsed, boolean animate) {
         tab.setCollapsed(isCollapsed);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
index 29a0f6e..6524104 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -46,10 +46,11 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
 import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton;
-import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.CompositorOnClickHandler;
+import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.ButtonType;
 import org.chromium.chrome.browser.compositor.layouts.components.TintedCompositorButton;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.AreaMotionEventFilter;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.MotionEventHandler;
+import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutView.StripLayoutViewOnClickHandler;
 import org.chromium.chrome.browser.compositor.scene_layer.TabStripSceneLayer;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.EventFilter;
@@ -475,8 +476,8 @@
         mStripVisibilityStateSupplier.addObserver(mStripVisibilityStateObserver);
 
         if (!ChromeFeatureList.sTabStripIncognitoMigration.isEnabled()) {
-            CompositorOnClickHandler selectorClickHandler =
-                    time -> handleModelSelectorButtonClick();
+            StripLayoutViewOnClickHandler selectorClickHandler =
+                    (time, view) -> handleModelSelectorButtonClick();
             createModelSelectorButton(context, selectorClickHandler);
         }
         // Use toolbar menu button padding to align MSB with menu button.
@@ -596,10 +597,12 @@
 
     // Incognito button for Tab Strip Redesign.
     private void createModelSelectorButton(
-            Context context, CompositorOnClickHandler selectorClickHandler) {
+            Context context, StripLayoutViewOnClickHandler selectorClickHandler) {
         mModelSelectorButton =
                 new TintedCompositorButton(
                         context,
+                        ButtonType.INCOGNITO_SWITCHER,
+                        null,
                         MODEL_SELECTOR_BUTTON_BACKGROUND_WIDTH_DP,
                         MODEL_SELECTOR_BUTTON_BACKGROUND_HEIGHT_DP,
                         selectorClickHandler,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
index 9f5c018..c158cdb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
@@ -24,7 +24,7 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
 import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton;
-import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.CompositorOnClickHandler;
+import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.ButtonType;
 import org.chromium.chrome.browser.compositor.layouts.components.TintedCompositorButton;
 import org.chromium.chrome.browser.compositor.overlays.strip.TabLoadTracker.TabLoadTrackerCallback;
 import org.chromium.chrome.browser.layouts.animation.CompositorAnimator;
@@ -51,23 +51,6 @@
         void onVisibilityChanged(boolean newVisibility);
     }
 
-    /** Delegate for additional tab functionality. */
-    public interface StripLayoutTabDelegate {
-        /**
-         * Handles tab click actions.
-         * @param tab The tab clicked.
-         */
-        void handleTabClick(StripLayoutTab tab);
-
-        /**
-         * Handles close button click actions.
-         *
-         * @param tab The tab whose close button was clicked.
-         * @param time The time the close button was clicked.
-         */
-        void handleCloseButtonClick(StripLayoutTab tab, long time);
-    }
-
     /** A property for animations to use for changing the Y offset of the tab. */
     public static final FloatProperty<StripLayoutTab> Y_OFFSET =
             new FloatProperty<>("offsetY") {
@@ -168,7 +151,6 @@
     private int mTabId;
 
     private final Context mContext;
-    private final StripLayoutTabDelegate mDelegate;
     private final TabLoadTracker mLoadTracker;
     private final LayoutUpdateHost mUpdateHost;
     private TintedCompositorButton mCloseButton;
@@ -221,21 +203,24 @@
     public StripLayoutTab(
             Context context,
             int id,
-            StripLayoutTabDelegate delegate,
+            StripLayoutViewOnClickHandler clickHandler,
             TabLoadTrackerCallback loadTrackerCallback,
             LayoutUpdateHost updateHost,
             boolean incognito) {
-        super(incognito);
+        super(incognito, clickHandler);
         mTabId = id;
         mContext = context;
-        mDelegate = delegate;
         mLoadTracker = new TabLoadTracker(id, loadTrackerCallback);
         mUpdateHost = updateHost;
-        CompositorOnClickHandler closeClickAction =
-                time -> mDelegate.handleCloseButtonClick(StripLayoutTab.this, time);
         mCloseButton =
                 new TintedCompositorButton(
-                        context, 0, 0, closeClickAction, R.drawable.btn_tab_close_normal);
+                        context,
+                        ButtonType.TAB_CLOSE,
+                        this,
+                        /* width= */ 0,
+                        /* height= */ 0,
+                        clickHandler,
+                        R.drawable.btn_tab_close_normal);
         mCloseButton.setTintResources(
                 R.color.default_icon_color_tint_list,
                 R.color.default_icon_color_tint_list,
@@ -356,13 +341,9 @@
         return super.checkClickedOrHovered(x, y);
     }
 
-    @Override
-    public void handleClick(long time) {
-        mDelegate.handleTabClick(this);
-    }
-
     /**
      * Marks if we are currently reordering this tab.
+     *
      * @param isReordering Whether the tab is reordering.
      */
     public void setIsReordering(boolean isReordering) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutView.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutView.java
index 189cd70f..6ffba1d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutView.java
@@ -18,6 +18,17 @@
  */
 public abstract class StripLayoutView implements VirtualView {
 
+    /** Handler for click actions on VirtualViews. */
+    public interface StripLayoutViewOnClickHandler {
+        /**
+         * Handles the click action.
+         *
+         * @param time The time of the click action.
+         * @param view View that received the click.
+         */
+        void onClick(long time, StripLayoutView view);
+    }
+
     /** A property for animations to use for changing the drawX of the view. */
     public static final FloatProperty<StripLayoutView> DRAW_X =
             new FloatProperty<>("drawX") {
@@ -66,11 +77,16 @@
     // A11y variables.
     private String mAccessibilityDescription = "";
 
+    // Event handlers.
+    private final StripLayoutViewOnClickHandler mOnClickHandler;
+
     /**
      * @param incognito The incognito state of the view.
+     * @param clickHandler @{@link StripLayoutViewOnClickHandler} for this view.
      */
-    protected StripLayoutView(boolean incognito) {
+    protected StripLayoutView(boolean incognito, StripLayoutViewOnClickHandler clickHandler) {
         mIsIncognito = incognito;
+        mOnClickHandler = clickHandler;
     }
 
     /**
@@ -268,6 +284,11 @@
         outTarget.set(mTouchTargetBounds);
     }
 
+    @Override
+    public void handleClick(long time) {
+        mOnClickHandler.onClick(time, this);
+    }
+
     /**
      * @return Return cached touch target bounds.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAuthUrlHeuristics.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAuthUrlHeuristics.java
index bd926221..a2fe254 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAuthUrlHeuristics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAuthUrlHeuristics.java
@@ -7,7 +7,10 @@
 import android.net.Uri;
 import android.text.TextUtils;
 
+import org.jni_zero.NativeMethods;
+
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.browser.tab.Tab;
 
 /**
  * Helper class to record histogram to determine whether the Custom Tab was launched with what looks
@@ -64,4 +67,13 @@
         } catch (UnsupportedOperationException ignored) {
         }
     }
+
+    public static void setFirstCctPageLoadForMetrics(Tab tab) {
+        CustomTabAuthUrlHeuristicsJni.get().setFirstCctPageLoadForPasswords(tab);
+    }
+
+    @NativeMethods
+    public interface Natives {
+        void setFirstCctPageLoadForPasswords(Tab tab);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index ab3cc01..05b67f37 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -668,6 +668,10 @@
         try (TraceEvent e = TraceEvent.scoped("CustomTabsConnection.prefetch")) {
             if (!ChromeFeatureList.sPrefetchBrowserInitiatedTriggers.isEnabled()
                     || !ChromeFeatureList.sCctNavigationalPrefetch.isEnabled()) {
+                Log.w(
+                        TAG,
+                        "Prefetch failed because PrefetchBrowserInitiatedTriggers and/or"
+                                + " CCTNavigationalPrefetch is not enabled.");
                 return false;
             }
             return prefetchInternal(session, uri, options);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/DefaultCustomTabIntentHandlingStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/DefaultCustomTabIntentHandlingStrategy.java
index 5462d11a..2781d9e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/DefaultCustomTabIntentHandlingStrategy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/DefaultCustomTabIntentHandlingStrategy.java
@@ -48,6 +48,10 @@
     @Override
     public void handleInitialIntent(BrowserServicesIntentDataProvider intentDataProvider) {
         @TabCreationMode int initialTabCreationMode = mTabProvider.getInitialTabCreationMode();
+        if (mTabProvider.getTab() != null) {
+            CustomTabAuthUrlHeuristics.setFirstCctPageLoadForMetrics(mTabProvider.getTab());
+        }
+
         if (initialTabCreationMode == TabCreationMode.HIDDEN) {
             handleInitialLoadForHiddenTab(initialTabCreationMode, intentDataProvider);
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridge.java
index 93c4fd0f..ccf3739 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridge.java
@@ -42,8 +42,10 @@
             int id,
             long groupTimestamp,
             @JniType("std::u16string") String groupTitle,
+            int groupColor,
             @JniType("std::vector") List<RecentlyClosedTab> tabs) {
-        RecentlyClosedGroup group = new RecentlyClosedGroup(id, groupTimestamp, groupTitle);
+        RecentlyClosedGroup group =
+                new RecentlyClosedGroup(id, groupTimestamp, groupTitle, groupColor);
         group.getTabs().addAll(tabs);
         entries.add(group);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedGroup.java
index a72fb8e..d7243ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedGroup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedGroup.java
@@ -10,24 +10,29 @@
 /** Represents a recently closed group from TabRestoreService. */
 public class RecentlyClosedGroup extends RecentlyClosedEntry {
     private final String mTitle;
+    private final int mColor;
     private final List<RecentlyClosedTab> mTabs = new ArrayList<>();
 
-    public RecentlyClosedGroup(int sessionId, long timestamp, String title) {
+    public RecentlyClosedGroup(int sessionId, long timestamp, String title, int color) {
         super(sessionId, timestamp);
         mTitle = title;
+        mColor = color;
     }
 
     /**
-     * @return title of the group this may be an empty string if the default title was used when
+     * Returns the title of the group this may be an empty string if the default title was used when
      * saving.
      */
     public String getTitle() {
         return mTitle;
     }
 
-    /**
-     * @return the list of tabs for this group.
-     */
+    /** Returns the color of the group. */
+    public int getColor() {
+        return mColor;
+    }
+
+    /** Returns the list of tabs for this group. */
     public List<RecentlyClosedTab> getTabs() {
         return mTabs;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
index 52b9209..94de984 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
@@ -141,6 +141,8 @@
                 new TitleUpdater(), false /* recursive */);
         fragmentManager.registerFragmentLifecycleCallbacks(
                 new WideDisplayPaddingApplier(), false /* recursive */);
+        fragmentManager.registerFragmentLifecycleCallbacks(
+                new SettingsMetricsReporter(), false /* recursive */);
 
         super.onCreate(savedInstanceState);
 
@@ -360,31 +362,11 @@
     }
 
     private void registerBottomSheetBackPressHandler() {
-        if (!mBottomSheetControllerSupplier.hasValue()) return;
-
-        BackPressHandler bottomSheetBackPressHandler =
-                mBottomSheetControllerSupplier.get().getBottomSheetBackPressHandler();
-        if (bottomSheetBackPressHandler != null) {
-            BackPressHelper.create(
-                    this,
-                    getOnBackPressedDispatcher(),
-                    bottomSheetBackPressHandler,
-                    SecondaryActivity.SETTINGS);
-        }
-    }
-
-    @Override
-    public void onAttachFragment(Fragment fragment) {
-        String className = fragment.getClass().getSimpleName();
-        RecordHistogram.recordSparseHistogram("Settings.FragmentAttached", className.hashCode());
-        // Log hashCode to easily add new class names to enums.xml.
-        Log.d(
-                "SettingsActivity",
-                String.format(
-                        Locale.ENGLISH,
-                        "Settings.FragmentAttached: <int value=\"%d\" label=\"%s\"/>",
-                        className.hashCode(),
-                        className));
+        BackPressHelper.create(
+                this,
+                getOnBackPressedDispatcher(),
+                mBottomSheetControllerSupplier.get().getBottomSheetBackPressHandler(),
+                SecondaryActivity.SETTINGS);
     }
 
     @Override
@@ -483,4 +465,29 @@
             }
         }
     }
+
+    private static class SettingsMetricsReporter
+            extends FragmentManager.FragmentLifecycleCallbacks {
+        @Override
+        public void onFragmentAttached(
+                @NonNull FragmentManager fragmentManager,
+                @NonNull Fragment fragment,
+                @NonNull Context context) {
+            if (!MAIN_FRAGMENT_TAG.equals(fragment.getTag())) {
+                return;
+            }
+
+            String className = fragment.getClass().getSimpleName();
+            RecordHistogram.recordSparseHistogram(
+                    "Settings.FragmentAttached", className.hashCode());
+            // Log hashCode to easily add new class names to enums.xml.
+            Log.d(
+                    "SettingsActivity",
+                    String.format(
+                            Locale.ENGLISH,
+                            "Settings.FragmentAttached: <int value=\"%d\" label=\"%s\"/>",
+                            className.hashCode(),
+                            className));
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorController.java
index 60e737c9..45e431d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorController.java
@@ -22,7 +22,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.CallbackController;
-import org.chromium.base.MathUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.Supplier;
@@ -411,7 +410,9 @@
             mWindow.setNavigationBarColor(newWindowNavigationBarColor);
             setWindowNavigationBarDividerColor(windowDividerColor);
             UiUtils.setNavigationBarIconColor(
-                    mRootView, !mForceDarkNavigationBarColor && mLightNavigationBar);
+                    mRootView,
+                    ColorUtils.isHighLuminance(
+                            ColorUtils.calculateLuminance(newNavigationBarColor)));
         }
     }
 
@@ -492,8 +493,10 @@
         }
 
         mNavigationBarScrimFraction = fraction;
-        mWindow.setNavigationBarColor(
-                applyCurrentScrimToColor(getNavigationBarColor(mForceDarkNavigationBarColor)));
+        @ColorInt
+        int scrimNavigationBarColor =
+                applyCurrentScrimToColor(getNavigationBarColor(mForceDarkNavigationBarColor));
+        mWindow.setNavigationBarColor(scrimNavigationBarColor);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
             mWindow.setNavigationBarDividerColor(
                     applyCurrentScrimToColor(
@@ -501,11 +504,9 @@
         }
 
         // Adjust the color of navigation bar icons based on color state of the navigation bar.
-        if (MathUtils.areFloatsEqual(1f, fraction)) {
-            UiUtils.setNavigationBarIconColor(mRootView, false);
-        } else if (MathUtils.areFloatsEqual(0f, fraction)) {
-            UiUtils.setNavigationBarIconColor(mRootView, true);
-        }
+        UiUtils.setNavigationBarIconColor(
+                mRootView,
+                ColorUtils.isHighLuminance(ColorUtils.calculateLuminance(scrimNavigationBarColor)));
     }
 
     @ColorInt
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
index a8d2b83..d3f8ccf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
@@ -75,6 +75,7 @@
 public class RecentTabsPageTest {
     private static final String EMAIL = "email@gmail.com";
     private static final String NAME = "Email Emailson";
+    private static final int COLOR_ID = 0;
 
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@@ -156,7 +157,7 @@
     public void testRecentlyClosedGroup_WithTitle() throws Exception {
         mPage = loadRecentTabsPage();
         // Set a recently closed group and confirm a view is rendered for it.
-        final RecentlyClosedGroup group = new RecentlyClosedGroup(2, 0, "Group Title");
+        final RecentlyClosedGroup group = new RecentlyClosedGroup(2, 0, "Group Title", COLOR_ID);
         Token tabGroupId = new Token(27839L, 4789L);
         group.getTabs()
                 .add(
@@ -212,7 +213,7 @@
         mPage = loadRecentTabsPage();
         long time = 904881600000L;
         // Set a recently closed group and confirm a view is rendered for it.
-        final RecentlyClosedGroup group = new RecentlyClosedGroup(2, time, null);
+        final RecentlyClosedGroup group = new RecentlyClosedGroup(2, time, null, COLOR_ID);
         Token tabGroupId = new Token(798L, 4389L);
         group.getTabs()
                 .add(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index 80ea1f3..6bea79a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -72,8 +72,9 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
 import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton;
-import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.CompositorOnClickHandler;
+import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.ButtonType;
 import org.chromium.chrome.browser.compositor.layouts.components.TintedCompositorButton;
+import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutView.StripLayoutViewOnClickHandler;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.animation.CompositorAnimationHandler;
 import org.chromium.chrome.browser.layouts.components.VirtualView;
@@ -123,7 +124,7 @@
     @Mock private View mToolbarContainerView;
     @Mock private StripTabHoverCardView mTabHoverCardView;
     @Mock private Profile mProfile;
-    @Mock private CompositorOnClickHandler mClickHandler;
+    @Mock private StripLayoutViewOnClickHandler mClickHandler;
     @Mock private TabDragSource mTabDragSource;
     @Mock private WindowAndroid mWindowAndroid;
     @Mock private LayerTitleCache mLayerTitleCache;
@@ -1241,13 +1242,20 @@
         // Setup
         initializeTest(false, false, 2);
         StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
+        StripLayoutTab tab = spy(tabs[0]);
         TintedCompositorButton closeButton =
-                new TintedCompositorButton(mContext, 24.f, 24.f, mClickHandler);
+                new TintedCompositorButton(
+                        mContext,
+                        ButtonType.TAB_CLOSE,
+                        tab,
+                        24.f,
+                        24.f,
+                        mClickHandler,
+                        R.drawable.btn_tab_close_normal);
         closeButton.setOpacity(1.f);
         int x = (int) closeButton.getDrawX();
         int y = (int) closeButton.getDrawY();
         StripLayoutHelper stripLayoutHelper = spy(mStripLayoutHelper);
-        StripLayoutTab tab = spy(tabs[0]);
         when(stripLayoutHelper.getTabAtPosition(x)).thenReturn(tab);
         stripLayoutHelper.setTabAtPositionForTesting(tab);
         tab.setCloseButtonForTesting(closeButton);
@@ -1274,7 +1282,14 @@
         initializeTest(false, false, 2);
         StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
         TintedCompositorButton closeButton =
-                new TintedCompositorButton(mContext, 24.f, 24.f, mClickHandler);
+                new TintedCompositorButton(
+                        mContext,
+                        ButtonType.TAB_CLOSE,
+                        tabs[0],
+                        24.f,
+                        24.f,
+                        mClickHandler,
+                        R.drawable.btn_tab_close_normal);
         closeButton.setOpacity(1.f);
         int x = (int) closeButton.getDrawX();
         int y = (int) closeButton.getDrawY();
@@ -4591,7 +4606,7 @@
 
         // Fake a click on the group indicator.
         StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
-        mStripLayoutHelper.handleGroupTitleClick((StripLayoutGroupTitle) views[0]);
+        mStripLayoutHelper.onClick(TIMESTAMP, views[0]);
 
         // Verify the proper event was sent to the TabGroupModelFilter.
         verify(mTabGroupModelFilter).setTabGroupCollapsed(0, true);
@@ -4618,7 +4633,7 @@
         StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
         mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], true);
         when(mTabGroupModelFilter.getTabGroupCollapsed(0)).thenReturn(true);
-        mStripLayoutHelper.handleGroupTitleClick((StripLayoutGroupTitle) views[0]);
+        mStripLayoutHelper.onClick(TIMESTAMP, views[0]);
 
         // Verify the proper event was sent to the TabGroupModelFilter.
         verify(mTabGroupModelFilter).setTabGroupCollapsed(0, false);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayerTest.java
index 31d30dd0..a0dc8e1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayerTest.java
@@ -32,11 +32,11 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
 import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton;
-import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.CompositorOnClickHandler;
+import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.ButtonType;
 import org.chromium.chrome.browser.compositor.layouts.components.TintedCompositorButton;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelperManager;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutTab;
-import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutTab.StripLayoutTabDelegate;
+import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutView.StripLayoutViewOnClickHandler;
 import org.chromium.chrome.browser.compositor.overlays.strip.TabLoadTracker.TabLoadTrackerCallback;
 import org.chromium.chrome.browser.compositor.scene_layer.TabStripSceneLayer;
 import org.chromium.chrome.browser.compositor.scene_layer.TabStripSceneLayerJni;
@@ -53,8 +53,7 @@
     @Mock private ResourceManager mResourceManager;
     @Mock private LayerTitleCache mLayerTitleCache;
     @Mock private SceneLayer mSceneLayer;
-    @Mock private CompositorOnClickHandler mCompositorOnClickHandler;
-    @Mock private StripLayoutTabDelegate mStripLayoutTabDelegate;
+    @Mock private StripLayoutViewOnClickHandler mOnClickHandler;
     @Mock private TabLoadTrackerCallback mTabLoadTrackerCallback;
     @Mock private LayoutRenderHost mLayoutRenderHost;
     @Mock private LayoutUpdateHost mLayoutUpdateHost;
@@ -92,19 +91,27 @@
         when(mTabStripSceneMock.init(mTabStripSceneLayer)).thenReturn(1L);
         mModelSelectorButton =
                 new TintedCompositorButton(
-                        mContext, 32.f, 32.f, mCompositorOnClickHandler, R.drawable.ic_incognito);
+                        mContext,
+                        ButtonType.INCOGNITO_SWITCHER,
+                        null,
+                        32.f,
+                        32.f,
+                        mOnClickHandler,
+                        R.drawable.ic_incognito);
         mNewTabButton =
                 new TintedCompositorButton(
                         mContext,
+                        ButtonType.NEW_TAB,
+                        null,
                         32.f,
                         32.f,
-                        mCompositorOnClickHandler,
+                        mOnClickHandler,
                         R.drawable.ic_new_tab_button);
         mStripLayoutTab =
                 new StripLayoutTab(
                         mContext,
                         1,
-                        mStripLayoutTabDelegate,
+                        mOnClickHandler,
                         mTabLoadTrackerCallback,
                         mLayoutUpdateHost,
                         false);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java
index aa3a3ca..6130b3d3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java
@@ -36,6 +36,8 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.customtabs.CustomTabAuthUrlHeuristics;
+import org.chromium.chrome.browser.customtabs.CustomTabAuthUrlHeuristicsJni;
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -78,11 +80,13 @@
     @Rule public JniMocker mocker = new JniMocker();
 
     @Mock UrlUtilities.Natives mUrlUtilitiesJniMock;
+    @Mock CustomTabAuthUrlHeuristics.Natives mCustomTabAuthUrlHeuristicsJniMock;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mocker.mock(UrlUtilitiesJni.TEST_HOOKS, mUrlUtilitiesJniMock);
+        mocker.mock(CustomTabAuthUrlHeuristicsJni.TEST_HOOKS, mCustomTabAuthUrlHeuristicsJniMock);
 
         when(env.profileProvider.getOriginalProfile()).thenReturn(mProfile);
         when(env.profileProvider.getOffTheRecordProfile(eq(true))).thenReturn(mIncognitoProfile);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorControllerUnitTest.java
index a4ceefc..90ed83c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorControllerUnitTest.java
@@ -87,6 +87,7 @@
     @Captor private ArgumentCaptor<Integer> mWindowDividerColorCaptor;
     @Captor private ArgumentCaptor<Integer> mNavigationBarColorChangedCaptor;
     @Captor private ArgumentCaptor<Integer> mNavigationBarDividerColorChangedCaptor;
+    @Captor private ArgumentCaptor<Integer> mRootViewSystemVisibility;
 
     @Before
     public void setUp() {
@@ -129,6 +130,7 @@
         doNothing()
                 .when(mObserver)
                 .onNavigationBarDividerChanged(mNavigationBarDividerColorChangedCaptor.capture());
+        doNothing().when(mRootView).setSystemUiVisibility(mRootViewSystemVisibility.capture());
     }
 
     @Test
@@ -287,6 +289,37 @@
         verify(mWindow, times(0)).setNavigationBarDividerColor(anyInt());
     }
 
+    @Test
+    public void testSetNavigationBarScrimFraction() {
+        when(mTab.getBackgroundColor()).thenReturn(Color.LTGRAY);
+        when(mLayoutManager.getActiveLayoutType()).thenReturn(LayoutType.BROWSING);
+        when(mEdgeToEdgeController.getBottomInset()).thenReturn(0);
+        when(mEdgeToEdgeController.isDrawingToEdge()).thenReturn(false);
+        when(mWindow.getNavigationBarColor()).thenReturn(Color.RED);
+
+        Mockito.clearInvocations(mWindow);
+        mNavColorController.updateActiveTabForTesting();
+        runColorUpdateAnimation();
+        assertWindowNavBarColor(Color.LTGRAY);
+
+        mNavColorController.setNavigationBarScrimFraction(1.0f);
+        // Light gray + the default scrim color overlay.
+        assertWindowNavBarColor(0xFF474747);
+        assertEquals(
+                "The navigation bar icons should not be using dark icons for a dark navigation"
+                        + " bar.",
+                0,
+                (mRootViewSystemVisibility.getValue() & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR));
+
+        mNavColorController.setNavigationBarScrimFraction(0.0f);
+        assertWindowNavBarColor(Color.LTGRAY);
+        assertEquals(
+                "The navigation bar icons should be using dark icons for the light navigation"
+                        + " bar.",
+                View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                (mRootViewSystemVisibility.getValue() & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR));
+    }
+
     private void runColorUpdateAnimation() {
         // Run the color  transition animation so color is applied to the window.
         ShadowLooper.idleMainLooper();
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3ac2738..9efa86d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -11589,6 +11589,9 @@
       <message name="IDS_TAB_ORGANIZATION_THUMBS_UP" desc="Accessibility label for the thumbs up icon that a user can click to provide positive feedback about a tab organization." is_accessibility_with_no_ui="true">
         Thumbs up submits feedback that you like this tab group suggestion
       </message>
+      <message name="IDS_DECLUTTER_TITLE" desc="The header text for the declutter UI">
+        Close unused tabs
+      </message>
 
       <!-- Strings for Window Titles in Menus -->
       <if expr="not use_titlecase">
diff --git a/chrome/app/generated_resources_grd/IDS_DECLUTTER_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_DECLUTTER_TITLE.png.sha1
new file mode 100644
index 0000000..126308a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DECLUTTER_TITLE.png.sha1
@@ -0,0 +1 @@
+329365f66510d40ae41f2e87b41801307aa4dc19
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c742476..238f1de 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1987,7 +1987,6 @@
     "//chrome/browser/ui/blocked_content:impl",
     "//chrome/browser/ui/bluetooth",
     "//chrome/browser/ui/bluetooth:impl",
-    "//chrome/browser/ui/browser_window",
     "//chrome/browser/ui/color:color_headers",
     "//chrome/browser/ui/color:mixers",
     "//chrome/browser/ui/cookie_controls",
@@ -2645,6 +2644,7 @@
       "android/customtabs/chrome_origin_verifier.h",
       "android/customtabs/client_data_header_web_contents_observer.cc",
       "android/customtabs/client_data_header_web_contents_observer.h",
+      "android/customtabs/custom_tab_auth_url_heuristics.cc",
       "android/customtabs/custom_tab_session_state_tracker.cc",
       "android/customtabs/custom_tab_session_state_tracker.h",
       "android/customtabs/custom_tabs_connection.cc",
@@ -4277,7 +4277,10 @@
       "//chrome/browser/ui/actions:actions_headers",
       "//chrome/browser/ui/apps",
       "//chrome/browser/ui/apps:impl",
+      "//chrome/browser/ui/browser_window",
       "//chrome/browser/ui/color:color_headers",
+      "//chrome/browser/ui/commerce",
+      "//chrome/browser/ui/commerce:impl",
       "//chrome/browser/ui/exclusive_access",
       "//chrome/browser/ui/lens",
       "//chrome/browser/ui/page_action:icon_type",
@@ -4401,6 +4404,7 @@
       "//chrome/browser/ui/views/toolbar",
       "//chrome/browser/ui/webui/searchbox",
       "//chrome/browser/ui/webui/top_chrome:impl",
+      "//chrome/browser/ui/commerce:impl",
     ]
 
     #!is_android
@@ -5240,6 +5244,7 @@
       "//chrome/browser/ui/webui/ash/skyvault",
       "//chrome/browser/ui/webui/ash/smb_shares",
       "//chrome/browser/ui/webui/ash/vm",
+      "//chrome/browser/ui/webui/signin/ash",
       "//chrome/services/sharing/public/cpp",
       "//chrome/services/sharing/public/proto",
       "//chrome/services/speech:lib",
@@ -5649,6 +5654,7 @@
       "//chrome/browser/ui/webui/ash/settings/pages/storage",
       "//chrome/browser/ui/webui/ash/settings/services/metrics",
       "//chrome/browser/ui/webui/ash/settings/services/settings_manager",
+      "//chrome/browser/ui/webui/signin/ash",
     ]
 
     if (is_chrome_branded) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ebc29ad..4c1f271 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2534,19 +2534,6 @@
      nullptr},
 };
 
-const FeatureEntry::FeatureParam kFeedCloseRefresh_Open[] = {
-    {"require_interaction", "false"}};
-
-const FeatureEntry::FeatureParam kFeedCloseRefresh_Interact[] = {
-    {"require_interaction", "true"}};
-
-const FeatureEntry::FeatureVariation kFeedCloseRefreshVariations[] = {
-    {"Open", kFeedCloseRefresh_Open, std::size(kFeedCloseRefresh_Open),
-     nullptr},
-    {"Interact", kFeedCloseRefresh_Interact,
-     std::size(kFeedCloseRefresh_Interact), nullptr},
-};
-
 #endif  // BUILDFLAG(IS_ANDROID)
 
 const FeatureEntry::Choice kNotificationSchedulerChoices[] = {
@@ -3472,14 +3459,14 @@
      switches::kCastMirroringTargetPlayoutDelay, "100"},
     {flag_descriptions::kCastMirroringTargetPlayoutDelay150ms,
      switches::kCastMirroringTargetPlayoutDelay, "150"},
-    {flag_descriptions::kCastMirroringTargetPlayoutDelay200ms,
-     switches::kCastMirroringTargetPlayoutDelay, "200"},
     {flag_descriptions::kCastMirroringTargetPlayoutDelay250ms,
      switches::kCastMirroringTargetPlayoutDelay, "250"},
     {flag_descriptions::kCastMirroringTargetPlayoutDelay300ms,
      switches::kCastMirroringTargetPlayoutDelay, "300"},
     {flag_descriptions::kCastMirroringTargetPlayoutDelay350ms,
-     switches::kCastMirroringTargetPlayoutDelay, "350"}};
+     switches::kCastMirroringTargetPlayoutDelay, "350"},
+    {flag_descriptions::kCastMirroringTargetPlayoutDelay400ms,
+     switches::kCastMirroringTargetPlayoutDelay, "400"}};
 
 #endif  // !BUILDFLAG(IS_ANDROID)
 
@@ -5662,11 +5649,6 @@
      flag_descriptions::kXsurfaceMetricsReportingName,
      flag_descriptions::kXsurfaceMetricsReportingDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(feed::kXsurfaceMetricsReporting)},
-    {"feed-close-refresh", flag_descriptions::kFeedCloseRefreshName,
-     flag_descriptions::kFeedCloseRefreshDescription, kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(feed::kFeedCloseRefresh,
-                                    kFeedCloseRefreshVariations,
-                                    "FeedCloseRefresh")},
     {"feed-containment", flag_descriptions::kFeedContainmentName,
      flag_descriptions::kFeedContainmentDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(feed::kFeedContainment)},
diff --git a/chrome/browser/android/customtabs/custom_tab_auth_url_heuristics.cc b/chrome/browser/android/customtabs/custom_tab_auth_url_heuristics.cc
new file mode 100644
index 0000000..19f3673
--- /dev/null
+++ b/chrome/browser/android/customtabs/custom_tab_auth_url_heuristics.cc
@@ -0,0 +1,22 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_android.h"
+#include "chrome/android/chrome_jni_headers/CustomTabAuthUrlHeuristics_jni.h"
+#include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/password_manager/android/first_cct_page_load_marker.h"
+
+using base::android::JavaParamRef;
+
+static void JNI_CustomTabAuthUrlHeuristics_SetFirstCctPageLoadForPasswords(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jtab) {
+  TabAndroid* tab = TabAndroid::GetNativeTab(env, jtab);
+
+  if (!tab || !tab->web_contents()) {
+    return;
+  }
+
+  FirstCctPageLoadMarker::CreateForWebContents(tab->web_contents());
+}
diff --git a/chrome/browser/android/recently_closed_tabs_bridge.cc b/chrome/browser/android/recently_closed_tabs_bridge.cc
index f577df7..41116ed3 100644
--- a/chrome/browser/android/recently_closed_tabs_bridge.cc
+++ b/chrome/browser/android/recently_closed_tabs_bridge.cc
@@ -93,7 +93,7 @@
   Java_RecentlyClosedBridge_addGroupToEntries(
       env, jentries, group.id.id(),
       group.timestamp.InMillisecondsSinceUnixEpoch(), group.visual_data.title(),
-      tabs);
+      static_cast<int>(group.visual_data.color()), tabs);
 }
 
 void AddBulkEventToEntries(
diff --git a/chrome/browser/apps/almanac_api_client/BUILD.gn b/chrome/browser/apps/almanac_api_client/BUILD.gn
index 4c051d9..0b18413 100644
--- a/chrome/browser/apps/almanac_api_client/BUILD.gn
+++ b/chrome/browser/apps/almanac_api_client/BUILD.gn
@@ -16,6 +16,8 @@
     "almanac_icon_cache.h",
     "device_info_manager.cc",
     "device_info_manager.h",
+    "device_info_manager_factory.cc",
+    "device_info_manager_factory.h",
     "proto_file_manager.cc",
     "proto_file_manager.h",
   ]
diff --git a/chrome/browser/apps/almanac_api_client/device_info_manager.h b/chrome/browser/apps/almanac_api_client/device_info_manager.h
index 4fea68f..4245f5c1 100644
--- a/chrome/browser/apps/almanac_api_client/device_info_manager.h
+++ b/chrome/browser/apps/almanac_api_client/device_info_manager.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/system/sys_info.h"
 #include "chrome/browser/apps/almanac_api_client/proto/client_context.pb.h"
+#include "components/keyed_service/core/keyed_service.h"
 #include "components/version_info/channel.h"
 
 class Profile;
@@ -86,12 +87,14 @@
 
 // Fetches information about the device the code is currently running on, used
 // to populate the device context for requests to the Almanac API server.
-class DeviceInfoManager {
+class DeviceInfoManager : public KeyedService {
  public:
+  // Prefer using DeviceInfoManagerFactory to access a shared Manager instance.
+  // TODO(b/364107415): Migrate all usage to the KeyedService.
   explicit DeviceInfoManager(Profile* profile);
   DeviceInfoManager(const DeviceInfoManager&) = delete;
   DeviceInfoManager& operator=(const DeviceInfoManager&) = delete;
-  ~DeviceInfoManager();
+  ~DeviceInfoManager() override;
 
   // Asynchronously fetches device information. Must be called from the UI
   // thread. The fetched DeviceInfo is cached inside this DeviceInfoManager, so
diff --git a/chrome/browser/apps/almanac_api_client/device_info_manager_factory.cc b/chrome/browser/apps/almanac_api_client/device_info_manager_factory.cc
new file mode 100644
index 0000000..1ec6dd4c
--- /dev/null
+++ b/chrome/browser/apps/almanac_api_client/device_info_manager_factory.cc
@@ -0,0 +1,43 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/almanac_api_client/device_info_manager_factory.h"
+
+#include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+#include "chrome/browser/profiles/profile_selections.h"
+
+namespace apps {
+
+DeviceInfoManager* DeviceInfoManagerFactory::GetForProfile(Profile* profile) {
+  return static_cast<DeviceInfoManager*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+DeviceInfoManagerFactory* DeviceInfoManagerFactory::GetInstance() {
+  static base::NoDestructor<DeviceInfoManagerFactory> instance;
+  return instance.get();
+}
+
+DeviceInfoManagerFactory::DeviceInfoManagerFactory()
+    : ProfileKeyedServiceFactory(
+          "DeviceInfoManager",
+          ProfileSelections::Builder()
+              .WithRegular(ProfileSelection::kOriginalOnly)
+              .WithGuest(ProfileSelection::kOwnInstance)
+              .WithSystem(ProfileSelection::kNone)
+              .WithAshInternals(ProfileSelection::kNone)
+              .Build()) {}
+
+DeviceInfoManagerFactory::~DeviceInfoManagerFactory() = default;
+
+std::unique_ptr<KeyedService>
+DeviceInfoManagerFactory::BuildServiceInstanceForBrowserContext(
+    content::BrowserContext* context) const {
+  return std::make_unique<DeviceInfoManager>(
+      Profile::FromBrowserContext(context));
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/almanac_api_client/device_info_manager_factory.h b/chrome/browser/apps/almanac_api_client/device_info_manager_factory.h
new file mode 100644
index 0000000..f71a473c
--- /dev/null
+++ b/chrome/browser/apps/almanac_api_client/device_info_manager_factory.h
@@ -0,0 +1,41 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_ALMANAC_API_CLIENT_DEVICE_INFO_MANAGER_FACTORY_H_
+#define CHROME_BROWSER_APPS_ALMANAC_API_CLIENT_DEVICE_INFO_MANAGER_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+
+namespace apps {
+
+class DeviceInfoManager;
+
+// Factory for DeviceInfoManager instances, keyed by Profile.
+class DeviceInfoManagerFactory : public ProfileKeyedServiceFactory {
+ public:
+  DeviceInfoManagerFactory(const DeviceInfoManagerFactory&) = delete;
+  DeviceInfoManagerFactory& operator=(const DeviceInfoManagerFactory&) = delete;
+
+  // Retrieve the shared DeviceInfoManager instance for the given profile.
+  // Returns nullptr if the service is not supported for the profile (e.g.
+  // Incognito).
+  static DeviceInfoManager* GetForProfile(Profile* profile);
+
+  static DeviceInfoManagerFactory* GetInstance();
+
+ private:
+  friend base::NoDestructor<DeviceInfoManagerFactory>;
+
+  DeviceInfoManagerFactory();
+  ~DeviceInfoManagerFactory() override;
+
+  // ProfileKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+      content::BrowserContext* context) const override;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_ALMANAC_API_CLIENT_DEVICE_INFO_MANAGER_FACTORY_H_
diff --git a/chrome/browser/apps/app_service/app_install/app_install_discovery_metrics.cc b/chrome/browser/apps/app_service/app_install/app_install_discovery_metrics.cc
index 1defc03..854a51b 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_discovery_metrics.cc
+++ b/chrome/browser/apps/app_service/app_install/app_install_discovery_metrics.cc
@@ -34,6 +34,8 @@
       return cros_events::AppInstallSurface::APP_INSTALL_URI_SHOWOFF;
     case AppInstallSurface::kAppInstallUriMall:
       return cros_events::AppInstallSurface::APP_INSTALL_URI_MALL;
+    case AppInstallSurface::kAppInstallUriMallV2:
+      return cros_events::AppInstallSurface::APP_INSTALL_URI_MALL_V2;
     case AppInstallSurface::kAppInstallUriGetit:
       return cros_events::AppInstallSurface::APP_INSTALL_URI_GETIT;
     case AppInstallSurface::kAppInstallUriLauncher:
diff --git a/chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.cc b/chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.cc
index 42b3ca63..a5a53b7 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.cc
+++ b/chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.cc
@@ -62,6 +62,9 @@
   if (base::EqualsCaseInsensitiveASCII(source, "mall")) {
     return AppInstallSurface::kAppInstallUriMall;
   }
+  if (base::EqualsCaseInsensitiveASCII(source, "mallv2")) {
+    return AppInstallSurface::kAppInstallUriMallV2;
+  }
   if (base::EqualsCaseInsensitiveASCII(source, "getit")) {
     return AppInstallSurface::kAppInstallUriGetit;
   }
diff --git a/chrome/browser/apps/app_service/app_install/app_install_navigation_throttle_unittest.cc b/chrome/browser/apps/app_service/app_install/app_install_navigation_throttle_unittest.cc
index b011680..44fe018 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_navigation_throttle_unittest.cc
+++ b/chrome/browser/apps/app_service/app_install/app_install_navigation_throttle_unittest.cc
@@ -118,6 +118,10 @@
       QueryParams("web:identifier", AppInstallSurface::kAppInstallUriLauncher));
 
   EXPECT_EQ(
+      ExtractQueryParams("package_id=web:identifier&source=mallv2"),
+      QueryParams("web:identifier", AppInstallSurface::kAppInstallUriMallV2));
+
+  EXPECT_EQ(
       ExtractQueryParams("source=mall&package_id=web:identifier"),
       QueryParams("web:identifier", AppInstallSurface::kAppInstallUriMall));
 
diff --git a/chrome/browser/apps/app_service/app_install/app_install_service_lacros.cc b/chrome/browser/apps/app_service/app_install/app_install_service_lacros.cc
index b8d8f02..fe04ab0 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_service_lacros.cc
+++ b/chrome/browser/apps/app_service/app_install/app_install_service_lacros.cc
@@ -32,6 +32,7 @@
       case AppInstallSurface::kAppInstallUriShowoff:
         return Surface::kAppInstallUriShowoff;
       case AppInstallSurface::kAppInstallUriMall:
+      case AppInstallSurface::kAppInstallUriMallV2:
         return Surface::kAppInstallUriMall;
       case AppInstallSurface::kAppInstallUriGetit:
         return Surface::kAppInstallUriGetit;
diff --git a/chrome/browser/apps/app_service/app_install/app_install_types.cc b/chrome/browser/apps/app_service/app_install/app_install_types.cc
index ca8fff072..5fa210d 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_types.cc
+++ b/chrome/browser/apps/app_service/app_install/app_install_types.cc
@@ -24,6 +24,8 @@
       return out << "AppInstallUriShowoff";
     case AppInstallSurface::kAppInstallUriMall:
       return out << "AppInstallUriMall";
+    case AppInstallSurface::kAppInstallUriMallV2:
+      return out << "AppInstallUriMallV2";
     case AppInstallSurface::kAppInstallUriGetit:
       return out << "AppInstallUriGetit";
     case AppInstallSurface::kAppInstallUriLauncher:
diff --git a/chrome/browser/apps/app_service/app_install/app_install_types.h b/chrome/browser/apps/app_service/app_install/app_install_types.h
index 123c156c..dbd47b9 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_types.h
+++ b/chrome/browser/apps/app_service/app_install/app_install_types.h
@@ -29,6 +29,7 @@
   kAppInstallUriUnknown,
   kAppInstallUriShowoff,
   kAppInstallUriMall,
+  kAppInstallUriMallV2,
   kAppInstallUriGetit,
   kAppInstallUriLauncher,
   kAppInstallUriPeripherals,
diff --git a/chrome/browser/apps/app_service/app_install/web_app_installer.cc b/chrome/browser/apps/app_service/app_install/web_app_installer.cc
index e0044eb..19f6e120 100644
--- a/chrome/browser/apps/app_service/app_install/web_app_installer.cc
+++ b/chrome/browser/apps/app_service/app_install/web_app_installer.cc
@@ -202,6 +202,7 @@
         case AppInstallSurface::kAppInstallUriUnknown:
         case AppInstallSurface::kAppInstallUriShowoff:
         case AppInstallSurface::kAppInstallUriMall:
+        case AppInstallSurface::kAppInstallUriMallV2:
         case AppInstallSurface::kAppInstallUriGetit:
         case AppInstallSurface::kAppInstallUriLauncher:
         case AppInstallSurface::kAppInstallUriPeripherals:
@@ -228,6 +229,7 @@
         case AppInstallSurface::kAppInstallUriUnknown:
         case AppInstallSurface::kAppInstallUriShowoff:
         case AppInstallSurface::kAppInstallUriMall:
+        case AppInstallSurface::kAppInstallUriMallV2:
         case AppInstallSurface::kAppInstallUriGetit:
         case AppInstallSurface::kAppInstallUriLauncher:
         case AppInstallSurface::kAppInstallUriPeripherals:
diff --git a/chrome/browser/ash/arc/BUILD.gn b/chrome/browser/ash/arc/BUILD.gn
index e8b691ea..b83270a 100644
--- a/chrome/browser/ash/arc/BUILD.gn
+++ b/chrome/browser/ash/arc/BUILD.gn
@@ -67,6 +67,7 @@
     "//chrome/browser/ash/fileapi",
     "//chrome/browser/ash/guest_os",
     "//chrome/browser/ash/guest_os/public",
+    "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/platform_keys/key_permissions",
     "//chrome/browser/ash/profiles",
@@ -74,7 +75,6 @@
     "//chrome/browser/image_decoder",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/resources:component_extension_resources_grit",
-    "//chrome/browser/ui/webui/ash",
     "//chrome/browser/ui/webui/ash/diagnostics_dialog",
     "//chrome/common",
     "//chromeos/ash/components/browser_context_helper",
diff --git a/chrome/browser/ash/arc/fileapi/BUILD.gn b/chrome/browser/ash/arc/fileapi/BUILD.gn
index ba0f63f..465c164 100644
--- a/chrome/browser/ash/arc/fileapi/BUILD.gn
+++ b/chrome/browser/ash/arc/fileapi/BUILD.gn
@@ -67,11 +67,15 @@
     "//ash/constants",
     "//base",
     "//chrome/browser/ash/drive",
+    "//chrome/browser/ash/file_manager",
     "//chrome/browser/ash/fileapi",
+    "//chrome/browser/ash/fusebox",
+    "//chrome/browser/ash/guest_os",
     "//chrome/browser/ash/policy/dlp",
     "//chrome/browser/chromeos/policy/dlp",
     "//chrome/browser/profiles:profile",
     "//chrome/common",
+    "//chromeos/ash/components/dbus/concierge:concierge_proto",
     "//chromeos/ash/components/dbus/virtual_file_provider",
     "//content/public/browser",
     "//extensions/browser/api/file_handlers",
@@ -86,7 +90,10 @@
     "//chrome/browser/apps/app_preload_service/proto",
   ]
 
-  allow_circular_includes_from = [ "//chrome/browser/ash/fileapi" ]
+  allow_circular_includes_from = [
+    "//chrome/browser/ash/file_manager",
+    "//chrome/browser/ash/fileapi",
+  ]
 }
 
 source_set("unit_tests") {
@@ -118,7 +125,11 @@
     "//chrome/browser/ash/file_system_provider",
     "//chrome/browser/ash/file_system_provider:test_support",
     "//chrome/browser/ash/fileapi",
+    "//chrome/browser/ash/fusebox",
+    "//chrome/browser/ash/guest_os",
+    "//chrome/browser/ash/guest_os/public",
     "//chrome/test:test_support",
+    "//chromeos/ash/components/dbus/seneschal",
     "//chromeos/ash/components/dbus/virtual_file_provider",
     "//components/keyed_service/core",
     "//content/test:test_support",
diff --git a/chrome/browser/ash/arc/fileapi/DEPS b/chrome/browser/ash/arc/fileapi/DEPS
index ec9ed33..b5b6a0b 100644
--- a/chrome/browser/ash/arc/fileapi/DEPS
+++ b/chrome/browser/ash/arc/fileapi/DEPS
@@ -19,8 +19,11 @@
   "+chrome/browser/ash/fileapi",
   "+chrome/browser/ash/file_manager",
   "+chrome/browser/ash/file_system_provider",
+  "+chrome/browser/ash/fusebox",
+  "+chrome/browser/ash/guest_os",
   "+chrome/browser/ash/policy/dlp",
   "+chrome/browser/chromeos/policy/dlp",
+  "+chrome/browser/ash/login/users",
   "+chrome/browser/profiles",
   "+chrome/browser/ui/ash/shelf",
   "+chrome/browser/ui/chrome_select_file_policy.h",
diff --git a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.cc b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.cc
index 49d157e..49730f5 100644
--- a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.cc
+++ b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.cc
@@ -13,10 +13,12 @@
 #include <vector>
 
 #include "ash/components/arc/arc_browser_context_keyed_service_factory_base.h"
+#include "ash/components/arc/arc_util.h"
 #include "ash/components/arc/session/arc_bridge_service.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
+#include "base/not_fatal_until.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/strings/escape.h"
 #include "base/system/sys_info.h"
@@ -31,8 +33,13 @@
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/ash/fileapi/external_file_url_util.h"
+#include "chrome/browser/ash/fusebox/fusebox_moniker.h"
+#include "chrome/browser/ash/fusebox/fusebox_server.h"
+#include "chrome/browser/ash/guest_os/guest_os_session_tracker.h"
+#include "chrome/browser/ash/guest_os/guest_os_share_path.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/ash/components/dbus/virtual_file_provider/virtual_file_provider_client.h"
+#include "chromeos/ash/components/dbus/vm_concierge/concierge_service.pb.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
@@ -53,6 +60,12 @@
 
 namespace {
 
+// The maximum number of Fusebox Monikers that can be shared concurrently.
+// When this number is reached, CreateMoniker() does not create a new Moniker,
+// and returns a null response. A large number is randomly chosen to not block
+// usual user flows.
+constexpr size_t kMaxNumberOfSharedMonikers = 16384;
+
 // Returns true if it's OK to allow ARC apps to read the given URL.
 bool IsUrlAllowed(const GURL& url) {
   // Currently, only externalfile URLs are allowed.
@@ -423,6 +436,104 @@
     observer.OnMediaStoreUriAdded(uri, *metadata);
 }
 
+void ArcFileSystemBridge::CreateMoniker(const GURL& content_uri,
+                                        bool read_only,
+                                        CreateMonikerCallback callback) {
+  CHECK_CURRENTLY_ON(content::BrowserThread::UI, base::NotFatalUntil::M132);
+
+  if (shared_monikers_.size() >= kMaxNumberOfSharedMonikers) {
+    // Avoid creating too many Monikers without closing.
+    LOG(WARNING) << "Rejecting to create a Fusebox Moniker for " << content_uri
+                 << " because the maximum number of shared Monikers ("
+                 << kMaxNumberOfSharedMonikers << ") is reached";
+    std::move(callback).Run(std::nullopt);
+    return;
+  }
+
+  const GURL url_decoded = DecodeFromChromeContentProviderUrl(content_uri);
+  if (url_decoded.is_empty() || !IsUrlAllowed(url_decoded)) {
+    LOG(ERROR) << "Invalid ChromeContentProvider URI: " << content_uri;
+    std::move(callback).Run(std::nullopt);
+    return;
+  }
+
+  scoped_refptr<storage::FileSystemContext> context =
+      GetFileSystemContext(profile_);
+  const file_manager::util::FileSystemURLAndHandle fs_url_and_handle =
+      GetFileSystemURL(*context, url_decoded);
+  if (!fs_url_and_handle.url.is_valid()) {
+    LOG(ERROR) << "Failed to get FileSystemURL for URL: " << url_decoded;
+    std::move(callback).Run(std::nullopt);
+    return;
+  }
+
+  const auto& vm_info =
+      guest_os::GuestOsSessionTracker::GetForProfile(profile_)->GetVmInfo(
+          kArcVmName);
+  if (!vm_info) {
+    LOG(ERROR) << "ARCVM is not running";
+    std::move(callback).Run(std::nullopt);
+    return;
+  }
+
+  auto* fusebox_server = fusebox::Server::GetInstance();
+  if (!fusebox_server) {
+    LOG(ERROR) << "Failed to get Fusebox server";
+    std::move(callback).Run(std::nullopt);
+    return;
+  }
+  const fusebox::Moniker moniker =
+      fusebox_server->CreateMoniker(fs_url_and_handle.url, read_only);
+  shared_monikers_.insert(moniker);
+
+  guest_os::GuestOsSharePath::GetForProfile(profile_)->SharePath(
+      kArcVmName, vm_info->seneschal_server_handle(),
+      base::FilePath(fusebox::MonikerMap::GetFilename(moniker)),
+      base::BindOnce(&ArcFileSystemBridge::OnShareMonikerPath,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     moniker));
+}
+
+void ArcFileSystemBridge::OnShareMonikerPath(
+    CreateMonikerCallback callback,
+    const fusebox::Moniker& moniker,
+    const base::FilePath& path,
+    bool success,
+    const std::string& failure_reason) {
+  if (!success) {
+    LOG(ERROR) << "Failed to share Fusebox Moniker path with ARCVM: reason: "
+               << failure_reason << ", path: " << path;
+    std::move(callback).Run(std::nullopt);
+    DestroyMoniker(moniker, base::DoNothing());
+    return;
+  }
+  std::move(callback).Run(moniker);
+}
+
+void ArcFileSystemBridge::DestroyMoniker(const fusebox::Moniker& moniker,
+                                         DestroyMonikerCallback callback) {
+  CHECK_CURRENTLY_ON(content::BrowserThread::UI, base::NotFatalUntil::M132);
+
+  if (!shared_monikers_.erase(moniker)) {
+    LOG(ERROR) << "Failed to destroy unknown Fusebox Moniker: "
+               << moniker.ToString();
+    std::move(callback).Run(false);
+    return;
+  }
+
+  auto* fusebox_server = fusebox::Server::GetInstance();
+  if (fusebox_server) {
+    fusebox_server->DestroyMoniker(moniker);
+  } else {
+    LOG(ERROR) << "Failed to get Fusebox server";
+    // Not returning false, since the Moniker and the resources held by the
+    // Fusebox server should have already gone.
+  }
+  // No need to call GuestOsSharePath::UnsharePath(), because the method is
+  // eventually called from GuestOsSharePath::PathDeleted().
+  std::move(callback).Run(true);
+}
+
 void ArcFileSystemBridge::GenerateVirtualFileId(
     const GURL& url_decoded,
     GenerateVirtualFileIdCallback callback,
diff --git a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.h b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.h
index 14908c3..e07599e 100644
--- a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.h
+++ b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge.h
@@ -10,16 +10,19 @@
 #include <list>
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
 
 #include "ash/components/arc/mojom/file_system.mojom-forward.h"
 #include "ash/components/arc/session/connection_observer.h"
+#include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "chrome/browser/ash/arc/fileapi/arc_select_files_handler.h"
 #include "chrome/browser/ash/arc/fileapi/file_stream_forwarder.h"
+#include "chrome/browser/ash/fusebox/fusebox_moniker.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "storage/browser/file_system/file_system_operation.h"
 #include "storage/browser/file_system/watcher_manager.h"
@@ -122,6 +125,11 @@
       GetFileSelectorElementsCallback callback) override;
   void OnMediaStoreUriAdded(const GURL& uri,
                             mojom::MediaStoreMetadataPtr metadata) override;
+  void CreateMoniker(const GURL& content_uri,
+                     bool read_only,
+                     CreateMonikerCallback callback) override;
+  void DestroyMoniker(const fusebox::Moniker& moniker,
+                      DestroyMonikerCallback callback) override;
 
   // ConnectionObserver<mojom::FileSystemInstance> overrides:
   void OnConnectionClosed() override;
@@ -169,6 +177,14 @@
                       const std::string& id,
                       base::ScopedFD fd);
 
+  // Called from CreateMoniker() after sharing the new Moniker's path with
+  // ARCVM.
+  void OnShareMonikerPath(CreateMonikerCallback callback,
+                          const fusebox::Moniker& moniker,
+                          const base::FilePath& path,
+                          bool success,
+                          const std::string& failure_reason);
+
   // Used to implement OpenFileToRead(), needs to be testable.
   //
   // Decode a percent-encoded externalfile: URL to an absolute path on
@@ -204,6 +220,9 @@
   // Map from file descriptor IDs to requested URLs.
   std::map<std::string, GURL> id_to_url_;
 
+  // Set of Fusebox Monikers currently shared with ARC.
+  std::set<fusebox::Moniker> shared_monikers_;
+
   std::list<FileStreamForwarderPtr> file_stream_forwarders_;
 
   std::unique_ptr<ArcSelectFilesHandlersManager> select_files_handlers_manager_;
diff --git a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge_unittest.cc b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge_unittest.cc
index 360fc43a..b991ced28 100644
--- a/chrome/browser/ash/arc/fileapi/arc_file_system_bridge_unittest.cc
+++ b/chrome/browser/ash/arc/fileapi/arc_file_system_bridge_unittest.cc
@@ -7,6 +7,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/arc/arc_util.h"
 #include "ash/components/arc/mojom/file_system.mojom.h"
 #include "ash/components/arc/session/arc_bridge_service.h"
 #include "ash/components/arc/test/connection_holder_util.h"
@@ -19,6 +20,7 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
+#include "base/test/test_future.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/arc/fileapi/chrome_content_provider_url_util.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
@@ -27,11 +29,20 @@
 #include "chrome/browser/ash/file_system_provider/service_factory.h"
 #include "chrome/browser/ash/fileapi/external_file_url_util.h"
 #include "chrome/browser/ash/fileapi/file_system_backend.h"
+#include "chrome/browser/ash/fusebox/fusebox_moniker.h"
+#include "chrome/browser/ash/fusebox/fusebox_server.h"
+#include "chrome/browser/ash/guest_os/guest_id.h"
+#include "chrome/browser/ash/guest_os/guest_os_session_tracker.h"
+#include "chrome/browser/ash/guest_os/guest_os_share_path.h"
+#include "chrome/browser/ash/guest_os/public/types.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
+#include "chromeos/ash/components/dbus/seneschal/seneschal_client.h"
 #include "chromeos/ash/components/dbus/virtual_file_provider/fake_virtual_file_provider_client.h"
 #include "chromeos/ash/components/dbus/virtual_file_provider/virtual_file_provider_client.h"
+#include "components/user_manager/scoped_user_manager.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "storage/browser/file_system/external_mount_points.h"
@@ -62,11 +73,14 @@
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     ash::ConciergeClient::InitializeFake(/*fake_cicerone_client=*/nullptr);
+    ash::SeneschalClient::InitializeFake();
     ash::VirtualFileProviderClient::InitializeFake();
+
     profile_manager_ = std::make_unique<TestingProfileManager>(
         TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
     profile_ = profile_manager_->CreateTestingProfile(kTestingProfileName);
+
     auto fake_provider =
         ash::file_system_provider::FakeExtensionProvider::Create(kExtensionId);
     const auto kProviderId = fake_provider->GetId();
@@ -76,6 +90,12 @@
                              ash::file_system_provider::MountOptions(
                                  kFileSystemId, "Test FileSystem"));
 
+    fake_user_manager_.Reset(std::make_unique<ash::FakeChromeUserManager>());
+    const AccountId account_id(
+        AccountId::FromUserEmail(profile_->GetProfileUserName()));
+    fake_user_manager_->AddUser(account_id);
+    fake_user_manager_->LoginUser(account_id);
+
     arc_file_system_bridge_ =
         std::make_unique<ArcFileSystemBridge>(profile_, &arc_bridge_service_);
     arc_bridge_service_.file_system()->SetInstance(&fake_file_system_);
@@ -85,8 +105,10 @@
   void TearDown() override {
     arc_bridge_service_.file_system()->CloseInstance(&fake_file_system_);
     arc_file_system_bridge_.reset();
+    fake_user_manager_.Reset();
     profile_manager_.reset();
     ash::VirtualFileProviderClient::Shutdown();
+    ash::SeneschalClient::Shutdown();
     ash::ConciergeClient::Shutdown();
   }
 
@@ -94,6 +116,8 @@
   base::ScopedTempDir temp_dir_;
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
+  user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
+      fake_user_manager_;
   raw_ptr<Profile, DanglingUntriaged> profile_ = nullptr;
 
   FakeFileSystemInstance fake_file_system_;
@@ -265,6 +289,35 @@
   EXPECT_TRUE(arc_file_system_bridge_->HandleIdReleased(kId));
 }
 
+TEST_F(ArcFileSystemBridgeTest, CreateAndDestroyMoniker) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv({"", "--enable-arcvm"});
+  EXPECT_TRUE(IsArcVmEnabled());
+
+  const guest_os::GuestId arcvm_id = guest_os::GuestId(
+      guest_os::VmType::ARCVM, kArcVmName, /*container_name=*/"");
+  guest_os::GuestOsSessionTracker::GetForProfile(profile_)->AddGuestForTesting(
+      arcvm_id,
+      guest_os::GuestInfo{arcvm_id, /*cid=*/32, /*username=*/"",
+                          /*homedir=*/base::FilePath(), /*ipv4_address=*/"",
+                          /*sftp_vsock_port=*/0});
+
+  fusebox::Server fusebox_server(/*delegate=*/nullptr);
+  base::test::TestFuture<const std::optional<fusebox::Moniker>&> create_future;
+  arc_file_system_bridge_->CreateMoniker(
+      EncodeToChromeContentProviderUrl(GURL(kTestUrl)),
+      /*read_only=*/true, create_future.GetCallback());
+  EXPECT_TRUE(create_future.Get().has_value());
+  const fusebox::Moniker moniker = create_future.Get().value();
+  EXPECT_TRUE(guest_os::GuestOsSharePath::GetForProfile(profile_)->IsPathShared(
+      kArcVmName, base::FilePath(fusebox::MonikerMap::GetFilename(moniker))));
+
+  base::test::TestFuture<bool> destroy_future;
+  arc_file_system_bridge_->DestroyMoniker(moniker,
+                                          destroy_future.GetCallback());
+  EXPECT_TRUE(destroy_future.Get());
+}
+
 TEST_F(ArcFileSystemBridgeTest, GetLinuxVFSPathFromExternalFileURL) {
   storage::ExternalMountPoints* system_mount_points =
       storage::ExternalMountPoints::GetSystemInstance();
diff --git a/chrome/browser/ash/arc/session/BUILD.gn b/chrome/browser/ash/arc/session/BUILD.gn
index 271f1b6..e200a56 100644
--- a/chrome/browser/ash/arc/session/BUILD.gn
+++ b/chrome/browser/ash/arc/session/BUILD.gn
@@ -84,6 +84,7 @@
     "//chrome/browser/ash/crostini",
     "//chrome/browser/ash/file_manager",
     "//chrome/browser/ash/guest_os/public",
+    "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/platform_keys/key_permissions",
     "//chrome/browser/ash/policy/arc",
@@ -91,7 +92,6 @@
     "//chrome/browser/ash/policy/handlers",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
-    "//chrome/browser/ui/webui/ash",
     "//chrome/browser/ui/webui/ash/add_supervision",
     "//chrome/browser/ui/webui/ash/diagnostics_dialog",
     "//chrome/common",
diff --git a/chrome/browser/ash/auth/BUILD.gn b/chrome/browser/ash/auth/BUILD.gn
index 7545e34..6a80f568 100644
--- a/chrome/browser/ash/auth/BUILD.gn
+++ b/chrome/browser/ash/auth/BUILD.gn
@@ -9,6 +9,8 @@
 
 static_library("auth") {
   sources = [
+    "active_session_fingerprint_client_impl.cc",
+    "active_session_fingerprint_client_impl.h",
     "cryptohome_pin_engine.cc",
     "cryptohome_pin_engine.h",
     "legacy_fingerprint_engine.cc",
diff --git a/chrome/browser/ash/auth/active_session_fingerprint_client_impl.cc b/chrome/browser/ash/auth/active_session_fingerprint_client_impl.cc
new file mode 100644
index 0000000..065a4ef8
--- /dev/null
+++ b/chrome/browser/ash/auth/active_session_fingerprint_client_impl.cc
@@ -0,0 +1,177 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/auth/active_session_fingerprint_client_impl.h"
+
+#include <memory>
+
+#include "ash/constants/ash_pref_names.h"
+#include "ash/public/cpp/ash_public_export.h"
+#include "ash/public/cpp/auth/active_session_auth_controller.h"
+#include "ash/public/cpp/auth/active_session_fingerprint_client.h"
+#include "ash/public/cpp/login_types.h"
+#include "ash/shell.h"
+#include "base/check.h"
+#include "base/check_is_test.h"
+#include "base/check_op.h"
+#include "base/containers/contains.h"
+#include "base/functional/callback_forward.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/notreached.h"
+#include "base/values.h"
+#include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
+#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
+#include "chromeos/ash/components/login/auth/public/auth_callbacks.h"
+#include "chromeos/ash/components/login/auth/public/user_context.h"
+#include "chromeos/ash/components/osauth/public/request/auth_request.h"
+#include "components/account_id/account_id.h"
+#include "components/prefs/pref_service.h"
+
+namespace ash {
+
+namespace {
+
+const char kFactorsOptionAll[] = "all";
+const char kFactorsOptionFingerprint[] = "FINGERPRINT";
+
+}  // namespace
+
+ActiveSessionFingerprintClientImpl::ActiveSessionFingerprintClientImpl()
+    : auth_performer_(UserDataAuthClient::Get()) {
+  if (Shell::HasInstance()) {
+    Shell::Get()->active_session_auth_controller()->SetFingerprintClient(this);
+  } else {
+    CHECK_IS_TEST();
+  }
+}
+
+ActiveSessionFingerprintClientImpl::~ActiveSessionFingerprintClientImpl() {
+  if (Shell::HasInstance()) {
+    Shell::Get()->active_session_auth_controller()->SetFingerprintClient(
+        nullptr);
+  } else {
+    CHECK_IS_TEST();
+  }
+}
+
+bool ActiveSessionFingerprintClientImpl::IsFingerprintAvailable(
+    AuthRequest::Reason reason,
+    const AccountId& account_id) {
+  auto* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id);
+
+  CHECK(profile);
+
+  auto* pref_service = profile->GetPrefs();
+
+  if (!pref_service) {
+    return false;
+  }
+
+  if (profile != ProfileManager::GetPrimaryUserProfile()) {
+    return false;
+  }
+
+  bool has_record =
+      pref_service->GetInteger(prefs::kQuickUnlockFingerprintRecord);
+
+  if (!has_record) {
+    return false;
+  }
+
+  switch (reason) {
+    case AuthRequest::Reason::kPasswordManager:
+      return false;
+    case AuthRequest::Reason::kSettings:
+      return false;
+    case AuthRequest::Reason::kWebAuthN: {
+      const base::Value::List& factors =
+          pref_service->GetList(prefs::kWebAuthnFactors);
+      if (base::Contains(factors, base::Value(kFactorsOptionAll)) ||
+          base::Contains(factors, base::Value(kFactorsOptionFingerprint))) {
+        return true;
+      }
+      return false;
+    }
+  }
+  NOTREACHED();
+}
+
+void ActiveSessionFingerprintClientImpl::PrepareFingerprintAuth(
+    std::unique_ptr<UserContext> user_context,
+    AuthOperationCallback auth_ready_callback,
+    ActiveSessionFingerprintScanCallback on_scan_callback) {
+  CHECK(on_scan_callback_.is_null());
+  CHECK(!on_scan_callback.is_null());
+  on_scan_callback_ = std::move(on_scan_callback);
+  auth_performer_.PrepareAuthFactor(
+      std::move(user_context), cryptohome::AuthFactorType::kLegacyFingerprint,
+      base::BindOnce(
+          &ActiveSessionFingerprintClientImpl::OnPrepareFingerprintAuth,
+          weak_factory_.GetWeakPtr(), std::move(auth_ready_callback)));
+}
+
+void ActiveSessionFingerprintClientImpl::OnPrepareFingerprintAuth(
+    AuthOperationCallback auth_ready_callback,
+    std::unique_ptr<UserContext> user_context,
+    std::optional<AuthenticationError> auth_error) {
+  if (auth_error.has_value()) {
+    LOG(ERROR) << "PrepareFingerprintAuth failed.";
+    on_scan_callback_.Reset();
+  }
+  UserDataAuthClient::Get()->AddFingerprintAuthObserver(this);
+  std::move(auth_ready_callback).Run(std::move(user_context), auth_error);
+}
+
+void ActiveSessionFingerprintClientImpl::TerminateFingerprintAuth(
+    std::unique_ptr<UserContext> user_context,
+    AuthOperationCallback callback) {
+  UserDataAuthClient::Get()->RemoveFingerprintAuthObserver(this);
+  on_scan_callback_.Reset();
+  auth_performer_.TerminateAuthFactor(
+      std::move(user_context), cryptohome::AuthFactorType::kLegacyFingerprint,
+      std::move(callback));
+}
+
+void ActiveSessionFingerprintClientImpl::OnFingerprintScan(
+    const ::user_data_auth::FingerprintScanResult& result) {
+  if (on_scan_callback_.is_null()) {
+    return;
+  }
+
+  FingerprintAuthScanResult state;
+  switch (result) {
+    case ::user_data_auth::FingerprintScanResult::
+        FINGERPRINT_SCAN_RESULT_SUCCESS:
+      state = ash::FingerprintAuthScanResult::kSuccess;
+      break;
+    case ::user_data_auth::FingerprintScanResult::FINGERPRINT_SCAN_RESULT_RETRY:
+      state = ash::FingerprintAuthScanResult::kFailed;
+      break;
+    case ::user_data_auth::FingerprintScanResult::
+        FINGERPRINT_SCAN_RESULT_LOCKOUT:
+      state = ash::FingerprintAuthScanResult::kTooManyAttempts;
+      break;
+    case ::user_data_auth::FingerprintScanResult::
+        FINGERPRINT_SCAN_RESULT_FATAL_ERROR:
+      state = ash::FingerprintAuthScanResult::kFatalError;
+      break;
+    default:  // The remaining states are for enrollment.
+      NOTREACHED();
+  }
+  on_scan_callback_.Run(state);
+}
+
+void ActiveSessionFingerprintClientImpl::OnEnrollScanDone(
+    const ::user_data_auth::FingerprintScanResult& result,
+    bool is_complete,
+    int percent_complete) {
+  CHECK(on_scan_callback_.is_null());
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ash/auth/active_session_fingerprint_client_impl.h b/chrome/browser/ash/auth/active_session_fingerprint_client_impl.h
new file mode 100644
index 0000000..dd71965
--- /dev/null
+++ b/chrome/browser/ash/auth/active_session_fingerprint_client_impl.h
@@ -0,0 +1,72 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_AUTH_ACTIVE_SESSION_FINGERPRINT_CLIENT_IMPL_H_
+#define CHROME_BROWSER_ASH_AUTH_ACTIVE_SESSION_FINGERPRINT_CLIENT_IMPL_H_
+
+#include <memory>
+#include <optional>
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "ash/public/cpp/auth/active_session_auth_controller.h"
+#include "ash/public/cpp/auth/active_session_fingerprint_client.h"
+#include "base/functional/callback_forward.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
+#include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
+#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
+#include "chromeos/ash/components/login/auth/auth_performer.h"
+#include "chromeos/ash/components/login/auth/public/auth_callbacks.h"
+#include "chromeos/ash/components/login/auth/public/user_context.h"
+#include "chromeos/ash/components/osauth/public/request/auth_request.h"
+#include "components/account_id/account_id.h"
+
+namespace ash {
+
+class ActiveSessionFingerprintClientImpl
+    : public ActiveSessionFingerprintClient,
+      public UserDataAuthClient::FingerprintAuthObserver {
+ public:
+  ActiveSessionFingerprintClientImpl();
+  ActiveSessionFingerprintClientImpl(
+      const ActiveSessionFingerprintClientImpl&) = delete;
+  ActiveSessionFingerprintClientImpl& operator=(
+      const ActiveSessionFingerprintClientImpl&) = delete;
+  ~ActiveSessionFingerprintClientImpl() override;
+
+  // ash::ActiveSessionFingerprintClient:
+  bool IsFingerprintAvailable(AuthRequest::Reason reason,
+                              const AccountId& account_id) override;
+  void PrepareFingerprintAuth(
+      std::unique_ptr<UserContext> user_context,
+      AuthOperationCallback auth_ready_callback,
+      ActiveSessionFingerprintScanCallback on_scan_callback) override;
+
+  void TerminateFingerprintAuth(std::unique_ptr<UserContext> user_context,
+                                AuthOperationCallback callback) override;
+
+  // ash::UserDataAuthClient::FingerprintAuthObserver:
+  void OnFingerprintScan(
+      const ::user_data_auth::FingerprintScanResult& result) override;
+
+  void OnEnrollScanDone(const ::user_data_auth::FingerprintScanResult& result,
+                        bool is_complete,
+                        int percent_complete) override;
+
+ private:
+  void OnPrepareFingerprintAuth(AuthOperationCallback auth_ready_callback,
+                                std::unique_ptr<UserContext> user_context,
+                                std::optional<AuthenticationError> auth_error);
+
+  ActiveSessionFingerprintScanCallback on_scan_callback_;
+
+  AuthPerformer auth_performer_;
+
+  base::WeakPtrFactory<ActiveSessionFingerprintClientImpl> weak_factory_{this};
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_AUTH_ACTIVE_SESSION_FINGERPRINT_CLIENT_IMPL_H_
diff --git a/chrome/browser/ash/boca/BUILD.gn b/chrome/browser/ash/boca/BUILD.gn
index 45381c323..05b601ad 100644
--- a/chrome/browser/ash/boca/BUILD.gn
+++ b/chrome/browser/ash/boca/BUILD.gn
@@ -22,7 +22,9 @@
     "//base",
     "//chrome/browser/profiles:profile",
     "//chromeos/ash/components/boca",
+    "//chromeos/ash/components/browser_context_helper",
     "//components/signin/public/identity_manager",
+    "//components/user_manager",
     "//services/network/public/cpp",
   ]
 }
diff --git a/chrome/browser/ash/boca/DEPS b/chrome/browser/ash/boca/DEPS
index 93b95e1..5a722d03 100644
--- a/chrome/browser/ash/boca/DEPS
+++ b/chrome/browser/ash/boca/DEPS
@@ -26,6 +26,7 @@
   "+chrome/browser/ui/browser_command_controller.h",
   "+chrome/browser/ui/browser_list.h",
   "+chrome/browser/ui/browser_list_observer.h",
+  "+chrome/browser/ui/browser_navigator.h",
   "+chrome/browser/ui/browser_window.h",
   "+chrome/browser/ui/tabs/tab_enums.h",
   "+chrome/browser/ui/tabs/tab_strip_model_observer.h",
diff --git a/chrome/browser/ash/boca/boca_manager.cc b/chrome/browser/ash/boca/boca_manager.cc
index 3cfcc099..a527c1f 100644
--- a/chrome/browser/ash/boca/boca_manager.cc
+++ b/chrome/browser/ash/boca/boca_manager.cc
@@ -7,6 +7,10 @@
 #include <memory>
 
 #include "chrome/browser/ash/boca/boca_manager_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/ash/components/boca/boca_session_manager.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "components/user_manager/user.h"
 
 namespace ash {
 // Static
@@ -15,7 +19,12 @@
       BocaManagerFactory::GetInstance()->GetForProfile(profile));
 }
 
-BocaManager::BocaManager(Profile* profile) {}
+BocaManager::BocaManager(Profile* profile) {
+  boca_session_manager_ = std::make_unique<boca::BocaSessionManager>(
+      ash::BrowserContextHelper::Get()
+          ->GetUserByBrowserContext(profile)
+          ->GetAccountId());
+}
 
 BocaManager::~BocaManager() = default;
 
diff --git a/chrome/browser/ash/boca/boca_manager.h b/chrome/browser/ash/boca/boca_manager.h
index 8a8adbe5..8da2d29 100644
--- a/chrome/browser/ash/boca/boca_manager.h
+++ b/chrome/browser/ash/boca/boca_manager.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_ASH_BOCA_BOCA_MANAGER_H_
 #define CHROME_BROWSER_ASH_BOCA_BOCA_MANAGER_H_
 
+#include "chromeos/ash/components/boca/boca_session_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 class Profile;
@@ -18,6 +19,8 @@
   explicit BocaManager(Profile* profile);
   ~BocaManager() override;
 
+ private:
+  std::unique_ptr<boca::BocaSessionManager> boca_session_manager_;
 };
 }  // namespace ash
 
diff --git a/chrome/browser/ash/boca/boca_manager_factory.cc b/chrome/browser/ash/boca/boca_manager_factory.cc
index 134077f..5d49391 100644
--- a/chrome/browser/ash/boca/boca_manager_factory.cc
+++ b/chrome/browser/ash/boca/boca_manager_factory.cc
@@ -7,9 +7,10 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/ash/boca/boca_manager.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chromeos/ash/components/boca/boca_role_util.h"
 
 namespace ash {
-
 // static
 BocaManagerFactory* BocaManagerFactory::GetInstance() {
   static base::NoDestructor<BocaManagerFactory> instance;
@@ -27,13 +28,18 @@
           "BocaManagerFactory",
           ProfileSelections::Builder()
               .WithRegular(ProfileSelection::kOriginalOnly)
-              .Build()) {}
+              // Do not init for ash internal such as login and lock screen.
+              .WithAshInternals(ProfileSelection::kNone)
+              .Build()) {
+  DependsOn(IdentityManagerFactory::GetInstance());
+}
 
 BocaManagerFactory::~BocaManagerFactory() = default;
 
 std::unique_ptr<KeyedService>
 BocaManagerFactory::BuildServiceInstanceForBrowserContext(
     content::BrowserContext* context) const {
+  CHECK(boca_util::IsEnabled());
   Profile* profile = Profile::FromBrowserContext(context);
   return std::make_unique<BocaManager>(profile);
 }
diff --git a/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl.cc b/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl.cc
index 85114218..e06d7169 100644
--- a/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl.cc
+++ b/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chromeos/window_pin_util.h"
 #include "content/public/browser/browser_thread.h"
@@ -143,4 +144,18 @@
   window_tracker->InitializeBrowserInfoForTracking(browser);
 }
 
+void OnTaskSystemWebAppManagerImpl::CreateBackgroundTabWithUrl(
+    SessionID window_id,
+    GURL url) {
+  Browser* const browser = GetBrowserWindowWithID(window_id);
+  if (!browser) {
+    return;
+  }
+
+  // TODO (b/353758782): Set navigation restrictions for the URL.
+  NavigateParams navigate_params(browser, url, ui::PAGE_TRANSITION_FROM_API);
+  navigate_params.disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
+  Navigate(&navigate_params);
+}
+
 }  // namespace ash::boca
diff --git a/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl.h b/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl.h
index e5f1c58..f6c33e2 100644
--- a/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl.h
+++ b/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl.h
@@ -9,6 +9,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/ash/components/boca/on_task/on_task_system_web_app_manager.h"
+#include "url/gurl.h"
 
 // Forward declaration of the browser profile and `SessionID`.
 class Profile;
@@ -32,6 +33,7 @@
   void SetPinStateForSystemWebAppWindow(bool pinned,
                                         SessionID window_id) override;
   void SetWindowTrackerForSystemWebAppWindow(SessionID window_id) override;
+  void CreateBackgroundTabWithUrl(SessionID window_id, GURL url) override;
 
  private:
   raw_ptr<Profile> profile_;
diff --git a/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl_browsertest.cc b/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl_browsertest.cc
index fc72ef6..8d6a0e9 100644
--- a/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl_browsertest.cc
+++ b/chrome/browser/ash/boca/on_task/on_task_system_web_app_manager_impl_browsertest.cc
@@ -16,6 +16,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "url/gurl.h"
 
 using ::testing::IsNull;
 using ::testing::NotNull;
@@ -23,6 +24,8 @@
 namespace ash::boca {
 namespace {
 
+constexpr char kTestUrl[] = "https://www.google.com";
+
 class OnTaskSystemWebAppManagerImplBrowserTest : public InProcessBrowserTest {
  protected:
   OnTaskSystemWebAppManagerImplBrowserTest() {
@@ -124,5 +127,29 @@
   EXPECT_FALSE(platform_util::IsBrowserLockedFullscreen(boca_app_browser));
 }
 
+IN_PROC_BROWSER_TEST_F(OnTaskSystemWebAppManagerImplBrowserTest,
+                       CreateBackgroundTabWithUrl) {
+  // Launch Boca app for testing purposes.
+  OnTaskSystemWebAppManagerImpl system_web_app_manager(profile());
+  base::test::TestFuture<bool> launch_future;
+  system_web_app_manager.LaunchSystemWebAppAsync(launch_future.GetCallback());
+  ASSERT_TRUE(launch_future.Get());
+  Browser* const boca_app_browser = FindBocaSystemWebAppBrowser();
+  ASSERT_THAT(boca_app_browser, NotNull());
+
+  // Boca homepage is by default opened.
+  EXPECT_EQ(boca_app_browser->tab_strip_model()->count(), 1);
+
+  // Create tab from the url and verify that Boca has the tab.
+  system_web_app_manager.CreateBackgroundTabWithUrl(
+      boca_app_browser->session_id(), GURL(kTestUrl));
+  content::RunAllTasksUntilIdle();
+  EXPECT_EQ(boca_app_browser->tab_strip_model()->count(), 2);
+  EXPECT_EQ(boca_app_browser->tab_strip_model()
+                ->GetWebContentsAt(1)
+                ->GetLastCommittedURL(),
+            GURL(kTestUrl));
+}
+
 }  // namespace
 }  // namespace ash::boca
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index 2284706..2344145 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -456,6 +456,7 @@
     "//chrome/browser/ui/webui/ash/cloud_upload",
     "//chrome/browser/ui/webui/ash/kerberos",
     "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
+    "//chrome/browser/ui/webui/signin/ash",
     "//chrome/browser/web_applications",
     "//chrome/browser/web_applications/app_service",
     "//chrome/common",
@@ -661,6 +662,7 @@
     "//chrome/browser/ui/aura/accessibility:impl",
     "//chrome/browser/ui/chromeos/magic_boost",
     "//chrome/browser/ui/webui/ash/cloud_upload",
+    "//chrome/browser/ui/webui/signin/ash",
     "//chrome/browser/web_applications/app_service",
   ]
 
diff --git a/chrome/browser/ash/crostini/BUILD.gn b/chrome/browser/ash/crostini/BUILD.gn
index 72fcd37..6e176d82 100644
--- a/chrome/browser/ash/crostini/BUILD.gn
+++ b/chrome/browser/ash/crostini/BUILD.gn
@@ -116,7 +116,6 @@
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/scheduler_config",
     "//chrome/browser/ui:browser_navigator_params_headers",
-    "//chrome/browser/ui/webui/ash",
     "//chrome/browser/ui/webui/ash/crostini_installer",
     "//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings",
     "//chrome/common",
diff --git a/chrome/browser/ash/file_manager/BUILD.gn b/chrome/browser/ash/file_manager/BUILD.gn
index 530c0bf..3d47c5f 100644
--- a/chrome/browser/ash/file_manager/BUILD.gn
+++ b/chrome/browser/ash/file_manager/BUILD.gn
@@ -145,7 +145,6 @@
     "//chrome/browser/apps/app_service:constants",
     "//chrome/browser/ash/app_list",
     "//chrome/browser/ash/app_list/search",
-    "//chrome/browser/ash/arc/fileapi",
     "//chrome/browser/ash/bruschetta",
     "//chrome/browser/ash/crostini",
     "//chrome/browser/ash/drive",
@@ -161,7 +160,6 @@
     "//chrome/browser/chromeos/policy/dlp",
     "//chrome/browser/chromeos/upload_office_to_cloud",
     "//chrome/browser/profiles",
-    "//chrome/browser/ui/webui/ash",
     "//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings_shared",
     "//chrome/browser/web_applications",
     "//chrome/common",
@@ -206,7 +204,6 @@
   allow_circular_includes_from = [
     "//chrome/browser/ash/app_list",
     "//chrome/browser/ash/app_list/search",
-    "//chrome/browser/ash/arc/fileapi",
     "//chrome/browser/ash/crostini",
     "//chrome/browser/ash/drive",
     "//chrome/browser/ash/file_manager/virtual_tasks",
@@ -215,7 +212,6 @@
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/policy/dlp",
     "//chrome/browser/ash/smb_client",
-    "//chrome/browser/ui/webui/ash",
   ]
 }
 
diff --git a/chrome/browser/ash/file_manager/path_util.cc b/chrome/browser/ash/file_manager/path_util.cc
index f5402c7..3d0f7d30 100644
--- a/chrome/browser/ash/file_manager/path_util.cc
+++ b/chrome/browser/ash/file_manager/path_util.cc
@@ -820,8 +820,20 @@
     force_external = true;
   }
 
-  // Force external URL for Crostini.
-  if (GetCrostiniMountDirectory(primary_profile).IsParent(path)) {
+  // Convert paths under /media/fuse/crostini_...
+  if (SetRelativePath(GetCrostiniMountDirectory(primary_profile), path,
+                      &relative_path)) {
+    if (arc::IsArcVmEnabled()) {
+      // For ARCVM, we can use ArcVolumeProvider URL and ask Seneschal to share
+      // the path to ARCVM so ARCVM does not need to talk through Chrome.
+      *arc_url_out =
+          GURL("content://org.chromium.arc.volumeprovider/crostini/")
+              .Resolve(base::EscapePath(relative_path.AsUTF8Unsafe()));
+      *requires_sharing_out = true;
+      return true;
+    }
+
+    // Use ChromeContentProvider for ARC++ Container to proxy through Chrome.
     force_external = true;
   }
 
diff --git a/chrome/browser/ash/file_manager/path_util_unittest.cc b/chrome/browser/ash/file_manager/path_util_unittest.cc
index 5c6d823..5b600ec9 100644
--- a/chrome/browser/ash/file_manager/path_util_unittest.cc
+++ b/chrome/browser/ash/file_manager/path_util_unittest.cc
@@ -871,7 +871,8 @@
   EXPECT_FALSE(requires_sharing);
 }
 
-TEST_F(FileManagerPathUtilConvertUrlTest, ConvertPathToArcUrl_Crostini) {
+TEST_F(FileManagerPathUtilConvertUrlTest,
+       ConvertPathToArcUrl_CrostiniOnArcContainer) {
   GURL url;
   bool requires_sharing = false;
   EXPECT_TRUE(ConvertPathToArcUrl(crostini_mount_point_.AppendASCII("a/b/c"),
@@ -883,6 +884,20 @@
   EXPECT_FALSE(requires_sharing);
 }
 
+TEST_F(FileManagerPathUtilConvertUrlTest, ConvertPathToArcUrl_CrostiniOnArcVm) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv({"", "--enable-arcvm"});
+  EXPECT_TRUE(arc::IsArcVmEnabled());
+
+  GURL url;
+  bool requires_sharing = false;
+  EXPECT_TRUE(ConvertPathToArcUrl(crostini_mount_point_.AppendASCII("a/b/c"),
+                                  &url, &requires_sharing));
+  EXPECT_EQ(GURL("content://org.chromium.arc.volumeprovider/crostini/a/b/c"),
+            url);
+  EXPECT_TRUE(requires_sharing);
+}
+
 TEST_F(FileManagerPathUtilConvertUrlTest, ConvertPathToArcUrl_MyDriveLegacy) {
   GURL url;
   bool requires_sharing = false;
diff --git a/chrome/browser/ash/login/screens/BUILD.gn b/chrome/browser/ash/login/screens/BUILD.gn
index 99db69a..fee9f30 100644
--- a/chrome/browser/ash/login/screens/BUILD.gn
+++ b/chrome/browser/ash/login/screens/BUILD.gn
@@ -29,6 +29,8 @@
     "choobe_screen.h",
     "chrome_user_selection_screen.cc",
     "chrome_user_selection_screen.h",
+    "connectivity_diagnostics_dialog.cc",
+    "connectivity_diagnostics_dialog.h",
     "consolidated_consent_screen.cc",
     "consolidated_consent_screen.h",
     "consumer_update_screen.cc",
@@ -226,6 +228,7 @@
     "//ash/components/arc/session:arc_base_enums",
     "//ash/constants",
     "//ash/public/mojom:input_device_settings",
+    "//ash/webui/connectivity_diagnostics",
     "//ash/webui/settings/public/constants:mojom",
     "//base",
     "//build:branding_buildflags",
@@ -271,6 +274,7 @@
     "//chrome/browser/ui/webui/ash/internet",
     "//chrome/browser/ui/webui/ash/login/testapi",
     "//chrome/browser/ui/webui/ash/settings",
+    "//chrome/browser/ui/webui/signin/ash",
     "//chrome/common",
     "//chrome/common:channel_info",
     "//chrome/common:chrome_features",
@@ -493,6 +497,7 @@
     "//chrome/browser/ui/webui/ash",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/browser/ui/webui/ash/settings",
+    "//chrome/browser/ui/webui/signin/ash",
     "//chrome/common",
     "//chrome/common:chrome_features",
     "//chrome/common:constants",
diff --git a/chrome/browser/ash/login/screens/DEPS b/chrome/browser/ash/login/screens/DEPS
index abfabe5..a1fbaeb 100644
--- a/chrome/browser/ash/login/screens/DEPS
+++ b/chrome/browser/ash/login/screens/DEPS
@@ -63,7 +63,6 @@
   "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/ash/system",
-  "+chrome/browser/ui/webui/ash/connectivity_diagnostics_dialog.h",
   "+chrome/browser/ui/webui/ash/internet",
   "+chrome/browser/ui/webui/ash/login",
   "+chrome/browser/ui/webui/ash/settings",
diff --git a/chrome/browser/ash/login/screens/connectivity_diagnostics_dialog.cc b/chrome/browser/ash/login/screens/connectivity_diagnostics_dialog.cc
new file mode 100644
index 0000000..5aff7d8
--- /dev/null
+++ b/chrome/browser/ash/login/screens/connectivity_diagnostics_dialog.cc
@@ -0,0 +1,45 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/login/screens/connectivity_diagnostics_dialog.h"
+
+#include <string>
+
+#include "ash/webui/connectivity_diagnostics/url_constants.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace {
+
+// Scale factor for size of the connectivity diagnostics dialog, based on
+// display size.
+const float kConnectivityDiagnosticsDialogScale = .8;
+
+}  // namespace
+
+namespace ash {
+
+// static
+void ConnectivityDiagnosticsDialog::ShowDialog(gfx::NativeWindow parent) {
+  ConnectivityDiagnosticsDialog* dialog = new ConnectivityDiagnosticsDialog();
+  dialog->ShowSystemDialog(parent);
+}
+
+ConnectivityDiagnosticsDialog::ConnectivityDiagnosticsDialog()
+    : SystemWebDialogDelegate(GURL(kChromeUIConnectivityDiagnosticsUrl),
+                              /*title=*/std::u16string()) {}
+
+ConnectivityDiagnosticsDialog::~ConnectivityDiagnosticsDialog() = default;
+
+void ConnectivityDiagnosticsDialog::GetDialogSize(gfx::Size* size) const {
+  const display::Display display =
+      display::Screen::GetScreen()->GetPrimaryDisplay();
+
+  *size =
+      gfx::Size(display.size().width() * kConnectivityDiagnosticsDialogScale,
+                display.size().height() * kConnectivityDiagnosticsDialogScale);
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ash/login/screens/connectivity_diagnostics_dialog.h b/chrome/browser/ash/login/screens/connectivity_diagnostics_dialog.h
new file mode 100644
index 0000000..57e15ec
--- /dev/null
+++ b/chrome/browser/ash/login/screens/connectivity_diagnostics_dialog.h
@@ -0,0 +1,30 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_LOGIN_SCREENS_CONNECTIVITY_DIAGNOSTICS_DIALOG_H_
+#define CHROME_BROWSER_ASH_LOGIN_SCREENS_CONNECTIVITY_DIAGNOSTICS_DIALOG_H_
+
+#include "chrome/browser/ui/webui/ash/system_web_dialog_delegate.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace ash {
+
+class ConnectivityDiagnosticsDialog : public SystemWebDialogDelegate {
+ public:
+  static void ShowDialog(gfx::NativeWindow parent);
+
+ protected:
+  ConnectivityDiagnosticsDialog();
+  ~ConnectivityDiagnosticsDialog() override;
+
+  ConnectivityDiagnosticsDialog(const ConnectivityDiagnosticsDialog&) = delete;
+  ConnectivityDiagnosticsDialog& operator=(
+      const ConnectivityDiagnosticsDialog&) = delete;
+
+  // ui::WebDialogDelegate
+  void GetDialogSize(gfx::Size* size) const override;
+};
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_LOGIN_SCREENS_CONNECTIVITY_DIAGNOSTICS_DIALOG_H_
diff --git a/chrome/browser/ash/login/screens/error_screen.cc b/chrome/browser/ash/login/screens/error_screen.cc
index 02ff9b0..e1b2debe 100644
--- a/chrome/browser/ash/login/screens/error_screen.cc
+++ b/chrome/browser/ash/login/screens/error_screen.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
+#include "chrome/browser/ash/login/screens/connectivity_diagnostics_dialog.h"
 #include "chrome/browser/ash/login/signin_specifics.h"
 #include "chrome/browser/ash/login/startup_utils.h"
 #include "chrome/browser/ash/login/ui/captive_portal_window_proxy.h"
@@ -27,7 +28,6 @@
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/webui/ash/connectivity_diagnostics_dialog.h"
 #include "chrome/browser/ui/webui/ash/internet/internet_detail_dialog.h"
 #include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
diff --git a/chrome/browser/ash/login/session/BUILD.gn b/chrome/browser/ash/login/session/BUILD.gn
index bad521a34..3045291 100644
--- a/chrome/browser/ash/login/session/BUILD.gn
+++ b/chrome/browser/ash/login/session/BUILD.gn
@@ -69,7 +69,6 @@
     "//chrome/browser/scalable_iph:scalable_iph_factory",
     "//chrome/browser/screen_ai:screen_ai_dlc_installer",
     "//chrome/browser/ui/ash/media_client",
-    "//chrome/browser/ui/webui/ash",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/common",
     "//chrome/common:channel_info",
diff --git a/chrome/browser/ash/login/signin/BUILD.gn b/chrome/browser/ash/login/signin/BUILD.gn
index 74ac2c5..322cc0d 100644
--- a/chrome/browser/ash/login/signin/BUILD.gn
+++ b/chrome/browser/ash/login/signin/BUILD.gn
@@ -75,6 +75,7 @@
     "//chrome/browser/sync:factories",
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/browser/ui/webui/ash/login",
+    "//chrome/browser/ui/webui/signin/ash",
     "//chrome/common",
     "//chrome/common:constants",
     "//chromeos/ash/components/account_manager",
diff --git a/chrome/browser/ash/login/signin/offline_signin_limiter.cc b/chrome/browser/ash/login/signin/offline_signin_limiter.cc
index 369812e..1fa0b08e 100644
--- a/chrome/browser/ash/login/signin/offline_signin_limiter.cc
+++ b/chrome/browser/ash/login/signin/offline_signin_limiter.cc
@@ -75,7 +75,7 @@
       base::BindRepeating(&OfflineSigninLimiter::UpdateLockScreenLimit,
                           base::Unretained(this)));
   // Start listening to power state.
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 
   // Start listening to session lock state
   auto* session_manager = session_manager::SessionManager::Get();
@@ -122,7 +122,7 @@
           std::make_unique<base::WallClockTimer>()) {}
 
 OfflineSigninLimiter::~OfflineSigninLimiter() {
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
   auto* session_manager = session_manager::SessionManager::Get();
   if (session_manager) {
     session_manager->RemoveObserver(this);
diff --git a/chrome/browser/ash/login/ui/BUILD.gn b/chrome/browser/ash/login/ui/BUILD.gn
index 775510e..6b01d934 100644
--- a/chrome/browser/ash/login/ui/BUILD.gn
+++ b/chrome/browser/ash/login/ui/BUILD.gn
@@ -114,7 +114,6 @@
     "//chrome/browser/safe_browsing",
     "//chrome/browser/themes",
     "//chrome/browser/ui/views/toolbar",
-    "//chrome/browser/ui/webui/ash",
     "//chrome/browser/ui/webui/ash/diagnostics_dialog",
     "//chrome/browser/ui/webui/ash/internet",
     "//chrome/browser/ui/webui/ash/login/testapi",
diff --git a/chrome/browser/ash/login/webview_login_browsertest.cc b/chrome/browser/ash/login/webview_login_browsertest.cc
index b5873aa..2f07b66 100644
--- a/chrome/browser/ash/login/webview_login_browsertest.cc
+++ b/chrome/browser/ash/login/webview_login_browsertest.cc
@@ -1808,7 +1808,7 @@
   // selector dialog once it's opened.
   void SimulateUserWillSelectClientCert(
       const std::string& cert_name_to_select) {
-    chrome::SetShowSSLClientCertificateSelectorHookForTest(base::BindRepeating(
+    SetShowSSLClientCertificateSelectorHookForTest(base::BindRepeating(
         &SigninFrameWebviewClientCertsLoginTest::OnClientCertSelectorRequested,
         cert_name_to_select));
   }
diff --git a/chrome/browser/ash/mahi/mahi_manager_impl_unittest.cc b/chrome/browser/ash/mahi/mahi_manager_impl_unittest.cc
index 07d3570..614c379 100644
--- a/chrome/browser/ash/mahi/mahi_manager_impl_unittest.cc
+++ b/chrome/browser/ash/mahi/mahi_manager_impl_unittest.cc
@@ -192,6 +192,11 @@
 
 // Title is included in the request proto.
 TEST_F(MahiManagerImplTest, SendingTitleOnly) {
+  feature_list_.Reset();
+  feature_list_.InitWithFeatures(
+      /*enabled_features=*/{chromeos::features::kMahi,
+                            chromeos::features::kFeatureManagementMahi},
+      /*disabled_features=*/{chromeos::features::kMahiSendingUrl});
   RequestSummary();
 
   EXPECT_EQ(GetMahiProvider()->latest_title(), "Title of url1");
diff --git a/chrome/browser/ash/mall/chrome_mall_ui_delegate.cc b/chrome/browser/ash/mall/chrome_mall_ui_delegate.cc
index fb6b402..313c944 100644
--- a/chrome/browser/ash/mall/chrome_mall_ui_delegate.cc
+++ b/chrome/browser/ash/mall/chrome_mall_ui_delegate.cc
@@ -4,9 +4,11 @@
 
 #include "chrome/browser/ash/mall/chrome_mall_ui_delegate.h"
 
+#include "base/check.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
 #include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
+#include "chrome/browser/apps/almanac_api_client/device_info_manager_factory.h"
 #include "chrome/browser/ash/mall/mall_url.h"
 #include "chrome/browser/profiles/profile.h"
 #include "url/gurl.h"
@@ -14,15 +16,19 @@
 namespace ash {
 
 ChromeMallUIDelegate::ChromeMallUIDelegate(content::WebUI* web_ui)
-    : web_ui_(web_ui), device_info_manager_(Profile::FromWebUI(web_ui)) {}
+    : web_ui_(web_ui) {}
 
 ChromeMallUIDelegate::~ChromeMallUIDelegate() = default;
 
 void ChromeMallUIDelegate::GetMallEmbedUrl(
     base::OnceCallback<void(const GURL&)> callback) {
-  device_info_manager_.GetDeviceInfo(base::BindOnce([](apps::DeviceInfo info) {
-                                       return GetMallLaunchUrl(info);
-                                     }).Then(std::move(callback)));
+  apps::DeviceInfoManager* manager =
+      apps::DeviceInfoManagerFactory::GetForProfile(
+          Profile::FromWebUI(web_ui_));
+  CHECK(manager);
+  manager->GetDeviceInfo(base::BindOnce([](apps::DeviceInfo info) {
+                           return GetMallLaunchUrl(info);
+                         }).Then(std::move(callback)));
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/mall/chrome_mall_ui_delegate.h b/chrome/browser/ash/mall/chrome_mall_ui_delegate.h
index e3e8904..ffb7082 100644
--- a/chrome/browser/ash/mall/chrome_mall_ui_delegate.h
+++ b/chrome/browser/ash/mall/chrome_mall_ui_delegate.h
@@ -19,7 +19,6 @@
 
  private:
   raw_ptr<content::WebUI> web_ui_;  // Owns `this`.
-  apps::DeviceInfoManager device_info_manager_;
 };
 
 }  // namespace ash
diff --git a/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl.cc b/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl.cc
index ca7fc6a..3dc6711f 100644
--- a/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl.cc
+++ b/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl.cc
@@ -39,6 +39,8 @@
 constexpr char kDeviceAnnotatedLocationPlaceholder[] =
     "${DEVICE_ANNOTATED_LOCATION}";
 constexpr char kDeviceIpPlaceholder[] = "${DEVICE_IP_ADDRESSES}";
+constexpr char kPlaceholderStartSymbol[] = "${";
+constexpr char kPlaceholderEndSymbol[] = "}";
 
 // Prefix values used to indicate the IP protocol of the IP addresses in the
 // effective DoH template URI.
@@ -48,6 +50,9 @@
 // Used as a replacement value for device identifiers when the user is
 // unaffiliated.
 constexpr char kDeviceNotManaged[] = "VALUE_NOT_AVAILABLE";
+constexpr char kIdentifierNotAvailable[] = "${VALUE_NOT_AVAILABLE}";
+constexpr char kUnknownPlaceholderMessage[] =
+    "Templates contain not replaced placeholder: ";
 
 // Part before "@" of the given |email| address.
 // "some_email@domain.com" => "some_email"
@@ -159,6 +164,76 @@
   return replacement;
 }
 
+// Returns first found placeholder in `templates` starting from position `pos`,
+// as option value. If no placeholders are found, returns empty optional.
+std::optional<std::string_view> GetNextPlaceholder(std::string_view templates,
+                                                   size_t pos) {
+  size_t placeholder_start = templates.find(kPlaceholderStartSymbol, pos);
+  if (placeholder_start == std::string::npos) {
+    return std::nullopt;
+  }
+
+  size_t placeholder_end =
+      templates.find(kPlaceholderEndSymbol, placeholder_start);
+  if (placeholder_end == std::string::npos) {
+    LOG(WARNING) << "Placeholders end symbol is missed in " << templates;
+    return std::nullopt;
+  }
+
+  return std::make_optional(templates.substr(
+      placeholder_start, placeholder_end - placeholder_start + 1));
+}
+
+// Checks if display templates `display_str` (with replaced identifiers) and
+// original templates `raw_str` contain placeholders with the same values. This
+// will indicate that those placeholders were not replaced. Replace those
+// placeholders with kIdentifierNotAvailable. Some placeholders like
+// "DEVICE_IP_ADDRESSES" can create 2 new placeholders in the display
+// template, so searching for every original placeholder instead of fetching
+// all of them and comparing one to one.
+void HighlightUnknownDisplayPlaceholders(std::string& display_str,
+                                         std::string_view raw_str) {
+  size_t search_start_pos = 0;
+  std::optional<std::string_view> maybe_placeholder =
+      GetNextPlaceholder(raw_str, search_start_pos);
+  while (maybe_placeholder.has_value()) {
+    std::string_view placeholder = maybe_placeholder.value();
+    size_t placeholder_pos_in_display = display_str.find(placeholder);
+    if (placeholder_pos_in_display != std::string::npos) {
+      LOG(WARNING) << kUnknownPlaceholderMessage << placeholder
+                   << ", value is not available";
+      base::ReplaceSubstringsAfterOffset(&display_str,
+                                         placeholder_pos_in_display,
+                                         placeholder, kIdentifierNotAvailable);
+    }
+
+    search_start_pos =
+        raw_str.find(kPlaceholderEndSymbol, search_start_pos + 1);
+    maybe_placeholder = GetNextPlaceholder(raw_str, search_start_pos);
+  }
+}
+
+// Looks into effective `templates` if they still contain placeholders which
+// were not replaced with data and strip them off. This step keeps compatibility
+// between new type of placeholders delivered by policy and older OS versions
+// which still have no definitions for such placeholders.
+void StripUnknownEffectivePlaceholders(std::string& templates) {
+  size_t search_start_pos = 0;
+
+  std::optional<std::string_view> maybe_placeholder =
+      GetNextPlaceholder(templates, search_start_pos);
+  while (maybe_placeholder.has_value()) {
+    std::string placeholder(maybe_placeholder.value());
+    LOG(WARNING) << kUnknownPlaceholderMessage << placeholder
+                 << ", it will be deleted";
+    search_start_pos =
+        templates.find(kPlaceholderStartSymbol, search_start_pos);
+    base::ReplaceSubstringsAfterOffset(&templates, search_start_pos,
+                                       placeholder, "");
+    maybe_placeholder = GetNextPlaceholder(templates, search_start_pos);
+  }
+}
+
 // Returns a copy of `template` where the identifier placeholders are replaced
 // with real user and device data.
 // If `hash_variable` is true, then the user and device identifiers are hashed
@@ -187,6 +262,7 @@
   std::string user_email = user->GetAccountId().GetUserEmail();
   std::string user_email_domain = EmailDomain(user_email);
   std::string user_email_name = EmailName(user_email);
+  std::string original_templates = templates;
   base::ReplaceSubstringsAfterOffset(
       &templates, 0, kUserEmailPlaceholder,
       FormatVariable(user_email, salt, hash_variable));
@@ -234,6 +310,13 @@
       &templates, 0, kDeviceIpPlaceholder,
       GetIpReplacementValue(/*use_network_byte_order=*/hash_variable, *user));
 
+  bool is_display_mode = !hash_variable;
+  if (is_display_mode) {
+    HighlightUnknownDisplayPlaceholders(templates, original_templates);
+  } else {
+    StripUnknownEffectivePlaceholders(templates);
+  }
+
   return templates;
 }
 
diff --git a/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl_unittest.cc b/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl_unittest.cc
index 34e9fd45..8729e7b 100644
--- a/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl_unittest.cc
+++ b/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl_unittest.cc
@@ -52,6 +52,38 @@
     "ID}-${DEVICE_ASSET_ID}-${DEVICE_SERIAL_NUMBER}-${DEVICE_ANNOTATED_"
     "LOCATION}/"
     "dns-query{?dns}";
+
+constexpr char kTemplateWithUnknownIdentifier[] =
+    "https://dns.google.alternativeuri/"
+    "${USER_EMAIL}-${USER_EMAIL_DOMAIN}-${USER_EMAIL_NAME}-${DEVICE_"
+    "DIRECTORY_"
+    "ID}-${DEVICE_ASSET_ID}-${DEVICE_SERIAL_NUMBER}-${UNKNOWN_ID}-${"
+    "DEVICE_"
+    "ANNOTATED_"
+    "LOCATION}/"
+    "dns-query{?dns}";
+
+constexpr char kTemplateWithThreeUnknownIdentifiers[] =
+    "https://dns.google.alternativeuri/"
+    "${USER_EMAIL}-${UNKNOWN_ID1}-${USER_EMAIL_NAME}-${DEVICE_"
+    "DIRECTORY_"
+    "ID}-${DEVICE_ASSET_ID}-${DEVICE_SERIAL_NUMBER}-${UNKNOWN_ID2}"
+    "-${UNKNOWN_ID3}/dns-query{?dns}";
+
+constexpr char kDisplayTemplateWithThreeUnknownIdentifiers[] =
+    "https://dns.google.alternativeuri/"
+    "${test-user@testdomain.com}-${VALUE_NOT_AVAILABLE}-${test-user}-"
+    "${85729104-ef7a-5718d62e72ca}-${admin-provided-test-asset-ID}-"
+    "${serial-number}-${VALUE_NOT_AVAILABLE}-${VALUE_NOT_AVAILABLE}/"
+    "dns-query{?dns}";
+
+constexpr char kDisplayTemplateOnlyUserAndUnknownIdentifiers[] =
+    "https://dns.google.alternativeuri/"
+    "${test-user@testdomain.com}-${VALUE_NOT_AVAILABLE}-${test-user}-"
+    "${VALUE_NOT_AVAILABLE}-${VALUE_NOT_AVAILABLE}-${VALUE_NOT_AVAILABLE}-"
+    "${VALUE_NOT_AVAILABLE}-${VALUE_NOT_AVAILABLE}/"
+    "dns-query{?dns}";
+
 constexpr char kDisplayTemplateIdentifiers[] =
     "https://dns.google.alternativeuri/"
     "${test-user@testdomain.com}-${testdomain.com}-${test-user}-${85729104-"
@@ -75,6 +107,19 @@
     "9AB270C9961EBBDF728F43396B0A25A1F198EA5F1F31719758C64E78839B928B/"
     "dns-query{?dns}";
 
+constexpr char kEffectiveTemplateWithUnknownIdentifiers[] =
+    "https://dns.google.alternativeuri/"
+    "EAE2DCB2164EB64B695BC555C4EC45D01C8F0DF73CCD3321E45E5B49F22A22DF-"
+    "0641BDB5149AF8B202F8EC96D8C256774CDFE9456CB12663DDF5897AFD91BC78-"
+    "F3BA0BDE2D6E8DBE626D0B9ECF7862B18256C4D1807621F9F01AF06A3F603137-"
+    "542BD404A979AB36D342019A18FFE0691B7763C1D145F2D7119FC1DCBBB7B248-"
+    "2601D4E337D68B6EE97B338482E45F7D3670E78075490A7F9916321AF7B4539F-"
+    "D60336AF57006D9FD327FD96F4B44F9067A09FB0A4DD83FB67EC0B2C472B9734-"
+    /*placeholder was stripped*/
+    "-"
+    "F7E37C960A2F15DD63CE0F694F29A3A76E4BE2700918E01FA22F0692098C6B28/"
+    "dns-query{?dns}";
+
 constexpr char kEffectiveTemplateIdentifiers[] =
     "https://dns.google.alternativeuri/"
     "EAE2DCB2164EB64B695BC555C4EC45D01C8F0DF73CCD3321E45E5B49F22A22DF-"
@@ -97,6 +142,45 @@
     "9CBE0CF3CA986C6BD8241B5A7030FBB807B7340AEFE0C53541B54545A888B551/"
     "dns-query{?dns}";
 
+constexpr char kEffectiveTemplateWithThreeUnknownIdentifiers[] =
+    "https://dns.google.alternativeuri/"
+    "EAE2DCB2164EB64B695BC555C4EC45D01C8F0DF73CCD3321E45E5B49F22A22DF-"
+    /*placeholder was stripped*/
+    "-"
+    "F3BA0BDE2D6E8DBE626D0B9ECF7862B18256C4D1807621F9F01AF06A3F603137-"
+    "542BD404A979AB36D342019A18FFE0691B7763C1D145F2D7119FC1DCBBB7B248-"
+    "2601D4E337D68B6EE97B338482E45F7D3670E78075490A7F9916321AF7B4539F-"
+    "D60336AF57006D9FD327FD96F4B44F9067A09FB0A4DD83FB67EC0B2C472B9734-"
+    /*placeholder was stripped*/
+    "-" /*placeholder was stripped*/
+    "/dns-query{?dns}";
+
+constexpr char kEffectiveTemplateUserOnlyWithThreeUnknownIdentifiers[] =
+    "https://dns.google.alternativeuri/"
+    "EAE2DCB2164EB64B695BC555C4EC45D01C8F0DF73CCD3321E45E5B49F22A22DF-"
+    /*placeholder was stripped*/
+    "-"
+    "F3BA0BDE2D6E8DBE626D0B9ECF7862B18256C4D1807621F9F01AF06A3F603137-"
+    "9AB270C9961EBBDF728F43396B0A25A1F198EA5F1F31719758C64E78839B928B-"
+    "9AB270C9961EBBDF728F43396B0A25A1F198EA5F1F31719758C64E78839B928B-"
+    "9AB270C9961EBBDF728F43396B0A25A1F198EA5F1F31719758C64E78839B928B-"
+    /*placeholder was stripped*/
+    "-" /*placeholder was stripped*/
+    "/dns-query{?dns}";
+
+constexpr char kEffectiveTemplateWithThreeUnknownIdentifiersNoSalt[] =
+    "https://dns.google.alternativeuri/"
+    "B07D2C5D119EB1881671C3B8D84CBE4FE3595C0C9ECBBF7670B18DDFDA072F66-"
+    /*placeholder was stripped*/
+    "-"
+    "F85AC825D102B9F2D546AA1679EA991AE845994C1343730D564F3FCD0A2168C3-"
+    "519F1980774A18DFCFC2003B4DC27E3497BF9B586E5901D7F2F6EDD1845613A9-"
+    "505CBC62B85263246EE6FC89264D4039E5B55FD353885EC86C2DAF5CAA05399E-"
+    "87278CD685B7191BB97AA713083522D99DBA30FD6F1DEC3C898E8745FB97E3E3-"
+    /*placeholder was stripped*/
+    "-" /*placeholder was stripped*/
+    "/dns-query{?dns}";
+
 constexpr char kTestDeviceDirectoryId[] = "85729104-ef7a-5718d62e72ca";
 constexpr char kTestDeviceAssetId[] = "admin-provided-test-asset-ID";
 constexpr char kTestDeviceAnnotatedLocation[] = "admin-provided-test-location";
@@ -270,6 +354,50 @@
   EXPECT_FALSE(doh_template_uri_resolver_->GetDohWithIdentifiersActive());
 }
 
+// Test that verifies the correct substitution of placeholders in the template
+// uri if unknown identifiers are included. Unknown identifiers will be deleted
+// from effective template.
+TEST_F(TemplatesUriResolverImplTest, TemplatesWithThreeUnknownIdentifiers) {
+  SetUpAffiliatedUser();
+  SetUpDOHSecureModeWithSalt(kTestSalt);
+  SetUpDOHTemplatesWithIdentifiers(kTemplateWithThreeUnknownIdentifiers);
+  SetUpDOHGoogleDnsTemplate();
+
+  doh_template_uri_resolver_->Update(pref_service());
+
+  EXPECT_EQ(GetDisplayTemplates(), kDisplayTemplateWithThreeUnknownIdentifiers);
+  EXPECT_EQ(GetEffectiveTemplates(),
+            kEffectiveTemplateWithThreeUnknownIdentifiers);
+  EXPECT_TRUE(doh_template_uri_resolver_->GetDohWithIdentifiersActive());
+
+  // `prefs::kDnsOverHttpsTemplates` should apply when
+  // `prefs::kDnsOverHttpsTemplatesWithIdentifiers` is cleared.
+  pref_service()->ClearPref(prefs::kDnsOverHttpsTemplatesWithIdentifiers);
+
+  doh_template_uri_resolver_->Update(pref_service());
+
+  EXPECT_EQ(GetEffectiveTemplates(), kGoogleDns);
+  EXPECT_FALSE(doh_template_uri_resolver_->GetDohWithIdentifiersActive());
+}
+
+// Tests that only user indentifiers are replaced in
+// `prefs::kDnsOverHttpsTemplatesWithIdentifiers` if the user is not affiliated.
+TEST_F(TemplatesUriResolverImplTest,
+       TemplatesWithUnknownIdentifiersUnaffiliated) {
+  SetUpUnaffiliatedUser();
+  SetUpDOHSecureModeWithSalt(kTestSalt);
+  SetUpDOHTemplatesWithIdentifiers(kTemplateWithThreeUnknownIdentifiers);
+  SetUpDOHGoogleDnsTemplate();
+
+  doh_template_uri_resolver_->Update(pref_service());
+
+  EXPECT_EQ(GetDisplayTemplates(),
+            kDisplayTemplateOnlyUserAndUnknownIdentifiers);
+  EXPECT_EQ(GetEffectiveTemplates(),
+            kEffectiveTemplateUserOnlyWithThreeUnknownIdentifiers);
+  EXPECT_TRUE(doh_template_uri_resolver_->GetDohWithIdentifiersActive());
+}
+
 // Tests that only user indentifiers are replaced in
 // `prefs::kDnsOverHttpsTemplatesWithIdentifiers` if the user is not affiliated.
 TEST_F(TemplatesUriResolverImplTest, TemplatesWithIdentifiersUnaffiliated) {
@@ -303,6 +431,26 @@
   EXPECT_TRUE(doh_template_uri_resolver_->GetDohWithIdentifiersActive());
 }
 
+// Verifies that an unknown identifier is stripped from placeholders.
+TEST_F(TemplatesUriResolverImplTest, MultipleTemplatesWithUnknownIdentifiers) {
+  SetUpAffiliatedUser();
+  SetUpDOHSecureModeWithSalt(kTestSalt);
+  SetUpDOHGoogleDnsTemplate();
+  std::string multiple_templates =
+      base::StringPrintf("%s %s %s", kTemplateWithUnknownIdentifier, kGoogleDns,
+                         kTemplateWithUnknownIdentifier);
+  SetUpDOHTemplatesWithIdentifiers(multiple_templates);
+
+  doh_template_uri_resolver_->Update(pref_service());
+
+  std::string expected_multiple_templates =
+      base::StringPrintf("%s %s %s", kEffectiveTemplateWithUnknownIdentifiers,
+                         kGoogleDns, kEffectiveTemplateWithUnknownIdentifiers);
+
+  EXPECT_EQ(GetEffectiveTemplates(), expected_multiple_templates);
+  EXPECT_TRUE(doh_template_uri_resolver_->GetDohWithIdentifiersActive());
+}
+
 TEST_F(TemplatesUriResolverImplTest, TemplatesWithIdentifiersNoSalt) {
   SetUpAffiliatedUser();
   SetUpDOHSecureModeWithSalt("");
@@ -322,15 +470,69 @@
   EXPECT_EQ(GetEffectiveTemplates(), kGoogleDns);
 }
 
+// Unknown identifiers will be stripped from effective templates and
+// replaced with "VALUE_NOT_AVAILABLE" in display templates.
+TEST_F(TemplatesUriResolverImplTest, TemplatesWithUnknownIdentifiersNoSalt) {
+  SetUpAffiliatedUser();
+  SetUpDOHSecureModeWithSalt("");
+  SetUpDOHTemplatesWithIdentifiers(kTemplateWithThreeUnknownIdentifiers);
+  SetUpDOHGoogleDnsTemplate();
+
+  doh_template_uri_resolver_->Update(pref_service());
+
+  EXPECT_EQ(GetDisplayTemplates(), kDisplayTemplateWithThreeUnknownIdentifiers);
+  EXPECT_EQ(GetEffectiveTemplates(),
+            kEffectiveTemplateWithThreeUnknownIdentifiersNoSalt);
+  EXPECT_TRUE(doh_template_uri_resolver_->GetDohWithIdentifiersActive());
+
+  // `prefs::kDnsOverHttpsTemplates` should apply when
+  // `prefs::kDnsOverHttpsTemplatesWithIdentifiers` is cleared.
+  pref_service()->ClearPref(prefs::kDnsOverHttpsTemplatesWithIdentifiers);
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(GetEffectiveTemplates(), kGoogleDns);
+}
+
 constexpr char kTemplateIdentifiersWithIp[] =
     "https://dns.google.alternativeuri/${DEVICE_IP_ADDRESSES}";
 
+constexpr char kTemplateWithIpAndUnknownIdentifier[] =
+    "https://dns.google.alternativeuri/${DEVICE_IP_ADDRESSES}${UNKNOWN}";
+
+constexpr char kTemplateWithIpAndTwoUnknownIdentifier[] =
+    "https://dns.google.alternativeuri/"
+    "${DEVICE_IP_ADDRESSES}${UNKNOWN1}${UNKNOWN2}";
+
+constexpr char kTemplateWithIpAndTwoUnknownIdentifierV2[] =
+    "https://dns.google.alternativeuri/"
+    "${UNKNOWN1}${DEVICE_IP_ADDRESSES}${UNKNOWN2}";
+
 constexpr char kEffectiveTemplateIdentifiersWithIp[] =
     "https://dns.google.alternativeuri/"
     "001064000001002000000000000000000100000000000001";
+
 constexpr char kDisplayTemplateIdentifiersWithIp[] =
     "https://dns.google.alternativeuri/${100.0.0.1}${::100:0:0:1}";
 
+constexpr char kDisplayTemplateWithIpAndUnknownIdentifier[] =
+    "https://dns.google.alternativeuri/"
+    "${100.0.0.1}${::100:0:0:1}${VALUE_NOT_AVAILABLE}";
+
+constexpr char kDisplayTemplateWithIpAndTwoUnknownIdentifier[] =
+    "https://dns.google.alternativeuri/"
+    "${100.0.0.1}${::100:0:0:1}${VALUE_NOT_AVAILABLE}${VALUE_NOT_AVAILABLE}";
+
+constexpr char kDisplayTemplateWithIpAndTwoUnknownIdentifierV2[] =
+    "https://dns.google.alternativeuri/"
+    "${VALUE_NOT_AVAILABLE}${100.0.0.1}${::100:0:0:1}${VALUE_NOT_AVAILABLE}";
+
+constexpr char kDisplayTemplateWithIpAndUnknownIdentifierIpDeleted[] =
+    "https://dns.google.alternativeuri/"
+    "${VALUE_NOT_AVAILABLE}";
+
+constexpr char kDisplayTemplateWithIpAndTwoUnknownIdentifierIpDeleted[] =
+    "https://dns.google.alternativeuri/"
+    "${VALUE_NOT_AVAILABLE}${VALUE_NOT_AVAILABLE}";
+
 constexpr char kNoReplacementEffectiveTemplateIdentifiersWithIp[] =
     "https://dns.google.alternativeuri/";
 constexpr char kNoReplacementDisplayTemplateIdentifiersWithIp[] =
@@ -412,6 +614,123 @@
   EXPECT_EQ(GetEffectiveTemplates(), kEffectiveTemplateIdentifiersWithIp);
 }
 
+// Verifies IP addresses placeholder replacement for the
+// DnsOverHttpsTemplatesWithIdentifiers policy when the user is affiliated.
+// More specifically, it tests that IP addresses are included if the
+// default network is managed by user policy or device policy. Unknown
+// identifier should be stripped in effective templates, and replaced with
+// placeholder in display template.
+TEST_F(TemplatesUriResolverImplTest,
+       TemplatesWithIpAddressAndUnknowIdentifierAndAffiliatedUser) {
+  SetUpAffiliatedUser();
+  SetUpDOHSecureModeWithSalt("");
+  SetUpDOHTemplatesWithIdentifiers(kTemplateWithIpAndUnknownIdentifier);
+  const ash::NetworkStateHandler* network_state_handler =
+      ash::NetworkHandler::Get()->network_state_handler();
+
+  const ash::NetworkState* network = network_state_handler->DefaultNetwork();
+
+  // Verify that the IP is not replaced for unmanaged networks, IP is deleted.
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(network->onc_source(), ::onc::ONCSource::ONC_SOURCE_UNKNOWN);
+  EXPECT_EQ(GetDisplayTemplates(),
+            kDisplayTemplateWithIpAndUnknownIdentifierIpDeleted);
+  EXPECT_EQ(GetEffectiveTemplates(),
+            kNoReplacementEffectiveTemplateIdentifiersWithIp);
+
+  ChangeNetworkOncSource(network->path(),
+                         ::onc::ONCSource::ONC_SOURCE_USER_POLICY);
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(GetDisplayTemplates(), kDisplayTemplateWithIpAndUnknownIdentifier);
+  EXPECT_EQ(GetEffectiveTemplates(), kEffectiveTemplateIdentifiersWithIp);
+
+  ChangeNetworkOncSource(network->path(),
+                         ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY);
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(GetDisplayTemplates(), kDisplayTemplateWithIpAndUnknownIdentifier);
+  EXPECT_EQ(GetEffectiveTemplates(), kEffectiveTemplateIdentifiersWithIp);
+}
+
+// Verifies IP addresses placeholder replacement for the
+// DnsOverHttpsTemplatesWithIdentifiers policy when the user is affiliated.
+// More specifically, it tests that IP addresses are included if the
+// default network is managed by user policy or device policy.
+// Both unknown identifier should be stripped in effective templates, and
+// replaced with placeholder in display template.
+TEST_F(TemplatesUriResolverImplTest,
+       TemplatesWithIpAddressAndTwoUnknowIdentifierAndAffiliatedUser) {
+  SetUpAffiliatedUser();
+  SetUpDOHSecureModeWithSalt("");
+  SetUpDOHTemplatesWithIdentifiers(kTemplateWithIpAndTwoUnknownIdentifier);
+  const ash::NetworkStateHandler* network_state_handler =
+      ash::NetworkHandler::Get()->network_state_handler();
+
+  const ash::NetworkState* network = network_state_handler->DefaultNetwork();
+
+  // Verify that the IP is not replaced for unmanaged networks, IP is deleted.
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(network->onc_source(), ::onc::ONCSource::ONC_SOURCE_UNKNOWN);
+  EXPECT_EQ(GetDisplayTemplates(),
+            kDisplayTemplateWithIpAndTwoUnknownIdentifierIpDeleted);
+  EXPECT_EQ(GetEffectiveTemplates(),
+            kNoReplacementEffectiveTemplateIdentifiersWithIp);
+
+  ChangeNetworkOncSource(network->path(),
+                         ::onc::ONCSource::ONC_SOURCE_USER_POLICY);
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(GetDisplayTemplates(),
+            kDisplayTemplateWithIpAndTwoUnknownIdentifier);
+  EXPECT_EQ(GetEffectiveTemplates(), kEffectiveTemplateIdentifiersWithIp);
+
+  ChangeNetworkOncSource(network->path(),
+                         ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY);
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(GetDisplayTemplates(),
+            kDisplayTemplateWithIpAndTwoUnknownIdentifier);
+  EXPECT_EQ(GetEffectiveTemplates(), kEffectiveTemplateIdentifiersWithIp);
+}
+
+// Verifies IP addresses placeholder replacement for the
+// kTemplateWithIpAndTwoUnknownIdentifierV2 policy when the user is affiliated.
+// More specifically, it tests that IP addresses are included if the
+// default network is managed by user policy or device policy.
+// Both unknown identifier should be stripped in effective templates, and
+// replaced with placeholder in display template.
+TEST_F(TemplatesUriResolverImplTest,
+       TemplatesWithIpAddressAndTwoUnknowIdentifierV2AndAffiliatedUser) {
+  SetUpAffiliatedUser();
+  SetUpDOHSecureModeWithSalt("");
+  SetUpDOHTemplatesWithIdentifiers(kTemplateWithIpAndTwoUnknownIdentifierV2);
+  const ash::NetworkStateHandler* network_state_handler =
+      ash::NetworkHandler::Get()->network_state_handler();
+
+  const ash::NetworkState* network = network_state_handler->DefaultNetwork();
+
+  // Verify that the IP is not replaced for unmanaged networks, IP is deleted,
+  // unknown identifiers are replaced in display template with
+  // "VALUE_NOT_AVAILABLE" and deleted from effective template.
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(network->onc_source(), ::onc::ONCSource::ONC_SOURCE_UNKNOWN);
+  EXPECT_EQ(GetDisplayTemplates(),
+            kDisplayTemplateWithIpAndTwoUnknownIdentifierIpDeleted);
+  EXPECT_EQ(GetEffectiveTemplates(),
+            kNoReplacementEffectiveTemplateIdentifiersWithIp);
+
+  ChangeNetworkOncSource(network->path(),
+                         ::onc::ONCSource::ONC_SOURCE_USER_POLICY);
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(GetDisplayTemplates(),
+            kDisplayTemplateWithIpAndTwoUnknownIdentifierV2);
+  EXPECT_EQ(GetEffectiveTemplates(), kEffectiveTemplateIdentifiersWithIp);
+
+  ChangeNetworkOncSource(network->path(),
+                         ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY);
+  doh_template_uri_resolver_->Update(pref_service());
+  EXPECT_EQ(GetDisplayTemplates(),
+            kDisplayTemplateWithIpAndTwoUnknownIdentifierV2);
+  EXPECT_EQ(GetEffectiveTemplates(), kEffectiveTemplateIdentifiersWithIp);
+}
+
 TEST_F(TemplatesUriResolverImplTest,
        TemplatesWithIdentifiersIpProtocolUpdates) {
   SetUpAffiliatedUser();
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_broker.cc b/chrome/browser/ash/policy/core/device_local_account_policy_broker.cc
index 5342ea54..7e4a019d 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_broker.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_broker.cc
@@ -150,7 +150,8 @@
     const scoped_refptr<base::SequencedTaskRunner>& resource_cache_task_runner,
     std::variant<AffiliatedInvalidationServiceProvider*,
                  invalidation::InvalidationListener*>
-        invalidation_service_provider_or_listener)
+        invalidation_service_provider_or_listener,
+    bool skip_first_policy_fetch)
     : invalidation_service_provider_or_listener_(
           invalidation::PointerVariantToRawPointer(
               invalidation_service_provider_or_listener)),
@@ -185,6 +186,7 @@
   schema_registry_.RegisterComponent(
       PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()), GetChromeSchema());
   schema_registry_.SetAllDomainsReady();
+  core_.SetSkipFirstPolicyFetch(skip_first_policy_fetch);
 }
 
 DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() {
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_broker.h b/chrome/browser/ash/policy/core/device_local_account_policy_broker.h
index 9e787f7e..ae175a1 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_broker.h
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_broker.h
@@ -70,7 +70,8 @@
           resource_cache_task_runner,
       std::variant<AffiliatedInvalidationServiceProvider*,
                    invalidation::InvalidationListener*>
-          invalidation_service_provider_or_listener);
+          invalidation_service_provider_or_listener,
+      bool skip_first_policy_fetch);
 
   DeviceLocalAccountPolicyBroker(const DeviceLocalAccountPolicyBroker&) =
       delete;
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_service.cc b/chrome/browser/ash/policy/core/device_local_account_policy_service.cc
index 4fac05c..4f6929d 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_service.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_service.cc
@@ -79,6 +79,17 @@
   }
 }
 
+// Store the information that the first policy fetch since the device boot
+// has been scheduled.
+void MarkPolicyFetchOnBootScheduled() {
+  base::FilePath first_policy_fetch_path;
+  CHECK(
+      base::PathService::Get(ash::FILE_DEVICE_LOCAL_ACCOUNT_FIRST_POLICY_FETCH,
+                             &first_policy_fetch_path));
+  base::File file(first_policy_fetch_path,
+                  base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+}
+
 }  // namespace
 
 DeviceLocalAccountPolicyService::DeviceLocalAccountPolicyService(
@@ -117,9 +128,24 @@
   external_data_service_ =
       std::make_unique<DeviceLocalAccountExternalDataService>(
           this, std::move(external_data_service_backend_task_runner));
+  CheckPolicyFetchRequired();
   UpdateAccountList();
 }
 
+void DeviceLocalAccountPolicyService::CheckPolicyFetchRequired() {
+  // We keep a flag file in memory whenever the first policy fetch happened. If
+  // it happened before, then on Chrome start the first policy fetch should be
+  // skipped since the policy should be up to date from invalidations.
+  base::FilePath first_policy_fetch_file;
+  CHECK(
+      base::PathService::Get(ash::FILE_DEVICE_LOCAL_ACCOUNT_FIRST_POLICY_FETCH,
+                             &first_policy_fetch_file));
+  skip_first_policy_fetch_ = base::PathExists(first_policy_fetch_file);
+  if (!skip_first_policy_fetch_) {
+    MarkPolicyFetchOnBootScheduled();
+  }
+}
+
 DeviceLocalAccountPolicyService::~DeviceLocalAccountPolicyService() {
   DCHECK(policy_brokers_.empty());
 }
@@ -313,7 +339,8 @@
           base::SingleThreadTaskRunner::GetCurrentDefault(),
           resource_cache_task_runner_,
           invalidation::RawPointerVariantToPointer(
-              invalidation_service_provider_or_listener_));
+              invalidation_service_provider_or_listener_),
+          skip_first_policy_fetch_);
     }
 
     // Fire up the cloud connection for fetching policy for the account from
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_service.h b/chrome/browser/ash/policy/core/device_local_account_policy_service.h
index 42fbe761..8c1ff1c0 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_service.h
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_service.h
@@ -146,6 +146,11 @@
   // Notifies the |observers_| that the policy for |user_id| has changed.
   void NotifyPolicyUpdated(const std::string& user_id);
 
+  // Populates `first_policy_fetch_scheduled_` based on presence of
+  // file in memory. This is done to avoid a policy fetch if this is
+  // not the first time Chrome is started since device boot.
+  void CheckPolicyFetchRequired();
+
   base::ObserverList<Observer, true>::Unchecked observers_;
 
   raw_ptr<ash::SessionManagerClient> session_manager_client_;
@@ -192,6 +197,10 @@
   // for device-local accounts.
   const base::FilePath component_policy_cache_root_;
 
+  // Whether the first policy fetch is should be skipped. This is set to true if
+  // there was a policy fetch already since the device boot.
+  bool skip_first_policy_fetch_ = false;
+
   base::WeakPtrFactory<DeviceLocalAccountPolicyService> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ash/policy/skyvault/BUILD.gn b/chrome/browser/ash/policy/skyvault/BUILD.gn
index 6328ea7..09bf0b4f 100644
--- a/chrome/browser/ash/policy/skyvault/BUILD.gn
+++ b/chrome/browser/ash/policy/skyvault/BUILD.gn
@@ -57,6 +57,7 @@
     "//chrome/common:constants",
     "//chromeos/ash/components/browser_context_helper",
     "//chromeos/ash/components/cryptohome",
+    "//chromeos/ash/components/dbus/userdataauth",
     "//chromeos/ash/components/dbus/userdataauth:userdataauth_proto",
     "//chromeos/ash/components/drivefs",
     "//chromeos/ash/components/drivefs/mojom",
diff --git a/chrome/browser/ash/privacy_hub/privacy_hub_util.cc b/chrome/browser/ash/privacy_hub/privacy_hub_util.cc
index cbd1f6c8..208921e9 100644
--- a/chrome/browser/ash/privacy_hub/privacy_hub_util.cc
+++ b/chrome/browser/ash/privacy_hub/privacy_hub_util.cc
@@ -366,7 +366,7 @@
   return observation;
 }
 
-void OpenSystemSettings(Profile* profile, ContentType type) {
+void OpenSystemSettings(ContentType type) {
   const char* settings_path = "";
   switch (type) {
     case ContentType::MEDIASTREAM_CAMERA: {
@@ -390,8 +390,8 @@
     }
   }
 
-  chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(profile,
-                                                               settings_path);
+  chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
+      ProfileManager::GetPrimaryUserProfile(), settings_path);
 }
 
 ScopedUserPermissionPrefForTest::ScopedUserPermissionPrefForTest(
diff --git a/chrome/browser/ash/privacy_hub/privacy_hub_util.h b/chrome/browser/ash/privacy_hub/privacy_hub_util.h
index 0f56764..74bd576 100644
--- a/chrome/browser/ash/privacy_hub/privacy_hub_util.h
+++ b/chrome/browser/ash/privacy_hub/privacy_hub_util.h
@@ -14,7 +14,6 @@
 #include "components/content_settings/core/common/content_settings_types.mojom.h"
 
 class AppAccessNotifier;
-class Profile;
 
 namespace ash {
 
@@ -100,7 +99,7 @@
 
 // Opens the system settings page that allows OS level control for the provided
 // content type if such settings page exists.
-void OpenSystemSettings(Profile* profile, ContentType type);
+void OpenSystemSettings(ContentType type);
 
 class ScopedUserPermissionPrefForTest {
  public:
diff --git a/chrome/browser/ash/system_web_apps/apps/BUILD.gn b/chrome/browser/ash/system_web_apps/apps/BUILD.gn
index 589b27c4..2f5df35 100644
--- a/chrome/browser/ash/system_web_apps/apps/BUILD.gn
+++ b/chrome/browser/ash/system_web_apps/apps/BUILD.gn
@@ -146,7 +146,6 @@
     "//chrome/browser/ash/policy/dlp",
     "//chrome/browser/chromeos/upload_office_to_cloud",
     "//chrome/browser/resources/ash/settings:resources",
-    "//chrome/browser/ui/webui/ash",
     "//chrome/browser/ui/webui/ash/diagnostics_dialog",
     "//chrome/browser/web_applications/mojom:mojom_web_apps_enum",
     "//chrome/common:channel_info",
@@ -292,6 +291,7 @@
     "//ash/constants",
     "//ash/webui/boca_ui",
     "//base/test:test_support",
+    "//chrome/browser/ui",
     "//testing/gtest",
 
     # Tests from subdirectories:
diff --git a/chrome/browser/ash/system_web_apps/apps/boca_web_app_info.cc b/chrome/browser/ash/system_web_apps/apps/boca_web_app_info.cc
index f16199d..9e5611b 100644
--- a/chrome/browser/ash/system_web_apps/apps/boca_web_app_info.cc
+++ b/chrome/browser/ash/system_web_apps/apps/boca_web_app_info.cc
@@ -10,8 +10,10 @@
 #include "ash/webui/grit/ash_boca_ui_resources.h"
 #include "chrome/browser/ash/system_web_apps/apps/system_web_app_install_utils.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/boca/boca_role_util.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "url/gurl.h"
@@ -89,3 +91,18 @@
 bool BocaSystemAppDelegate::IsAppEnabled() const {
   return ash::boca_util::IsEnabled();
 }
+
+bool BocaSystemAppDelegate::HasCustomTabMenuModel() const {
+  return IsConsumerProfile(profile());
+}
+
+std::unique_ptr<ui::SimpleMenuModel> BocaSystemAppDelegate::GetTabMenuModel(
+    ui::SimpleMenuModel::Delegate* delegate) const {
+  std::unique_ptr<ui::SimpleMenuModel> tab_menu =
+      std::make_unique<ui::SimpleMenuModel>(delegate);
+  tab_menu->AddItemWithStringId(TabStripModel::CommandReload,
+                                IDS_TAB_CXMENU_RELOAD);
+  tab_menu->AddItemWithStringId(TabStripModel::CommandGoBack,
+                                IDS_CONTENT_CONTEXT_BACK);
+  return tab_menu;
+}
diff --git a/chrome/browser/ash/system_web_apps/apps/boca_web_app_info.h b/chrome/browser/ash/system_web_apps/apps/boca_web_app_info.h
index 796a72f..898b5cc4 100644
--- a/chrome/browser/ash/system_web_apps/apps/boca_web_app_info.h
+++ b/chrome/browser/ash/system_web_apps/apps/boca_web_app_info.h
@@ -5,7 +5,10 @@
 #ifndef CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_APPS_BOCA_WEB_APP_INFO_H_
 #define CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_APPS_BOCA_WEB_APP_INFO_H_
 
+#include <memory>
+
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_delegate.h"
+#include "ui/base/models/simple_menu_model.h"
 #include "url/gurl.h"
 
 namespace web_app {
@@ -30,6 +33,9 @@
   bool IsUrlInSystemAppScope(const GURL& url) const override;
   bool ShouldPinTab(GURL url) const override;
   bool IsAppEnabled() const override;
+  bool HasCustomTabMenuModel() const override;
+  std::unique_ptr<ui::SimpleMenuModel> GetTabMenuModel(
+      ui::SimpleMenuModel::Delegate* delegate) const override;
 };
 
 std::unique_ptr<web_app::WebAppInstallInfo> CreateWebAppInfoForBocaApp();
diff --git a/chrome/browser/ash/system_web_apps/apps/boca_web_app_info_unittest.cc b/chrome/browser/ash/system_web_apps/apps/boca_web_app_info_unittest.cc
index bc4e324..eea1cee 100644
--- a/chrome/browser/ash/system_web_apps/apps/boca_web_app_info_unittest.cc
+++ b/chrome/browser/ash/system_web_apps/apps/boca_web_app_info_unittest.cc
@@ -7,7 +7,9 @@
 #include "ash/constants/ash_features.h"
 #include "ash/webui/boca_ui/url_constants.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/models/simple_menu_model.h"
 
 namespace {
 
@@ -64,6 +66,10 @@
   EXPECT_TRUE(delegate()->ShouldAllowMaximize());
 }
 
+TEST_F(BocaSystemAppProviderDelegateTest, UsesDefaultTabMenuModel) {
+  EXPECT_FALSE(delegate()->HasCustomTabMenuModel());
+}
+
 class BocaSystemAppConsumerDelegateTest : public BocaSystemAppDelegateTest {
  public:
   BocaSystemAppConsumerDelegateTest() {
@@ -99,4 +105,14 @@
   EXPECT_TRUE(delegate()->ShouldHideNewTabButton());
 }
 
+TEST_F(BocaSystemAppConsumerDelegateTest, UsesCustomTabMenuModel) {
+  ASSERT_TRUE(delegate()->HasCustomTabMenuModel());
+
+  const std::unique_ptr<ui::SimpleMenuModel> tab_menu =
+      delegate()->GetTabMenuModel(nullptr);
+  ASSERT_EQ(2u, tab_menu->GetItemCount());
+  EXPECT_EQ(TabStripModel::CommandReload, tab_menu->GetCommandIdAt(0));
+  EXPECT_EQ(TabStripModel::CommandGoBack, tab_menu->GetCommandIdAt(1));
+}
+
 }  // namespace
diff --git a/chrome/browser/ash/system_web_apps/apps/camera_app/BUILD.gn b/chrome/browser/ash/system_web_apps/apps/camera_app/BUILD.gn
index 619f83f..9d7e3a3 100644
--- a/chrome/browser/ash/system_web_apps/apps/camera_app/BUILD.gn
+++ b/chrome/browser/ash/system_web_apps/apps/camera_app/BUILD.gn
@@ -28,7 +28,6 @@
     "//chrome/browser/ash/hats",
     "//chrome/browser/ash/system_web_apps/types",
     "//chrome/browser/screen_ai/public:optical_character_recognizer",
-    "//chrome/browser/ui/webui/ash",
     "//chrome/services/pdf/public/mojom",
     "//content/public/browser",
     "//mojo/public/cpp/base",
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index fe3e13d..31990e8 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4205,9 +4205,9 @@
       !matching_certificates.empty() ? std::move(matching_certificates)
                                      : std::move(nonmatching_certificates);
 
-  return chrome::ShowSSLClientCertificateSelector(
-      web_contents, cert_request_info, std::move(client_cert_choices),
-      std::move(delegate));
+  return ShowSSLClientCertificateSelector(web_contents, cert_request_info,
+                                          std::move(client_cert_choices),
+                                          std::move(delegate));
 }
 
 content::MediaObserver* ChromeContentBrowserClient::GetMediaObserver() {
diff --git a/chrome/browser/compose/chrome_compose_client.cc b/chrome/browser/compose/chrome_compose_client.cc
index cf8812d..46925d1 100644
--- a/chrome/browser/compose/chrome_compose_client.cc
+++ b/chrome/browser/compose/chrome_compose_client.cc
@@ -669,6 +669,8 @@
       GetMSBBStateFromPrefs());
 
   compose::ProactiveNudgeTracker::Signals nudge_signals;
+  nudge_signals.ukm_source_id =
+      GetWebContents().GetPrimaryMainFrame()->GetPageUkmSourceId();
   nudge_signals.page_origin =
       web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin();
   nudge_signals.page_url = web_contents()->GetURL();
diff --git a/chrome/browser/compose/chrome_compose_client_unittest.cc b/chrome/browser/compose/chrome_compose_client_unittest.cc
index c7fd41d..33abe31 100644
--- a/chrome/browser/compose/chrome_compose_client_unittest.cc
+++ b/chrome/browser/compose/chrome_compose_client_unittest.cc
@@ -132,7 +132,8 @@
                 GetInstance(),
             base::BindRepeating([](content::BrowserContext* context)
                                     -> std::unique_ptr<KeyedService> {
-              return std::make_unique<MockSegmentationPlatformService>();
+              return std::make_unique<
+                  testing::NiceMock<MockSegmentationPlatformService>>();
             })},
         TestingProfile::TestingFactory{
             OptimizationGuideKeyedServiceFactory::GetInstance(),
@@ -889,13 +890,15 @@
       autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
 
   base::test::TestFuture<segmentation_platform::TrainingLabels> training_labels;
+  ukm::SourceId source =
+      web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId();
   EXPECT_CALL(GetSegmentationPlatformService(),
               CollectTrainingData(
                   segmentation_platform::proto::SegmentId::
                       OPTIMIZATION_TARGET_SEGMENTATION_COMPOSE_PROMOTION,
-                  kTrainingRequestId, _, _))
+                  kTrainingRequestId, source, _, _))
       .Times(1)
-      .WillOnce(testing::WithArg<2>(testing::Invoke(
+      .WillOnce(testing::WithArg<3>(testing::Invoke(
           [&](auto labels) { training_labels.SetValue(labels); })));
 
   client().CloseUI(compose::mojom::CloseReason::kInsertButton);
diff --git a/chrome/browser/compose/proactive_nudge_tracker.cc b/chrome/browser/compose/proactive_nudge_tracker.cc
index b5f29228..93cea13 100644
--- a/chrome/browser/compose/proactive_nudge_tracker.cc
+++ b/chrome/browser/compose/proactive_nudge_tracker.cc
@@ -587,10 +587,12 @@
   training_labels.output_metric =
       std::make_pair("Compose.ProactiveNudge.DerivedEngagement",
                      static_cast<base::HistogramBase::Sample>(engagement));
+  ukm::SourceId source =
+      state_ ? state_->signals.ukm_source_id : ukm::kInvalidSourceId;
   segmentation_service_->CollectTrainingData(
       segmentation_platform::proto::SegmentId::
           OPTIMIZATION_TARGET_SEGMENTATION_COMPOSE_PROMOTION,
-      training_request_id, training_labels, base::DoNothing());
+      training_request_id, source, training_labels, base::DoNothing());
 }
 
 bool ProactiveNudgeTracker::MatchesCurrentField(autofill::FormGlobalId form,
diff --git a/chrome/browser/compose/proactive_nudge_tracker.h b/chrome/browser/compose/proactive_nudge_tracker.h
index 341e785..3c8dcc5 100644
--- a/chrome/browser/compose/proactive_nudge_tracker.h
+++ b/chrome/browser/compose/proactive_nudge_tracker.h
@@ -94,6 +94,7 @@
     Signals& operator=(Signals&&);
     ~Signals();
 
+    ukm::SourceId ukm_source_id;
     url::Origin page_origin;
     GURL page_url;
     autofill::FormData form;
diff --git a/chrome/browser/compose/proactive_nudge_tracker_unittest.cc b/chrome/browser/compose/proactive_nudge_tracker_unittest.cc
index 57850421..1a4bae0 100644
--- a/chrome/browser/compose/proactive_nudge_tracker_unittest.cc
+++ b/chrome/browser/compose/proactive_nudge_tracker_unittest.cc
@@ -200,6 +200,7 @@
     autofill::FormFieldData field = CreateTestFormFieldData(),
     base::TimeTicks page_change_time = base::TimeTicks::Now()) {
   ProactiveNudgeTracker::Signals signals;
+  signals.ukm_source_id = ukm::kInvalidSourceId;
   signals.field = field;
   signals.form = CreateFormData();
   signals.page_change_time = page_change_time;
@@ -433,7 +434,8 @@
 
 TEST_F(ProactiveNudgeTrackerSegmentationTest,
        SegmentationShowWithoutCollectingTrainingData) {
-  EXPECT_CALL(segmentation_service(), CollectTrainingData(_, _, _, _)).Times(0);
+  EXPECT_CALL(segmentation_service(), CollectTrainingData(_, _, _, _, _))
+      .Times(0);
   base::test::TestFuture<segmentation_platform::ClassificationResultCallback>
       future;
   auto field = CreateTestFormFieldData();
@@ -457,7 +459,8 @@
 
 TEST_F(ProactiveNudgeTrackerSegmentationTest,
        SegmentationShowWithAlwaysCollectTrainingData) {
-  EXPECT_CALL(segmentation_service(), CollectTrainingData(_, _, _, _)).Times(1);
+  EXPECT_CALL(segmentation_service(), CollectTrainingData(_, _, _, _, _))
+      .Times(1);
   compose::GetMutableConfigForTesting()
       .proactive_nudge_always_collect_training_data = true;
 
@@ -487,7 +490,8 @@
 TEST_F(ProactiveNudgeTrackerSegmentationTest, SegmentationRandomForceShow) {
   compose::GetMutableConfigForTesting().proactive_nudge_force_show_probability =
       kSegmentationForceShowResult + 1e-6;
-  EXPECT_CALL(segmentation_service(), CollectTrainingData(_, _, _, _)).Times(1);
+  EXPECT_CALL(segmentation_service(), CollectTrainingData(_, _, _, _, _))
+      .Times(1);
   base::test::TestFuture<segmentation_platform::ClassificationResultCallback>
       future;
   auto field = CreateTestFormFieldData();
@@ -742,9 +746,9 @@
                 CollectTrainingData(
                     segmentation_platform::proto::SegmentId::
                         OPTIMIZATION_TARGET_SEGMENTATION_COMPOSE_PROMOTION,
-                    TrainingRequestId(request_number), _, _))
+                    TrainingRequestId(request_number), _, _, _))
         .Times(1)
-        .WillOnce(testing::Invoke([&](auto, auto, auto labels, auto) {
+        .WillOnce(testing::Invoke([&](auto, auto, auto, auto labels, auto) {
           training_labels.SetValue(labels);
         }));
     return training_labels;
diff --git a/chrome/browser/content_settings/generated_cookie_prefs.cc b/chrome/browser/content_settings/generated_cookie_prefs.cc
index 3125619..c0c7d883 100644
--- a/chrome/browser/content_settings/generated_cookie_prefs.cc
+++ b/chrome/browser/content_settings/generated_cookie_prefs.cc
@@ -69,6 +69,8 @@
       return CookiePrimarySetting::BLOCK_THIRD_PARTY;
     case CookieControlsMode::kIncognitoOnly:
       return CookiePrimarySetting::BLOCK_THIRD_PARTY_INCOGNITO;
+    case CookieControlsMode::kLimited:
+      return CookiePrimarySetting::LIMIT_THIRD_PARTY;
     case CookieControlsMode::kOff:
       return CookiePrimarySetting::ALLOW_ALL;
   }
diff --git a/chrome/browser/content_settings/generated_cookie_prefs.h b/chrome/browser/content_settings/generated_cookie_prefs.h
index 4a7b8f78..b4355eb2c 100644
--- a/chrome/browser/content_settings/generated_cookie_prefs.h
+++ b/chrome/browser/content_settings/generated_cookie_prefs.h
@@ -24,7 +24,8 @@
   ALLOW_ALL,
   BLOCK_THIRD_PARTY_INCOGNITO,
   BLOCK_THIRD_PARTY,
-  BLOCK_ALL
+  BLOCK_ALL,
+  LIMIT_THIRD_PARTY
 };
 
 // The base class for generated preferences which support WebUI cookie controls
diff --git a/chrome/browser/content_settings/host_content_settings_map_unittest.cc b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
index 6443e576..be6d40a 100644
--- a/chrome/browser/content_settings/host_content_settings_map_unittest.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
@@ -53,6 +53,7 @@
 #include "components/permissions/features.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "components/safe_browsing/core/common/features.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
 #include "net/base/schemeful_site.h"
@@ -139,7 +140,13 @@
 class HostContentSettingsMapTest : public testing::Test {
  public:
   HostContentSettingsMapTest()
-      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
+    // TODO(crbug.com/362466866): Instead of disabling the
+    // `kSafetyHubAbusiveNotificationRevocation` feature, find a stable
+    // fix such that the tests still pass when the feature is enabled.
+    feature_list_.InitAndDisableFeature(
+        safe_browsing::kSafetyHubAbusiveNotificationRevocation);
+  }
 
   void FastForwardTime(base::TimeDelta delta) {
     task_environment_.FastForwardBy(delta);
@@ -153,6 +160,7 @@
   }
 
   content::BrowserTaskEnvironment task_environment_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 // Wrapper to TestingProfile to reduce test boilerplates, by keeping a fixed
diff --git a/chrome/browser/content_settings/one_time_permission_provider.cc b/chrome/browser/content_settings/one_time_permission_provider.cc
index 4900b2c9..e917a3d 100644
--- a/chrome/browser/content_settings/one_time_permission_provider.cc
+++ b/chrome/browser/content_settings/one_time_permission_provider.cc
@@ -39,11 +39,11 @@
   // main function for the browser process is run (which initializes the HCSM).
   // For this reason, the PowerMonitor is always initialized before the observer
   // is added here.
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 }
 
 OneTimePermissionProvider::~OneTimePermissionProvider() {
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 }
 
 // TODO(b/307193732): handle the PartitionKey in all relevant methods, including
diff --git a/chrome/browser/content_settings/page_specific_content_settings_delegate.cc b/chrome/browser/content_settings/page_specific_content_settings_delegate.cc
index 91f03251..6447bd0 100644
--- a/chrome/browser/content_settings/page_specific_content_settings_delegate.cc
+++ b/chrome/browser/content_settings/page_specific_content_settings_delegate.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/content_settings/page_specific_content_settings_delegate.h"
 
 #include "base/feature_list.h"
-#include "build/build_config.h"
 #include "chrome/browser/browsing_data/browsing_data_file_system_util.h"
 #include "chrome/browser/browsing_data/chrome_browsing_data_model_delegate.h"
 #include "chrome/browser/content_settings/chrome_content_settings_utils.h"
@@ -13,6 +12,7 @@
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
 #include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
+#include "chrome/browser/permissions/system/system_permission_settings.h"
 #include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
@@ -41,10 +41,6 @@
 #include "pdf/pdf_features.h"
 #endif  // BUILDFLAG(ENABLE_PDF)
 
-#if BUILDFLAG(IS_MAC)
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
-#endif
-
 using content_settings::PageSpecificContentSettings;
 
 PageSpecificContentSettingsDelegate::PageSpecificContentSettingsDelegate(
@@ -263,22 +259,7 @@
   DCHECK(type == ContentSettingsType::MEDIASTREAM_MIC ||
          type == ContentSettingsType::MEDIASTREAM_CAMERA);
 
-#if BUILDFLAG(IS_MAC)
-  switch (type) {
-    case ContentSettingsType::MEDIASTREAM_CAMERA: {
-      return system_media_permissions::CheckSystemVideoCapturePermission() ==
-             system_media_permissions::SystemPermission::kDenied;
-    }
-    case ContentSettingsType::MEDIASTREAM_MIC: {
-      return system_media_permissions::CheckSystemAudioCapturePermission() ==
-             system_media_permissions::SystemPermission::kDenied;
-    }
-    default:
-      return false;
-  }
-#else
-  return false;
-#endif
+  return system_permission_settings::IsDenied(type);
 }
 
 bool PageSpecificContentSettingsDelegate::IsFrameAllowlistedForJavaScript(
diff --git a/chrome/browser/data_sharing/desktop/data_sharing_service_browsertest.cc b/chrome/browser/data_sharing/desktop/data_sharing_service_browsertest.cc
index 9cd2c6f..6bf2564 100644
--- a/chrome/browser/data_sharing/desktop/data_sharing_service_browsertest.cc
+++ b/chrome/browser/data_sharing/desktop/data_sharing_service_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/branding_buildflags.h"
 #include "chrome/browser/data_sharing/data_sharing_service_factory.h"
 #include "chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.h"
 #include "chrome/browser/ui/browser.h"
@@ -25,6 +26,11 @@
 };
 
 IN_PROC_BROWSER_TEST_F(DataSharingServiceBrowserTest, ReadGroup) {
+// In branded build, tests are skipped since it fetches data from the server.
+// In non-branded build, fake data are returned from a dummy implementation.
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  GTEST_SKIP() << "N/A for Google Chrome Branding Build";
+#else
   base::RunLoop run_loop;
   auto* service = data_sharing::DataSharingServiceFactory::GetForProfile(
       browser()->profile());
@@ -36,8 +42,17 @@
                  result) {
             EXPECT_EQ(data_sharing::GroupId("12345"),
                       result->group_token.group_id);
+            EXPECT_EQ("GROUP_NAME", result->display_name);
+            EXPECT_EQ(1u, result->members.size());
+            data_sharing::GroupMember member = result->members[0];
+            EXPECT_EQ("GAIA_ID", member.gaia_id);
+            EXPECT_EQ("MEMBER_NAME", member.display_name);
+            EXPECT_EQ("test@gmail.com", member.email);
+            EXPECT_EQ(data_sharing::MemberRole::kMember, member.role);
+            EXPECT_EQ(GURL("http://example.com"), member.avatar_url);
             run_loop->Quit();
           },
           &run_loop));
   run_loop.Run();
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 }
diff --git a/chrome/browser/download/bubble/download_display_controller.cc b/chrome/browser/download/bubble/download_display_controller.cc
index 4df81ca..d3c4bff 100644
--- a/chrome/browser/download/bubble/download_display_controller.cc
+++ b/chrome/browser/download/bubble/download_display_controller.cc
@@ -101,11 +101,11 @@
   if (display) {
     MaybeShowButtonWhenCreated();
   }
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 }
 
 DownloadDisplayController::~DownloadDisplayController() {
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 }
 
 void DownloadDisplayController::OnNewItem(bool show_animation) {
diff --git a/chrome/browser/educational_tip/BUILD.gn b/chrome/browser/educational_tip/BUILD.gn
index 2a9bdb0..7e94fe5 100644
--- a/chrome/browser/educational_tip/BUILD.gn
+++ b/chrome/browser/educational_tip/BUILD.gn
@@ -51,6 +51,7 @@
   resources_package = "org.chromium.chrome.browser.educational_tip"
 
   sources = [
+    "junit/src/org/chromium/chrome/browser/educational_tip/DefaultBrowserPromoCoordinatorUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleViewBinderUnitTest.java",
   ]
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderFactory.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderFactory.java
index 22215f6..88cbda9 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderFactory.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderFactory.java
@@ -17,9 +17,11 @@
      * @return An instance of EducationalTipCardProvider.
      */
     static EducationalTipCardProvider createInstance(
-            @NonNull Context context, @EducationalTipCardType int cardType) {
+            @NonNull Context context,
+            @EducationalTipCardType int cardType,
+            @NonNull Runnable onModuleClickedCallback) {
         if (cardType == EducationalTipCardType.DEFAULT_BROWSER_PROMO) {
-            return new DefaultBrowserPromoCoordinator(context);
+            return new DefaultBrowserPromoCoordinator(context, onModuleClickedCallback);
         }
 
         assert false : "Educational tip module's card type not supported!";
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediator.java
index 8264949..ab6d79738 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediator.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediator.java
@@ -30,7 +30,8 @@
         mModel = model;
         mModuleDelegate = moduleDelegate;
         mEducationalTipCardProvider =
-                EducationalTipCardProviderFactory.createInstance(mContext, getCardType());
+                EducationalTipCardProviderFactory.createInstance(
+                        mContext, getCardType(), this::removeModule);
     }
 
     /** Show the educational tip module. */
@@ -63,4 +64,12 @@
     int getModuleType() {
         return mModuleType;
     }
+
+    /**
+     * Called when the user has viewed the card information to remove the educational tip module
+     * from the magic stack.
+     */
+    private void removeModule() {
+        mModuleDelegate.removeModule(mModuleType);
+    }
 }
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoCoordinator.java
index e2ec3ef..e89b4d8 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoCoordinator.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoCoordinator.java
@@ -14,8 +14,14 @@
 public class DefaultBrowserPromoCoordinator implements EducationalTipCardProvider {
     private final Context mContext;
 
-    public DefaultBrowserPromoCoordinator(@NonNull Context context) {
+    // For the default browser promo card specifically, it is triggered only when the user clicks on
+    // the bottom sheet, directing them to the default app settings page.
+    private final Runnable mOnModuleClickedCallback;
+
+    public DefaultBrowserPromoCoordinator(
+            @NonNull Context context, @NonNull Runnable onModuleClickedCallback) {
         mContext = context;
+        mOnModuleClickedCallback = onModuleClickedCallback;
     }
 
     @Override
@@ -39,7 +45,6 @@
 
     @Override
     public void onCardClicked() {
-        // TODO(b/355015904): add a callback here to remind EducationalTipModuleCoordinator to
-        // refresh the module.
+        mOnModuleClickedCallback.run();
     }
 }
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/DefaultBrowserPromoCoordinatorUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/DefaultBrowserPromoCoordinatorUnitTest.java
new file mode 100644
index 0000000..1cc71f1c
--- /dev/null
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/DefaultBrowserPromoCoordinatorUnitTest.java
@@ -0,0 +1,59 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.educational_tip;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.chrome.browser.educational_tip.cards.DefaultBrowserPromoCoordinator;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.ui.shadows.ShadowAppCompatResources;
+
+/** Test relating to {@link DefaultBrowserPromoCoordinator} */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(
+        manifest = Config.NONE,
+        shadows = {ShadowAppCompatResources.class})
+public class DefaultBrowserPromoCoordinatorUnitTest {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Mock private Runnable mOnModuleClickedCallback;
+
+    private DefaultBrowserPromoCoordinator mDefaultBrowserPromoCoordinator;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mDefaultBrowserPromoCoordinator =
+                new DefaultBrowserPromoCoordinator(
+                        RuntimeEnvironment.application, mOnModuleClickedCallback);
+    }
+
+    @Test
+    @SmallTest
+    @EnableFeatures({ChromeFeatureList.EDUCATIONAL_TIP_MODULE})
+    public void testClickDefaultBrowserPromoCard() {
+        assertTrue(ChromeFeatureList.sEducationalTipModule.isEnabled());
+
+        mDefaultBrowserPromoCoordinator.onCardClicked();
+        verify(mOnModuleClickedCallback, times(1)).run();
+    }
+}
diff --git a/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.cc b/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.cc
index a2b56399..ad734f5 100644
--- a/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.cc
+++ b/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.cc
@@ -121,7 +121,10 @@
 
 content::NavigationThrottle::ThrottleCheckResult
 OidcAuthResponseCaptureNavigationThrottle::WillProcessResponse() {
-  return AttemptToTriggerInterception();
+  return (base::FeatureList::IsEnabled(
+             profile_management::features::kOidcAuthResponseInterception))
+             ? AttemptToTriggerInterception()
+             : PROCEED;
 }
 
 const url_matcher::URLMatcher*
diff --git a/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle_unittest.cc b/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle_unittest.cc
index cab104e..7a68def 100644
--- a/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle_unittest.cc
+++ b/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle_unittest.cc
@@ -87,27 +87,52 @@
                             id_token_field.c_str(), state_field.c_str());
 }
 
+// Convenient helper function that builds valid OIDC response URL using valid
+// tokens
+std::string BuildStandardResponseUrl(const std::string& oidc_state) {
+  std::string auth_token = BuildTokenFromDict(
+      base::Value::Dict()
+          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
+          .Set(kSubjectClaimName, kExampleAuthSubject));
+  std::string id_token = BuildTokenFromDict(
+      base::Value::Dict()
+          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
+          .Set(kSubjectClaimName, kExampleIdSubject)
+          .Set(kIssuerClaimName, kExampleIdIssuer));
+  return BuildOidcResponseUrl(auth_token, id_token, oidc_state);
+}
+
 }  // namespace
 
 namespace profile_management {
 
+bool IsSourceUrlValid(std::string url_string) {
+  return !OidcAuthResponseCaptureNavigationThrottle::
+              GetOidcEnrollmentUrlMatcher()
+                  ->MatchURL(GURL(url_string))
+                  .empty();
+}
+
 class OidcAuthResponseCaptureNavigationThrottleTest
     : public BrowserWithTestWindowTest,
-      public testing::WithParamInterface<std::tuple<bool, bool>> {
+      public testing::WithParamInterface<std::tuple<bool, bool, bool>> {
  public:
   OidcAuthResponseCaptureNavigationThrottleTest() {
     scoped_feature_list_.InitWithFeatureStates(
         {{features::kOidcAuthProfileManagement, enable_oidc_interception()},
          {features::kEnableGenericOidcAuthProfileManagement,
-          enable_generic_oidc()}});
+          enable_generic_oidc()},
+         {features::kOidcAuthResponseInterception, enable_process_response()}});
   }
 
   OidcAuthResponseCaptureNavigationThrottleTest(bool enable_oidc_interception,
-                                                bool enable_generic_oidc) {
+                                                bool enable_generic_oidc,
+                                                bool enable_process_response) {
     scoped_feature_list_.InitWithFeatureStates(
         {{features::kOidcAuthProfileManagement, enable_oidc_interception},
          {features::kEnableGenericOidcAuthProfileManagement,
-          enable_generic_oidc}});
+          enable_generic_oidc},
+         {features::kOidcAuthResponseInterception, enable_process_response}});
   }
 
   ~OidcAuthResponseCaptureNavigationThrottleTest() override = default;
@@ -147,7 +172,15 @@
     EXPECT_EQ(tokens.state, expected_tokens.state);
   }
 
-  void ExpectNoOidcInterception(
+  void SetupRedirectionForHandle(
+      content::MockNavigationHandle& navigation_handle,
+      std::vector<GURL> source_urls,
+      const GURL& last_url) {
+    navigation_handle.set_url(last_url);
+    navigation_handle.set_redirect_chain(source_urls);
+  }
+
+  void RunThrottleAndExpectNoOidcInterception(
       MockOidcAuthenticationSigninInterceptor* oidc_interceptor,
       const std::string& redirection_url,
       NavigationThrottle::ThrottleAction expected_throttle_action) {
@@ -160,26 +193,29 @@
                   MaybeInterceptOidcAuthentication(_, _, _, _, _))
           .Times(0);
     }
+
     auto throttle =
         OidcAuthResponseCaptureNavigationThrottle::MaybeCreateThrottleFor(
             &navigation_handle);
-
     if (!enable_oidc_interception()) {
       ASSERT_EQ(nullptr, throttle.get());
-    } else {
-      if (expected_throttle_action == NavigationThrottle::DEFER) {
-        throttle->set_resume_callback_for_testing(
-            task_environment()->QuitClosure());
-      }
-      navigation_handle.set_url(GURL(redirection_url));
-      EXPECT_EQ(expected_throttle_action,
-                throttle->WillProcessResponse().action());
+      return;
+    }
 
-      if (expected_throttle_action == NavigationThrottle::DEFER) {
-        task_environment()->RunUntilQuit();
-      } else {
-        task_environment()->RunUntilIdle();
-      }
+    if (expected_throttle_action == NavigationThrottle::DEFER) {
+      throttle->set_resume_callback_for_testing(
+          task_environment()->QuitClosure());
+    }
+
+    SetupRedirectionForHandle(
+        navigation_handle,
+        {GURL(kOidcEntraReprocessUrl), GURL(redirection_url)},
+        GURL(redirection_url));
+    EXPECT_EQ(expected_throttle_action,
+              throttle->WillRedirectRequest().action());
+
+    if (expected_throttle_action == NavigationThrottle::DEFER) {
+      task_environment()->RunUntilQuit();
     }
   }
 
@@ -204,16 +240,6 @@
   }
 
   void TestNoServiceForInvalidProfile(Profile* invalid_profile) {
-    std::string auth_token = BuildTokenFromDict(
-        base::Value::Dict()
-            .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
-            .Set(kSubjectClaimName, kExampleAuthSubject));
-    std::string id_token = BuildTokenFromDict(
-        base::Value::Dict()
-            .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
-            .Set(kSubjectClaimName, kExampleIdSubject)
-            .Set(kIssuerClaimName, kExampleIdIssuer));
-
     auto* oidc_interceptor =
         static_cast<MockOidcAuthenticationSigninInterceptor*>(
             OidcAuthenticationSigninInterceptorFactory::GetForProfile(
@@ -232,14 +258,15 @@
     if (!enable_oidc_interception()) {
       ASSERT_EQ(nullptr, throttle.get());
     } else {
-      navigation_handle.set_url(GURL(BuildOidcResponseUrl(
-          auth_token, id_token, /*oidc_state=*/std::string())));
-      navigation_handle.set_redirect_chain(
-          {GURL(kOidcEntraReprocessUrl),
-           GURL(BuildOidcResponseUrl(auth_token, id_token,
-                                     /*oidc_state=*/std::string()))});
+      std::string redirection_url =
+          BuildStandardResponseUrl(/*oidc_state=*/std::string());
+      SetupRedirectionForHandle(
+          navigation_handle,
+          {GURL(kOidcEntraReprocessUrl), GURL(redirection_url)},
+          GURL(redirection_url));
+
       EXPECT_EQ(NavigationThrottle::PROCEED,
-                throttle->WillProcessResponse().action());
+                throttle->WillRedirectRequest().action());
       task_environment()->RunUntilIdle();
       CheckFunnelAndResultHistogram(
           OidcInterceptionFunnelStep::kValidRedirectionCaptured,
@@ -268,11 +295,14 @@
     auto* oidc_interceptor = GetMockOidcInterceptor();
     if (!enable_oidc_interception()) {
       ASSERT_EQ(nullptr, oidc_interceptor);
-    } else if (enable_generic_oidc() ||
-               source_url != kOidcNonEntraReprocessUrl) {
+    } else if (enable_generic_oidc() || IsSourceUrlValid(source_url)) {
       ExpectOidcInterception(
           oidc_interceptor,
           ProfileManagementOidcTokens(auth_token, id_token, oidc_state));
+    } else {
+      EXPECT_CALL(*oidc_interceptor,
+                  MaybeInterceptOidcAuthentication(_, _, _, _, _))
+          .Times(0);
     }
 
     auto throttle =
@@ -280,17 +310,21 @@
             &navigation_handle);
     if (!enable_oidc_interception()) {
       ASSERT_EQ(nullptr, throttle.get());
-    } else if (!enable_generic_oidc() &&
-               source_url == kOidcNonEntraReprocessUrl) {
-      navigation_handle.set_url(GURL(redirection_url));
+      return;
+    }
+
+    SetupRedirectionForHandle(navigation_handle,
+                              {GURL(source_url), GURL(redirection_url)},
+                              GURL(redirection_url));
+
+    if (!enable_generic_oidc() && !IsSourceUrlValid(source_url)) {
       EXPECT_EQ(NavigationThrottle::PROCEED,
-                throttle->WillProcessResponse().action());
+                throttle->WillRedirectRequest().action());
     } else {
       throttle->set_resume_callback_for_testing(
           task_environment()->QuitClosure());
-      navigation_handle.set_url(GURL(redirection_url));
       EXPECT_EQ(NavigationThrottle::DEFER,
-                throttle->WillProcessResponse().action());
+                throttle->WillRedirectRequest().action());
       task_environment()->RunUntilQuit();
     }
   }
@@ -321,6 +355,7 @@
 
   bool enable_oidc_interception() { return std::get<0>(GetParam()); }
   bool enable_generic_oidc() { return std::get<1>(GetParam()); }
+  bool enable_process_response() { return std::get<2>(GetParam()); }
 
  protected:
   data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
@@ -331,19 +366,9 @@
 };
 
 TEST_P(OidcAuthResponseCaptureNavigationThrottleTest,
-       DirectNavigationOnGenericOidcOnly) {
-  std::string auth_token = BuildTokenFromDict(
-      base::Value::Dict()
-          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
-          .Set(kSubjectClaimName, kExampleAuthSubject));
-  std::string id_token = BuildTokenFromDict(
-      base::Value::Dict()
-          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
-          .Set(kSubjectClaimName, kExampleIdSubject)
-          .Set(kIssuerClaimName, kExampleIdIssuer));
-
+       NoInterceptionForDirectNavigation) {
   std::string direct_navigate_url =
-      BuildOidcResponseUrl(auth_token, id_token, /*oidc_state=*/std::string());
+      BuildStandardResponseUrl(/*oidc_state=*/std::string());
 
   content::MockNavigationHandle navigation_handle(GURL(direct_navigate_url),
                                                   main_frame());
@@ -351,10 +376,6 @@
 
   if (!enable_oidc_interception()) {
     ASSERT_EQ(nullptr, oidc_interceptor);
-  } else if (enable_generic_oidc()) {
-    ExpectOidcInterception(
-        oidc_interceptor,
-        ProfileManagementOidcTokens(auth_token, id_token, /*state=*/""));
   } else {
     EXPECT_CALL(*oidc_interceptor,
                 MaybeInterceptOidcAuthentication(_, _, _, _, _))
@@ -365,16 +386,7 @@
       OidcAuthResponseCaptureNavigationThrottle::MaybeCreateThrottleFor(
           &navigation_handle);
 
-  if (enable_generic_oidc() && enable_oidc_interception()) {
-    throttle->set_resume_callback_for_testing(
-        task_environment()->QuitClosure());
-    navigation_handle.set_url(GURL(direct_navigate_url));
-    EXPECT_EQ(NavigationThrottle::DEFER,
-              throttle->WillProcessResponse().action());
-    task_environment()->RunUntilQuit();
-    CheckFunnelAndResultHistogram(
-        OidcInterceptionFunnelStep::kSuccessfulInfoParsed, std::nullopt);
-  } else if (enable_oidc_interception()) {
+  if (enable_oidc_interception()) {
     navigation_handle.set_url(GURL(direct_navigate_url));
     EXPECT_EQ(NavigationThrottle::PROCEED,
               throttle->WillProcessResponse().action());
@@ -383,6 +395,56 @@
   }
 }
 
+TEST_P(OidcAuthResponseCaptureNavigationThrottleTest, MissingRedirectionChain) {
+  std::string auth_token = BuildTokenFromDict(
+      base::Value::Dict()
+          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
+          .Set(kSubjectClaimName, kExampleAuthSubject));
+  std::string id_token = BuildTokenFromDict(
+      base::Value::Dict()
+          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
+          .Set(kSubjectClaimName, kExampleIdSubject)
+          .Set(kIssuerClaimName, kExampleIdIssuer));
+  std::string url_without_redirection_chain =
+      BuildOidcResponseUrl(auth_token, id_token, /*state=*/std::string());
+
+  content::MockNavigationHandle navigation_handle(
+      GURL(url_without_redirection_chain), main_frame());
+  auto* oidc_interceptor = GetMockOidcInterceptor();
+
+  if (!enable_oidc_interception()) {
+    ASSERT_EQ(nullptr, oidc_interceptor);
+  } else if (enable_generic_oidc()) {
+    ExpectOidcInterception(
+        oidc_interceptor, ProfileManagementOidcTokens(auth_token, id_token,
+                                                      /*state=*/std::string()));
+  } else {
+    EXPECT_CALL(*oidc_interceptor,
+                MaybeInterceptOidcAuthentication(_, _, _, _, _))
+        .Times(0);
+  }
+
+  auto throttle =
+      OidcAuthResponseCaptureNavigationThrottle::MaybeCreateThrottleFor(
+          &navigation_handle);
+
+  if (!enable_oidc_interception()) {
+    ASSERT_EQ(nullptr, throttle.get());
+    return;
+  }
+
+  if (enable_generic_oidc()) {
+    throttle->set_resume_callback_for_testing(
+        task_environment()->QuitClosure());
+    EXPECT_EQ(NavigationThrottle::DEFER,
+              throttle->WillRedirectRequest().action());
+    task_environment()->RunUntilQuit();
+  } else {
+    EXPECT_EQ(NavigationThrottle::PROCEED,
+              throttle->WillRedirectRequest().action());
+  }
+}
+
 TEST_P(OidcAuthResponseCaptureNavigationThrottleTest, SuccessfulInterception) {
   TestInterceptionForUrl(/*add_oidc_state=*/false,
                          /*source_url=*/kOidcEntraReprocessUrl);
@@ -398,8 +460,11 @@
       OidcInterceptionFunnelStep::kSuccessfulInfoParsed, std::nullopt);
 }
 
+// Test case for when the source URL of OIDC authentication is not considered to
+// be valid. We should only consider interception if Generic OIDC flag is
+// enabled.
 TEST_P(OidcAuthResponseCaptureNavigationThrottleTest,
-       SuccessfulInterceptionWithState_nonEntraUrl) {
+       SuccessfulInterceptionWithState_invalidSourceUrl) {
   TestInterceptionForUrl(/*add_oidc_state=*/true,
                          /*source_url=*/kOidcNonEntraReprocessUrl);
 
@@ -409,16 +474,6 @@
   }
 }
 
-TEST_P(OidcAuthResponseCaptureNavigationThrottleTest, MismatchingHost) {
-  TestInterceptionForUrl(/*add_oidc_state=*/false,
-                         /*source_url=*/kOidcNonEntraReprocessUrl);
-
-  if (enable_generic_oidc()) {
-    CheckFunnelAndResultHistogram(
-        OidcInterceptionFunnelStep::kSuccessfulInfoParsed, std::nullopt);
-  }
-}
-
 TEST_P(OidcAuthResponseCaptureNavigationThrottleTest, MsftKmsiThrottling) {
   TestInterceptionForUrl(/*add_oidc_state=*/false,
                          /*source_url=*/kOidcEntraKmsiUrl);
@@ -453,8 +508,8 @@
       /*oidc_auth_token=*/std::string(), id_token, /*state=*/std::string());
 
   auto* oidc_interceptor = GetMockOidcInterceptor();
-  ExpectNoOidcInterception(oidc_interceptor, redirection_url,
-                           NavigationThrottle::PROCEED);
+  RunThrottleAndExpectNoOidcInterception(oidc_interceptor, redirection_url,
+                                         NavigationThrottle::PROCEED);
   CheckFunnelAndResultHistogram(
       OidcInterceptionFunnelStep::kValidRedirectionCaptured,
       OidcInterceptionResult::kInvalidUrlOrTokens);
@@ -470,8 +525,8 @@
       auth_token, /*oidc_id_token=*/std::string(), /*state=*/std::string());
 
   auto* oidc_interceptor = GetMockOidcInterceptor();
-  ExpectNoOidcInterception(oidc_interceptor, redirection_url,
-                           NavigationThrottle::PROCEED);
+  RunThrottleAndExpectNoOidcInterception(oidc_interceptor, redirection_url,
+                                         NavigationThrottle::PROCEED);
   CheckFunnelAndResultHistogram(
       OidcInterceptionFunnelStep::kValidRedirectionCaptured,
       OidcInterceptionResult::kInvalidUrlOrTokens);
@@ -490,8 +545,8 @@
       BuildOidcResponseUrl(auth_token, id_token, /*oidc_state=*/std::string());
 
   auto* oidc_interceptor = GetMockOidcInterceptor();
-  ExpectNoOidcInterception(oidc_interceptor, redirection_url,
-                           NavigationThrottle::DEFER);
+  RunThrottleAndExpectNoOidcInterception(oidc_interceptor, redirection_url,
+                                         NavigationThrottle::DEFER);
   CheckFunnelAndResultHistogram(
       OidcInterceptionFunnelStep::kValidRedirectionCaptured,
       OidcInterceptionResult::kInvalidUrlOrTokens);
@@ -509,8 +564,8 @@
       BuildOidcResponseUrl(auth_token, id_token, /*oidc_state=*/std::string());
 
   auto* oidc_interceptor = GetMockOidcInterceptor();
-  ExpectNoOidcInterception(oidc_interceptor, redirection_url,
-                           NavigationThrottle::DEFER);
+  RunThrottleAndExpectNoOidcInterception(oidc_interceptor, redirection_url,
+                                         NavigationThrottle::DEFER);
   CheckFunnelAndResultHistogram(
       OidcInterceptionFunnelStep::kValidRedirectionCaptured,
       OidcInterceptionResult::kInvalidUrlOrTokens);
@@ -525,8 +580,8 @@
       BuildOidcResponseUrl(auth_token, id_token, /*oidc_state=*/std::string());
 
   auto* oidc_interceptor = GetMockOidcInterceptor();
-  ExpectNoOidcInterception(oidc_interceptor, redirection_url,
-                           NavigationThrottle::DEFER);
+  RunThrottleAndExpectNoOidcInterception(oidc_interceptor, redirection_url,
+                                         NavigationThrottle::DEFER);
   CheckFunnelAndResultHistogram(
       OidcInterceptionFunnelStep::kValidRedirectionCaptured,
       OidcInterceptionResult::kInvalidUrlOrTokens);
@@ -552,8 +607,8 @@
       auth_token, malformed_id_token, /*oidc_state=*/std::string());
 
   auto* oidc_interceptor = GetMockOidcInterceptor();
-  ExpectNoOidcInterception(oidc_interceptor, redirection_url,
-                           NavigationThrottle::CANCEL_AND_IGNORE);
+  RunThrottleAndExpectNoOidcInterception(oidc_interceptor, redirection_url,
+                                         NavigationThrottle::CANCEL_AND_IGNORE);
   CheckFunnelAndResultHistogram(
       OidcInterceptionFunnelStep::kValidRedirectionCaptured,
       OidcInterceptionResult::kInvalidUrlOrTokens);
@@ -580,8 +635,8 @@
       auth_token, malformed_id_token, /*oidc_state=*/std::string());
 
   auto* oidc_interceptor = GetMockOidcInterceptor();
-  ExpectNoOidcInterception(oidc_interceptor, redirection_url,
-                           NavigationThrottle::CANCEL_AND_IGNORE);
+  RunThrottleAndExpectNoOidcInterception(oidc_interceptor, redirection_url,
+                                         NavigationThrottle::CANCEL_AND_IGNORE);
   CheckFunnelAndResultHistogram(
       OidcInterceptionFunnelStep::kValidRedirectionCaptured,
       OidcInterceptionResult::kInvalidUrlOrTokens);
@@ -589,22 +644,13 @@
 
 TEST_P(OidcAuthResponseCaptureNavigationThrottleTest, DataDecoderFailure) {
   in_process_data_decoder_.SimulateJsonParserCrash(/*drop=*/true);
-  std::string auth_token = BuildTokenFromDict(
-      base::Value::Dict()
-          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
-          .Set(kSubjectClaimName, kExampleAuthSubject));
-  std::string id_token = BuildTokenFromDict(
-      base::Value::Dict()
-          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
-          .Set(kSubjectClaimName, kExampleIdSubject)
-          .Set(kIssuerClaimName, kExampleIdIssuer));
 
   std::string redirection_url =
-      BuildOidcResponseUrl(auth_token, id_token, /*oidc_state=*/std::string());
+      BuildStandardResponseUrl(/*oidc_state=*/std::string());
 
   auto* oidc_interceptor = GetMockOidcInterceptor();
-  ExpectNoOidcInterception(oidc_interceptor, redirection_url,
-                           NavigationThrottle::DEFER);
+  RunThrottleAndExpectNoOidcInterception(oidc_interceptor, redirection_url,
+                                         NavigationThrottle::DEFER);
   CheckFunnelAndResultHistogram(
       OidcInterceptionFunnelStep::kValidRedirectionCaptured,
       OidcInterceptionResult::kInvalidUrlOrTokens);
@@ -638,7 +684,82 @@
     All,
     OidcAuthResponseCaptureNavigationThrottleTest,
     testing::Combine(/*enable_oidc_interception=*/testing::Bool(),
-                     /*enable_generic_oidc=*/testing::Bool()));
+                     /*enable_generic_oidc=*/testing::Bool(),
+                     /*enable_process_response=*/testing::Values(false)));
+
+// Test class dedicated for OIDC throttle regarding WillProcessResponse
+class OidcAuthNavigationThrottleProcessResponseTest
+    : public OidcAuthResponseCaptureNavigationThrottleTest {
+ public:
+  OidcAuthNavigationThrottleProcessResponseTest()
+      : OidcAuthResponseCaptureNavigationThrottleTest() {}
+
+  ~OidcAuthNavigationThrottleProcessResponseTest() override = default;
+};
+
+TEST_P(OidcAuthNavigationThrottleProcessResponseTest, ProcessResponse) {
+  std::string auth_token = BuildTokenFromDict(
+      base::Value::Dict()
+          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
+          .Set(kSubjectClaimName, kExampleAuthSubject));
+  std::string id_token = BuildTokenFromDict(
+      base::Value::Dict()
+          .Set(kUserPrincipleNameClaimName, kExampleUserPrincipleName)
+          .Set(kSubjectClaimName, kExampleIdSubject)
+          .Set(kIssuerClaimName, kExampleIdIssuer));
+
+  std::string direct_navigate_url =
+      BuildOidcResponseUrl(auth_token, id_token, /*oidc_state=*/std::string());
+
+  content::MockNavigationHandle navigation_handle(GURL(direct_navigate_url),
+                                                  main_frame());
+  auto* oidc_interceptor = GetMockOidcInterceptor();
+
+  if (!enable_oidc_interception()) {
+    ASSERT_EQ(nullptr, oidc_interceptor);
+  } else if (enable_process_response()) {
+    ExpectOidcInterception(
+        oidc_interceptor,
+        ProfileManagementOidcTokens(auth_token, id_token, /*state=*/""));
+  } else {
+    EXPECT_CALL(*oidc_interceptor,
+                MaybeInterceptOidcAuthentication(_, _, _, _, _))
+        .Times(0);
+  }
+
+  auto throttle =
+      OidcAuthResponseCaptureNavigationThrottle::MaybeCreateThrottleFor(
+          &navigation_handle);
+
+  if (enable_process_response() && enable_oidc_interception()) {
+    throttle->set_resume_callback_for_testing(
+        task_environment()->QuitClosure());
+
+    SetupRedirectionForHandle(
+        navigation_handle,
+        {GURL(kOidcEntraReprocessUrl), GURL(direct_navigate_url)},
+        GURL(direct_navigate_url));
+
+    EXPECT_EQ(NavigationThrottle::DEFER,
+              throttle->WillProcessResponse().action());
+    task_environment()->RunUntilQuit();
+    CheckFunnelAndResultHistogram(
+        OidcInterceptionFunnelStep::kSuccessfulInfoParsed, std::nullopt);
+  } else if (enable_oidc_interception()) {
+    navigation_handle.set_url(GURL(direct_navigate_url));
+    EXPECT_EQ(NavigationThrottle::PROCEED,
+              throttle->WillProcessResponse().action());
+  } else {
+    ASSERT_EQ(nullptr, throttle);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    OidcAuthNavigationThrottleProcessResponseTest,
+    testing::Combine(/*enable_oidc_interception=*/testing::Bool(),
+                     /*enable_generic_oidc=*/testing::Bool(),
+                     /*enable_process_response=*/testing::Values(true)));
 
 // Test class dedicated to test if OIDC throttle validatation accepts the
 // correct set of URLs.
@@ -648,15 +769,13 @@
   OidcAuthNavigationThrottleUrlMatchingTest()
       : OidcAuthResponseCaptureNavigationThrottleTest(
             /*enable_oidc_interception=*/true,
-            /*enable_generic_oidc=*/false) {}
+            /*enable_generic_oidc=*/false,
+            /*enable_process_response=*/false) {}
 
   ~OidcAuthNavigationThrottleUrlMatchingTest() override = default;
 
   void TestUrlMatching(std::string url_string, bool expect_matched = true) {
-    ASSERT_EQ(expect_matched, !OidcAuthResponseCaptureNavigationThrottle::
-                                   GetOidcEnrollmentUrlMatcher()
-                                       ->MatchURL(GURL(url_string))
-                                       .empty());
+    ASSERT_EQ(expect_matched, IsSourceUrlValid(url_string));
   }
 };
 
diff --git a/chrome/browser/enterprise/profile_management/profile_management_features.cc b/chrome/browser/enterprise/profile_management/profile_management_features.cc
index 2f76e4bf..5987399 100644
--- a/chrome/browser/enterprise/profile_management/profile_management_features.cc
+++ b/chrome/browser/enterprise/profile_management/profile_management_features.cc
@@ -20,6 +20,10 @@
              "OidcAuthProfileManagement",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+BASE_FEATURE(kOidcAuthResponseInterception,
+             "OidcAuthResponseInterception",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kOidcEnrollmentTimeout,
              "kOidcEnrollmentTimeout",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/chrome/browser/enterprise/profile_management/profile_management_features.h b/chrome/browser/enterprise/profile_management/profile_management_features.h
index 448c5dcf..6dfe3e2 100644
--- a/chrome/browser/enterprise/profile_management/profile_management_features.h
+++ b/chrome/browser/enterprise/profile_management/profile_management_features.h
@@ -20,6 +20,10 @@
 // Controls whether OIDC-response profile management is enabled.
 BASE_DECLARE_FEATURE(kOidcAuthProfileManagement);
 
+// Controls whether OIDC profile enrollment can be started from navigation
+// response in addition to redirections.
+BASE_DECLARE_FEATURE(kOidcAuthResponseInterception);
+
 // Controls whether OIDC enrollment process can time out (and after how long).
 BASE_DECLARE_FEATURE(kOidcEnrollmentTimeout);
 
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 8c5d429a..b20414a5 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -243,10 +243,12 @@
   } else {
     Browser* browser =
         ChromeExtensionFunctionDetails(function).GetCurrentBrowser();
-    if (!browser)
+    if (!browser) {
       *error = tabs_constants::kNoCurrentWindowError;
-    else if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, nullptr))
+    } else if (!ExtensionTabUtil::GetActiveTab(browser, &web_contents,
+                                               nullptr)) {
       *error = tabs_constants::kNoSelectedTabError;
+    }
   }
   return web_contents;
 }
@@ -1553,8 +1555,10 @@
   if (DevToolsWindow::IsDevToolsWindow(contents))
     return RespondNow(Error(tabs_constants::kNotAllowedForDevToolsError));
 
-  if (!ExtensionTabUtil::BrowserSupportsTabs(browser))
+  // GetTabById may return a null browser for prerender tabs.
+  if (!browser || !ExtensionTabUtil::BrowserSupportsTabs(browser)) {
     return RespondNow(Error(tabs_constants::kNoCurrentWindowError));
+  }
 
   web_contents_ = contents;
 
@@ -1872,9 +1876,10 @@
     if (!current_browser)
       return RespondNow(Error(tabs_constants::kNoCurrentWindowError));
 
-    if (!ExtensionTabUtil::GetDefaultTab(current_browser, &web_contents,
-                                         nullptr))
+    if (!ExtensionTabUtil::GetActiveTab(current_browser, &web_contents,
+                                        nullptr)) {
       return RespondNow(Error(kUnknownErrorDoNotUse));
+    }
 
     browser = current_browser;
   } else {
@@ -2357,10 +2362,10 @@
                     &browser, nullptr, &contents, nullptr, &error)) {
       return RespondNow(Error(std::move(error)));
     }
-    // TODO(devlin): Can this happen? GetTabById() should return false if
-    // |browser| or |contents| is null.
-    if (!browser || !contents)
+    // The browser will be null for prerender tabs.
+    if (!browser) {
       return RespondNow(Error(kUnknownErrorDoNotUse));
+    }
   } else {
     browser = ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
     if (!browser)
@@ -2482,8 +2487,9 @@
       return set_init_result_error(tabs_constants::kNoCurrentWindowError);
     content::WebContents* web_contents = nullptr;
     // Can happen during shutdown.
-    if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
+    if (!ExtensionTabUtil::GetActiveTab(browser, &web_contents, &tab_id)) {
       return set_init_result_error(tabs_constants::kNoTabInBrowserWindowError);
+    }
   }
 
   execute_tab_id_ = tab_id;
diff --git a/chrome/browser/extensions/extension_browser_window.h b/chrome/browser/extensions/extension_browser_window.h
index f6a960f..bbb51e6 100644
--- a/chrome/browser/extensions/extension_browser_window.h
+++ b/chrome/browser/extensions/extension_browser_window.h
@@ -12,6 +12,10 @@
 
 class Browser;
 
+namespace content {
+class WebContents;
+}
+
 namespace extensions {
 
 class Extension;
@@ -43,11 +47,11 @@
   // Returns the tabs:: API constant for the window type of the `browser`.
   virtual std::string GetBrowserWindowTypeText() const = 0;
 
-  // Creates a base::Value::Dict representing the window for the given
-  // `browser`, and scrubs any privacy-sensitive data that `extension` does not
-  // have access to. `populate_tab_behavior` determines whether tabs will be
-  // populated in the result. `context` is used to determine the
-  // ScrubTabBehavior for the populated tabs data.
+  // Creates a base::Value::Dict representing the window for the browser and
+  // scrubs any privacy-sensitive data that `extension` does not have access to.
+  // `populate_tab_behavior` determines whether tabs will be populated in the
+  // result. `context` is used to determine the ScrubTabBehavior for the
+  // populated tabs data.
   // TODO(devlin): Convert this to a api::Windows::Window object.
   virtual base::Value::Dict CreateWindowValueForExtension(
       const Extension* extension,
@@ -58,6 +62,21 @@
   // chrome.tabs.getAllInWindow() extensions API.
   virtual base::Value::List CreateTabList(const Extension* extension,
                                           mojom::ContextType context) const = 0;
+
+  // On success, returns true and fills in the WebContents and extensions API
+  // tab ID for the active tab. The optional_tab_id may be null if the caller
+  // doesn't need it. Returns false if there is no active tab.
+  virtual bool GetActiveTab(content::WebContents** contents,
+                            int* optional_tab_id) const = 0;
+
+  // Open the extension's options page. Returns true if an options page was
+  // successfully opened (though it may not necessarily *load*, e.g. if the
+  // URL does not exist).
+  virtual bool OpenOptionsPage(const Extension* extension) = 0;
+
+  // Returns true if the Browser can report tabs to extensions. Example of
+  // Browsers which don't support tabs include apps and devtools.
+  virtual bool SupportsTabs() = 0;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_browser_window_desktop.cc b/chrome/browser/extensions/extension_browser_window_desktop.cc
index cf83d52..4d9fc953 100644
--- a/chrome/browser/extensions/extension_browser_window_desktop.cc
+++ b/chrome/browser/extensions/extension_browser_window_desktop.cc
@@ -10,7 +10,11 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
+#include "chrome/browser/ui/singleton_tabs.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/common/manifest_handlers/incognito_info.h"
+#include "extensions/common/manifest_handlers/options_page_info.h"
 #include "ui/base/base_window.h"
 
 namespace extensions {
@@ -109,4 +113,71 @@
   return tab_list;
 }
 
+bool ExtensionBrowserWindowDesktop::GetActiveTab(
+    content::WebContents** contents,
+    int* optional_tab_id) const {
+  DCHECK(contents);
+
+  *contents = browser_->tab_strip_model()->GetActiveWebContents();
+  if (*contents) {
+    if (optional_tab_id) {
+      *optional_tab_id = ExtensionTabUtil::GetTabId(*contents);
+    }
+    return true;
+  }
+
+  return false;
+}
+
+bool ExtensionBrowserWindowDesktop::OpenOptionsPage(
+    const Extension* extension) {
+  if (!OptionsPageInfo::HasOptionsPage(extension)) {
+    return false;
+  }
+
+  // Force the options page to open in non-OTR window if the extension is not
+  // running in split mode, because it won't be able to save settings from OTR.
+  // This version of OpenOptionsPage() can be called from an OTR window via e.g.
+  // the action menu, since that's not initiated by the extension.
+  Browser* browser_to_use = &browser_.get();
+  std::unique_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer;
+  if (browser_->profile()->IsOffTheRecord() &&
+      !IncognitoInfo::IsSplitMode(extension)) {
+    displayer = std::make_unique<chrome::ScopedTabbedBrowserDisplayer>(
+        browser_->profile()->GetOriginalProfile());
+    browser_to_use = displayer->browser();
+  }
+
+  GURL url_to_navigate;
+  bool open_in_tab = OptionsPageInfo::ShouldOpenInTab(extension);
+  if (open_in_tab) {
+    // Options page tab is simply e.g. chrome-extension://.../options.html.
+    url_to_navigate = OptionsPageInfo::GetOptionsPage(extension);
+  } else {
+    // Options page tab is Extension settings pointed at that Extension's ID,
+    // e.g. chrome://extensions?options=...
+    url_to_navigate = GURL(chrome::kChromeUIExtensionsURL);
+    GURL::Replacements replacements;
+    std::string query =
+        base::StringPrintf("options=%s", extension->id().c_str());
+    replacements.SetQueryStr(query);
+    url_to_navigate = url_to_navigate.ReplaceComponents(replacements);
+  }
+
+  // We need to respect path differences because we don't want opening the
+  // options page to close a page that might be open to extension content.
+  // However, if the options page opens inside the chrome://extensions page, we
+  // can override an existing page.
+  // Note: ref behavior is to ignore.
+  ShowSingletonTabOverwritingNTP(browser_to_use, url_to_navigate,
+                                 open_in_tab
+                                     ? NavigateParams::RESPECT
+                                     : NavigateParams::IGNORE_AND_NAVIGATE);
+  return true;
+}
+
+bool ExtensionBrowserWindowDesktop::SupportsTabs() {
+  return !browser_->is_type_devtools();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_browser_window_desktop.h b/chrome/browser/extensions/extension_browser_window_desktop.h
index e3d5497..8dcb1ca 100644
--- a/chrome/browser/extensions/extension_browser_window_desktop.h
+++ b/chrome/browser/extensions/extension_browser_window_desktop.h
@@ -27,6 +27,10 @@
       mojom::ContextType context) const override;
   base::Value::List CreateTabList(const Extension* extension,
                                   mojom::ContextType context) const override;
+  bool GetActiveTab(content::WebContents** contents,
+                    int* tab_id) const override;
+  bool OpenOptionsPage(const Extension* extension) override;
+  bool SupportsTabs() override;
 
  private:
   const raw_ref<Browser> browser_;
diff --git a/chrome/browser/extensions/extension_protocols_unittest.cc b/chrome/browser/extensions/extension_protocols_unittest.cc
index d16b803..60cfa97c 100644
--- a/chrome/browser/extensions/extension_protocols_unittest.cc
+++ b/chrome/browser/extensions/extension_protocols_unittest.cc
@@ -276,7 +276,7 @@
     loader_factory_.reset();
     content_verifier_->Shutdown();
     // Shut down the PowerMonitor if initialized.
-    base::PowerMonitor::ShutdownForTesting();
+    base::PowerMonitor::GetInstance()->ShutdownForTesting();
   }
 
   GetResult RequestOrLoad(const GURL& url,
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index 8f6b690a..bc7e1a5 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -639,20 +639,11 @@
   return false;
 }
 
-bool ExtensionTabUtil::GetDefaultTab(Browser* browser,
-                                     WebContents** contents,
-                                     int* tab_id) {
-  DCHECK(browser);
-  DCHECK(contents);
-
-  *contents = browser->tab_strip_model()->GetActiveWebContents();
-  if (*contents) {
-    if (tab_id)
-      *tab_id = GetTabId(*contents);
-    return true;
-  }
-
-  return false;
+bool ExtensionTabUtil::GetActiveTab(Browser* browser,
+                                    WebContents** contents,
+                                    int* optional_tab_id) {
+  return ExtensionWindowFromBrowser(browser)->GetActiveTab(contents,
+                                                           optional_tab_id);
 }
 
 // static
@@ -1003,47 +994,7 @@
 
 bool ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
                                        Browser* browser) {
-  if (!OptionsPageInfo::HasOptionsPage(extension))
-    return false;
-
-  // Force the options page to open in non-OTR window if the extension is not
-  // running in split mode, because it won't be able to save settings from OTR.
-  // This version of OpenOptionsPage() can be called from an OTR window via e.g.
-  // the action menu, since that's not initiated by the extension.
-  std::unique_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer;
-  if (browser->profile()->IsOffTheRecord() &&
-      !IncognitoInfo::IsSplitMode(extension)) {
-    displayer = std::make_unique<chrome::ScopedTabbedBrowserDisplayer>(
-        browser->profile()->GetOriginalProfile());
-    browser = displayer->browser();
-  }
-
-  GURL url_to_navigate;
-  bool open_in_tab = OptionsPageInfo::ShouldOpenInTab(extension);
-  if (open_in_tab) {
-    // Options page tab is simply e.g. chrome-extension://.../options.html.
-    url_to_navigate = OptionsPageInfo::GetOptionsPage(extension);
-  } else {
-    // Options page tab is Extension settings pointed at that Extension's ID,
-    // e.g. chrome://extensions?options=...
-    url_to_navigate = GURL(chrome::kChromeUIExtensionsURL);
-    GURL::Replacements replacements;
-    std::string query =
-        base::StringPrintf("options=%s", extension->id().c_str());
-    replacements.SetQueryStr(query);
-    url_to_navigate = url_to_navigate.ReplaceComponents(replacements);
-  }
-
-  // We need to respect path differences because we don't want opening the
-  // options page to close a page that might be open to extension content.
-  // However, if the options page opens inside the chrome://extensions page, we
-  // can override an existing page.
-  // Note: ref behavior is to ignore.
-  ShowSingletonTabOverwritingNTP(browser, url_to_navigate,
-                                 open_in_tab
-                                     ? NavigateParams::RESPECT
-                                     : NavigateParams::IGNORE_AND_NAVIGATE);
-  return true;
+  return ExtensionWindowFromBrowser(browser)->OpenOptionsPage(extension);
 }
 
 // static
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h
index 4cceaea..c4fcf8d4 100644
--- a/chrome/browser/extensions/extension_tab_util.h
+++ b/chrome/browser/extensions/extension_tab_util.h
@@ -159,11 +159,19 @@
   static bool GetTabStripModel(const content::WebContents* web_contents,
                                TabStripModel** tab_strip_model,
                                int* tab_index);
-  static bool GetDefaultTab(Browser* browser,
-                            content::WebContents** contents,
-                            int* tab_id);
+
+  // On success, returns true and fills in the WebContents and extensions API
+  // tab ID for the active tab. The optional_tab_id may be null if the caller
+  // doesn't need it. Returns false if there is no active tab.
+  static bool GetActiveTab(Browser* browser,
+                           content::WebContents** contents,
+                           int* optional_tab_id);
+
   // Any out parameter (|browser|, |tab_strip|, |contents|, & |tab_index|) may
   // be NULL and will not be set within the function.
+  //
+  // The output `*browser` value may be null if the tab is a prerender tab that
+  // has no corresponding browser window.
   static bool GetTabById(int tab_id,
                          content::BrowserContext* browser_context,
                          bool include_incognito,
diff --git a/chrome/browser/feedback/system_logs/log_sources/performance_log_source_unittest.cc b/chrome/browser/feedback/system_logs/log_sources/performance_log_source_unittest.cc
index 3d1db05..f493afb 100644
--- a/chrome/browser/feedback/system_logs/log_sources/performance_log_source_unittest.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/performance_log_source_unittest.cc
@@ -64,7 +64,7 @@
   void SetUp() override { environment_.SetUp(local_state_); }
 
   void TearDown() override {
-    base::PowerMonitor::ShutdownForTesting();
+    base::PowerMonitor::GetInstance()->ShutdownForTesting();
     environment_.TearDown();
   }
 
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
index f217aab..1150529 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -64,7 +64,10 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/origin.h"
 
-#if !BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/build_info.h"
+#include "base/strings/string_util.h"
+#else
 #include "chrome/browser/permissions/one_time_permissions_tracker_factory.h"
 #include "chrome/browser/permissions/one_time_permissions_tracker_observer.h"
 #include "chrome/browser/ui/browser.h"
@@ -321,7 +324,7 @@
      FILE_PATH_LITERAL("Library/Mobile Documents/com~apple~CloudDocs"),
      kDontBlockChildren},
 #endif
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
     // On Linux also block access to devices via /dev.
     {kNoBasePathKey, FILE_PATH_LITERAL("/dev"), kBlockAllChildren},
     // And security sensitive data in /proc and /sys.
@@ -337,6 +340,10 @@
     // website can do with access to that directory and its contents.
     {base::DIR_HOME, FILE_PATH_LITERAL(".dbus"), kBlockAllChildren},
 #endif
+#if BUILDFLAG(IS_ANDROID)
+    {base::DIR_ANDROID_APP_DATA, nullptr, kBlockAllChildren},
+    {base::DIR_CACHE, nullptr, kBlockAllChildren},
+#endif
     // TODO(crbug.com/40095723): Refine this list, for example add
     // XDG_CONFIG_HOME when it is not set ~/.config?
 };
@@ -352,6 +359,16 @@
                              HandleType handle_type,
                              std::vector<BlockPathRule> rules) {
   DCHECK(!path.empty());
+#if BUILDFLAG(IS_ANDROID)
+  // The only check for content-URIs is that they are not from an internal
+  // FileProvider.
+  if (path.IsContentUri()) {
+    base::android::BuildInfo* info = base::android::BuildInfo::GetInstance();
+    return base::StartsWith(
+        path.value(), base::StrCat({"content://", info->package_name(), "."}),
+        base::CompareCase::INSENSITIVE_ASCII);
+  }
+#endif
   DCHECK(path.IsAbsolute());
 
   base::FilePath check_path;
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index b15eea0..09e20cd 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -314,14 +314,24 @@
 
   // Shorthand for `ConfirmSensitiveEntryAccessSync()` with some common
   // arguments.
-  bool IsOpenAllowed(const base::FilePath& path, HandleType handle_type) {
+  SensitiveDirectoryResult GetOpenResult(const base::FilePath& path,
+                                         HandleType handle_type) {
     base::test::TestFuture<
         ChromeFileSystemAccessPermissionContext::SensitiveEntryResult>
         future;
     permission_context_->ConfirmSensitiveEntryAccess(
         kTestOrigin, PathType::kLocal, path, handle_type, UserAction::kOpen,
         content::GlobalRenderFrameHostId(), future.GetCallback());
-    return future.Get() == SensitiveDirectoryResult::kAllowed;
+    return future.Get();
+  }
+
+  bool IsOpenAllowed(const base::FilePath& path, HandleType handle_type) {
+    return GetOpenResult(path, handle_type) ==
+           SensitiveDirectoryResult::kAllowed;
+  }
+
+  bool IsOpenAbort(const base::FilePath& path, HandleType handle_type) {
+    return GetOpenResult(path, handle_type) == SensitiveDirectoryResult::kAbort;
   }
 
   void SetDefaultContentSettingValue(ContentSettingsType type,
@@ -453,8 +463,6 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-#if !BUILDFLAG(IS_ANDROID)
-
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_NoSpecialPath) {
   const base::FilePath kTestPath =
@@ -523,26 +531,38 @@
   base::ScopedPathOverride app_override(base::DIR_EXE, app_dir, true, true);
 
   // The App directory itself should not be allowed.
-  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
-                permission_context(), PathType::kLocal, app_dir,
-                HandleType::kDirectory, UserAction::kOpen),
-            SensitiveDirectoryResult::kAbort);
+  EXPECT_TRUE(IsOpenAbort(app_dir, HandleType::kDirectory));
   // The parent of App directory should also not be allowed.
-  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
-                permission_context(), PathType::kLocal, temp_dir_.GetPath(),
-                HandleType::kDirectory, UserAction::kOpen),
-            SensitiveDirectoryResult::kAbort);
+  EXPECT_TRUE(IsOpenAbort(temp_dir_.GetPath(), HandleType::kDirectory));
   // Paths inside of the App directory should also not be allowed.
-  EXPECT_EQ(
-      ConfirmSensitiveEntryAccessSync(permission_context(), PathType::kLocal,
-                                      app_dir.AppendASCII("foo"),
-                                      HandleType::kFile, UserAction::kOpen),
-      SensitiveDirectoryResult::kAbort);
-  EXPECT_EQ(
-      ConfirmSensitiveEntryAccessSync(
-          permission_context(), PathType::kLocal, app_dir.AppendASCII("foo"),
-          HandleType::kDirectory, UserAction::kOpen),
-      SensitiveDirectoryResult::kAbort);
+  EXPECT_TRUE(IsOpenAbort(app_dir.AppendASCII("foo"), HandleType::kFile));
+  EXPECT_TRUE(IsOpenAbort(app_dir.AppendASCII("foo"), HandleType::kDirectory));
+
+#if BUILDFLAG(IS_ANDROID)
+  base::FilePath app_data_dir = temp_dir_.GetPath().AppendASCII("app_data");
+  base::ScopedPathOverride app_data_override(base::DIR_ANDROID_APP_DATA,
+                                             app_data_dir, true, true);
+
+  // The android app data directory, its parent and paths inside should not be
+  // allowed.
+  EXPECT_TRUE(IsOpenAbort(app_data_dir, HandleType::kDirectory));
+  EXPECT_TRUE(IsOpenAbort(temp_dir_.GetPath(), HandleType::kDirectory));
+  EXPECT_TRUE(IsOpenAbort(app_data_dir.AppendASCII("foo"), HandleType::kFile));
+  EXPECT_TRUE(
+      IsOpenAbort(app_data_dir.AppendASCII("foo"), HandleType::kDirectory));
+
+  base::FilePath cache_dir = temp_dir_.GetPath().AppendASCII("cache");
+  base::ScopedPathOverride cache_override(base::DIR_CACHE, cache_dir, true,
+                                          true);
+  // The android cache directory, its parent and paths inside should not be
+  // allowed.
+  EXPECT_TRUE(IsOpenAbort(cache_dir, HandleType::kDirectory));
+  EXPECT_TRUE(IsOpenAbort(temp_dir_.GetPath(), HandleType::kDirectory));
+  EXPECT_TRUE(IsOpenAbort(cache_dir.AppendASCII("foo"), HandleType::kFile));
+  EXPECT_TRUE(
+      IsOpenAbort(cache_dir.AppendASCII("foo"), HandleType::kDirectory));
+
+#endif  // BUILDFLAG(IS_ANDROID)
 }
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
@@ -629,7 +649,7 @@
        ConfirmSensitiveEntryAccess_ExplicitPathBlock) {
 // Linux is the only OS where we have some blocked directories with explicit
 // paths (as opposed to PathService provided paths).
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
   // /dev should be blocked.
   EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
                 permission_context(), PathType::kLocal,
@@ -788,6 +808,28 @@
 }
 #endif
 
+#if BUILDFLAG(IS_ANDROID)
+TEST_F(ChromeFileSystemAccessPermissionContextTest,
+       ConfirmSensitiveEntryAccess_ContentUri) {
+  // This test runs under org.chromium.native_test.
+  // Content-URI with an authority which matches the package name should fail.
+  EXPECT_TRUE(IsOpenAbort(
+      base::FilePath(
+          "content://org.chromium.native_test.fileprovider/cache/dir"),
+      HandleType::kDirectory));
+  EXPECT_TRUE(IsOpenAbort(
+      base::FilePath(
+          "content://org.chromium.native_test.fileprovider/cache/file"),
+      HandleType::kFile));
+
+  EXPECT_TRUE(IsOpenAllowed(base::FilePath("content://authority/dir"),
+                            HandleType::kDirectory));
+  EXPECT_TRUE(IsOpenAllowed(base::FilePath("content://authority/file"),
+                            HandleType::kFile));
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_ResolveSymbolicLink) {
   if (!base::FeatureList::IsEnabled(
@@ -830,6 +872,7 @@
                 HandleType::kFile, UserAction::kOpen),
             SensitiveDirectoryResult::kAllowed);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_DangerousFile) {
@@ -868,6 +911,8 @@
       SensitiveDirectoryResult::kAbort);
 }
 
+// TODO(crbug.com/40101963): Enable android tests.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        CanObtainWritePermission_ContentSettingAsk) {
   SetDefaultContentSettingValue(ContentSettingsType::FILE_SYSTEM_WRITE_GUARD,
@@ -1129,6 +1174,7 @@
                 .path,
             new_path);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetWellKnownDirectoryPath_Base_OK) {
@@ -1148,6 +1194,8 @@
             temp_dir_.GetPath());
 }
 
+// TODO(crbug.com/40101963): Enable android tests.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetWellKnownDirectoryPath_Pdf_Downloads) {
   DownloadPrefs::FromBrowserContext(browser_context())
@@ -1290,6 +1338,7 @@
             PermissionRequestOutcome::kGrantedByPersistentPermission);
   EXPECT_EQ(grant->GetStatus(), PermissionStatus::GRANTED);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        IsValidObject_GrantsWithDeprecatedTimestampKeyAreNotValidObjects) {
@@ -1306,6 +1355,8 @@
   EXPECT_FALSE(permission_context()->IsValidObject(grant));
 }
 
+// TODO(crbug.com/40101963): Enable android tests.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(
     ChromeFileSystemAccessPermissionContextTest,
     GetGrantedObjectsAndConvertObjectsToGrants_GrantsAreRetainedViaPersistedPermissions) {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 267b0a5..282f0e0 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1311,7 +1311,7 @@
   },
   {
     "name": "cast-mirroring-target-playout-delay",
-    "owners": [ "gbj@google.com", "jophba@chromium.org", "cros-edu-eng@google.com" ],
+    "owners": [ "bzielinski@google.com", "jophba@chromium.org", "cros-edu-eng@google.com" ],
     "expiry_milestone": 150
   },
   {
@@ -4609,11 +4609,6 @@
     "expiry_milestone": 140
   },
   {
-    "name": "feed-close-refresh",
-    "owners": ["//chrome/android/feed/OWNERS", "iwells@chromium.org"],
-    "expiry_milestone": 112
-  },
-  {
     "name": "feed-containment",
     "owners": ["//chrome/android/feed/OWNERS", "jianli@chromium.org"],
     "expiry_milestone": 140
@@ -5753,7 +5748,7 @@
   {
     "name": "link-preview",
     "owners": [ "//chrome/browser/preloading/preview/OWNERS" ],
-    "expiry_milestone": 130
+    "expiry_milestone": 140
   },
   {
     "name": "linked-services-setting",
@@ -6352,7 +6347,7 @@
   {
     "name": "omit-cors-client-cert",
     "owners": [ "toyoshim@chromium.org", "loading-dev@chromium.org" ],
-    "expiry_milestone": 130
+    "expiry_milestone": 140
   },
   {
     "name": "omnibox-2023-refresh-connection-security-indicators",
@@ -8047,7 +8042,7 @@
   {
     "name": "shimless-rma-os-update",
     "owners": [ "zentaro@chromium.org", "gavinwill@chromium.org", "cros-peripherals@google.com" ],
-    "expiry_milestone": 130
+    "expiry_milestone": 140
   },
   {
   "name": "shopping-icon-color-variant",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e5bd024..e8b871a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -5057,18 +5057,17 @@
     "permission is rejected and Chrome's Cast feature is disabled.";
 
 const char kCastMirroringTargetPlayoutDelayName[] =
-    "Changes the target playout delay for cast mirroring.";
+    "Changes the target playout delay for Cast mirroring.";
 const char kCastMirroringTargetPlayoutDelayDescription[] =
-    "Choose a target playout delay for cast mirroring. A lower delay will "
-    "decrease latency, but may come at the cost of other quality standards "
-    "such as dropped frames or FPS.";
-const char kCastMirroringTargetPlayoutDelayDefault[] = "400ms (default)";
+    "Choose a target playout delay for Cast mirroring. A lower delay will "
+    "decrease latency, but may impact other quality indicators.";
+const char kCastMirroringTargetPlayoutDelayDefault[] = "Default (200ms)";
 const char kCastMirroringTargetPlayoutDelay100ms[] = "100ms.";
 const char kCastMirroringTargetPlayoutDelay150ms[] = "150ms.";
-const char kCastMirroringTargetPlayoutDelay200ms[] = "200ms.";
 const char kCastMirroringTargetPlayoutDelay250ms[] = "250ms.";
 const char kCastMirroringTargetPlayoutDelay300ms[] = "300ms.";
-const char kCastMirroringTargetPlayoutDelay350ms[] = "3500ms.";
+const char kCastMirroringTargetPlayoutDelay350ms[] = "350ms.";
+const char kCastMirroringTargetPlayoutDelay400ms[] = "400ms.";
 
 const char kEnableLiveCaptionMultilangName[] = "Multilingual Live Caption";
 const char kEnableLiveCaptionMultilangDescription[] =
@@ -6874,7 +6873,10 @@
 const char kFilesKernelDriversName[] = "Prefer in-kernel filesystem drivers";
 const char kFilesKernelDriversDescription[] =
     "Prefer the in-kernel drivers to the FUSE drivers for filesystems such as "
-    "exFAT or NTFS";
+    "exFAT or NTFS. "
+    "Enabling this flag allows the kernel drivers to be used on systems that "
+    "support them. "
+    "Disabling this flag ensures that the FUSE drivers are used.";
 
 const char kFilesExtractArchiveName[] = "Extract archive in Files app";
 const char kFilesExtractArchiveDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index d016f20..231e64e7 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2929,10 +2929,10 @@
 extern const char kCastMirroringTargetPlayoutDelayDefault[];
 extern const char kCastMirroringTargetPlayoutDelay100ms[];
 extern const char kCastMirroringTargetPlayoutDelay150ms[];
-extern const char kCastMirroringTargetPlayoutDelay200ms[];
 extern const char kCastMirroringTargetPlayoutDelay250ms[];
 extern const char kCastMirroringTargetPlayoutDelay300ms[];
 extern const char kCastMirroringTargetPlayoutDelay350ms[];
+extern const char kCastMirroringTargetPlayoutDelay400ms[];
 
 extern const char kEnablePolicyTestPageName[];
 extern const char kEnablePolicyTestPageDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 7c0da93..35680cc7 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -126,6 +126,7 @@
     &base::features::kCollectAndroidFrameTimelineMetrics,
     &download::features::kDownloadNotificationServiceUnifiedAPI,
     &features::kAndroidBcivWithSuppression,
+    &features::kAndroidBcivZeroBrowserFrames,
     &features::kAndroidBrowserControlsInViz,
     &features::kGenericSensorExtraClasses,
     &features::kBackForwardCache,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 33ea072..4be2a27 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -230,6 +230,7 @@
     public static final String BACK_GESTURE_REFACTOR = "BackGestureRefactorAndroid";
     public static final String BACK_TO_HOME_ANIMATION = "BackToHomeAnimation";
     public static final String BCIV_WITH_SUPPRESSION = "AndroidBcivWithSuppression";
+    public static final String BCIV_ZERO_BROWSER_FRAMES = "AndroidBcivZeroBrowserFrames";
     public static final String BLOCK_INTENTS_WHILE_LOCKED = "BlockIntentsWhileLocked";
     public static final String BOARDING_PASS_DETECTOR = "BoardingPassDetector";
     public static final String BOTTOM_BROWSER_CONTROLS_REFACTOR = "BottomBrowserControlsRefactor";
@@ -840,6 +841,8 @@
             newMutableFlagWithSafeDefault(BOTTOM_BROWSER_CONTROLS_REFACTOR, true);
     public static final MutableFlagWithSafeDefault sBcivWithSuppression =
             newMutableFlagWithSafeDefault(BCIV_WITH_SUPPRESSION, false);
+    public static final MutableFlagWithSafeDefault sBcivZeroBrowserFrames =
+            newMutableFlagWithSafeDefault(BCIV_ZERO_BROWSER_FRAMES, false);
     public static final MutableFlagWithSafeDefault sBrowserControlsInViz =
             newMutableFlagWithSafeDefault(BROWSER_CONTROLS_IN_VIZ, true);
     public static final MutableFlagWithSafeDefault sBrowserControlsEarlyResize =
diff --git a/chrome/browser/media/media_foundation_service_monitor.cc b/chrome/browser/media/media_foundation_service_monitor.cc
index a09ad82..9e0f202 100644
--- a/chrome/browser/media/media_foundation_service_monitor.cc
+++ b/chrome/browser/media/media_foundation_service_monitor.cc
@@ -300,7 +300,7 @@
     AddGlobalSample(kSignificantPlayback, base::Time::Now());
 
   content::ServiceProcessHost::AddObserver(this);
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
   display::Screen::GetScreen()->AddObserver(this);
 }
 
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc
index acbddc0..b80ddc8 100644
--- a/chrome/browser/media/router/media_router_feature.cc
+++ b/chrome/browser/media/router/media_router_feature.cc
@@ -73,12 +73,6 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-BASE_FEATURE(kCastMirroringPlayoutDelay,
-             "CastMirroringPlayoutDelay",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-const base::FeatureParam<int> kCastMirroringPlayoutDelayMs{
-    &kCastMirroringPlayoutDelay, "cast_mirroring_playout_delay_ms", 200};
-
 // TODO(b/202294946): Remove when enabled by default after a few milestones.
 BASE_FEATURE(kGlobalMediaControlsCastStartStop,
              "GlobalMediaControlsCastStartStop",
@@ -213,8 +207,8 @@
 std::optional<base::TimeDelta> GetCastMirroringPlayoutDelay() {
   std::optional<base::TimeDelta> target_playout_delay;
 
-  // First see if there is a command line switch for mirroring playout delay.
-  // Otherwise, check the relevant feature.
+  // The default playout delay can be overridden with the command line flag
+  // `cast-mirroring-target-playout-delay`.
   const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
   if (cl->HasSwitch(switches::kCastMirroringTargetPlayoutDelay)) {
     int switch_playout_delay = 0;
@@ -226,13 +220,6 @@
     }
   }
 
-  if (!target_playout_delay.has_value() &&
-      base::FeatureList::IsEnabled(kCastMirroringPlayoutDelay) &&
-      IsValidMirroringPlayoutDelayMs(kCastMirroringPlayoutDelayMs.Get())) {
-    target_playout_delay =
-        base::Milliseconds(kCastMirroringPlayoutDelayMs.Get());
-  }
-
   return target_playout_delay;
 }
 
diff --git a/chrome/browser/media/router/media_router_feature.h b/chrome/browser/media/router/media_router_feature.h
index 546287a..9fbe4ca 100644
--- a/chrome/browser/media/router/media_router_feature.h
+++ b/chrome/browser/media/router/media_router_feature.h
@@ -62,10 +62,6 @@
 // fall back to audio tab mirroring when casting from the Global Media Controls.
 BASE_DECLARE_FEATURE(kFallbackToAudioTabMirroring);
 
-// If enabled, mirroring sessions use the playout delay specified by
-// `kCastMirroringPlayoutDelayMs`.
-BASE_DECLARE_FEATURE(kCastMirroringPlayoutDelay);
-
 // When enabled, Cast virtual connections are removed without explicitly sending
 // a close connection request to the receiver when the sender webpage navigates
 // away.
diff --git a/chrome/browser/media/router/media_router_feature_unittest.cc b/chrome/browser/media/router/media_router_feature_unittest.cc
index 3f9627f..11371bd 100644
--- a/chrome/browser/media/router/media_router_feature_unittest.cc
+++ b/chrome/browser/media/router/media_router_feature_unittest.cc
@@ -53,38 +53,13 @@
   EXPECT_EQ(token, GetReceiverIdHashToken(pref_service.get()));
 }
 
-TEST(MediaRouterFeatureTest, GetCastMirroringPlayoutDelay) {
-  base::test::ScopedFeatureList feature_list;
-  base::FieldTrialParams feature_params;
-  feature_params[kCastMirroringPlayoutDelayMs.name] = "100";
-  feature_list.InitAndEnableFeatureWithParameters(kCastMirroringPlayoutDelay,
-                                                  feature_params);
-  EXPECT_TRUE(GetCastMirroringPlayoutDelay().has_value());
-  EXPECT_EQ(GetCastMirroringPlayoutDelay().value(), base::Milliseconds(100));
-
-  // Incorrect values are ignored.
-  feature_list.Reset();
-  feature_params[kCastMirroringPlayoutDelayMs.name] = "0";
-  feature_list.InitAndEnableFeatureWithParameters(kCastMirroringPlayoutDelay,
-                                                  feature_params);
-  EXPECT_FALSE(GetCastMirroringPlayoutDelay().has_value());
-
-  feature_list.Reset();
-  feature_params[kCastMirroringPlayoutDelayMs.name] = "2000";
-  feature_list.InitAndEnableFeatureWithParameters(kCastMirroringPlayoutDelay,
-                                                  feature_params);
-  EXPECT_FALSE(GetCastMirroringPlayoutDelay().has_value());
-}
-
 TEST(MediaRouterFeatureTest, GetCastMirroringPlayoutDelayCommandLine) {
-  base::TimeDelta default_delay = base::Milliseconds(200);
-
   base::test::ScopedFeatureList feature_list;
   // Test that an invalid switch is not returned.
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   command_line->AppendSwitchASCII(switches::kCastMirroringTargetPlayoutDelay,
                                   "foo");
-  EXPECT_EQ(GetCastMirroringPlayoutDelay().value(), default_delay);
+  EXPECT_FALSE(GetCastMirroringPlayoutDelay().has_value());
 
   base::TimeDelta expected_delay = base::Milliseconds(150);
 
@@ -92,15 +67,6 @@
   command_line->AppendSwitchASCII(switches::kCastMirroringTargetPlayoutDelay,
                                   "150");
   EXPECT_EQ(GetCastMirroringPlayoutDelay().value(), expected_delay);
-
-  // Test that command line takes precedence over feature.
-  base::FieldTrialParams feature_params;
-  feature_params[kCastMirroringPlayoutDelayMs.name] = "500";
-  feature_list.InitAndEnableFeatureWithParameters(kCastMirroringPlayoutDelay,
-                                                  feature_params);
-  ASSERT_NE(base::Milliseconds(kCastMirroringPlayoutDelayMs.Get()),
-            expected_delay);
-  EXPECT_EQ(GetCastMirroringPlayoutDelay().value(), expected_delay);
 }
 
 class MediaRouterEnabledTest : public ::testing::Test {
diff --git a/chrome/browser/media/router/providers/cast/mirroring_activity_unittest.cc b/chrome/browser/media/router/providers/cast/mirroring_activity_unittest.cc
index a6de8f7..26f88b0e 100644
--- a/chrome/browser/media/router/providers/cast/mirroring_activity_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/mirroring_activity_unittest.cc
@@ -785,11 +785,9 @@
 }
 
 TEST_F(MirroringActivityTest, TargetPlayoutDelaySetInRequest) {
-  base::test::ScopedFeatureList feature_list;
-  base::FieldTrialParams feature_params;
-  feature_params[media_router::kCastMirroringPlayoutDelayMs.name] = "300";
-  feature_list.InitAndEnableFeatureWithParameters(
-      media_router::kCastMirroringPlayoutDelay, feature_params);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII(switches::kCastMirroringTargetPlayoutDelay,
+                                  "300");
 
   static constexpr char kUrl[] =
       "cast:0F5096E8?streamingCaptureAudio=1&streamingTargetPlayoutDelayMillis="
@@ -806,11 +804,9 @@
 }
 
 TEST_F(MirroringActivityTest, TargetPlayoutDelayFeatureFlagParam) {
-  base::test::ScopedFeatureList feature_list;
-  base::FieldTrialParams feature_params;
-  feature_params[media_router::kCastMirroringPlayoutDelayMs.name] = "300";
-  feature_list.InitAndEnableFeatureWithParameters(
-      media_router::kCastMirroringPlayoutDelay, feature_params);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII(switches::kCastMirroringTargetPlayoutDelay,
+                                  "300");
 
   static constexpr char kUrl[] = "cast:0F5096E8?streamingCaptureAudio=1";
   GURL url(kUrl);
@@ -824,13 +820,11 @@
 }
 
 TEST_F(MirroringActivityTest, CastStreamingSenderUma) {
-  base::HistogramTester uma_recorder;
-  base::test::ScopedFeatureList feature_list;
-  base::FieldTrialParams feature_params;
-  feature_params[media_router::kCastMirroringPlayoutDelayMs.name] = "200";
-  feature_list.InitAndEnableFeatureWithParameters(
-      media_router::kCastMirroringPlayoutDelay, feature_params);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII(switches::kCastMirroringTargetPlayoutDelay,
+                                  "200");
 
+  base::HistogramTester uma_recorder;
   static constexpr char kJsonStats[] = R"({
     "audio": {
       "TRANSMISSION_KBPS": 20.0,
diff --git a/chrome/browser/media/webrtc/BUILD.gn b/chrome/browser/media/webrtc/BUILD.gn
index cc97faf..293042a 100644
--- a/chrome/browser/media/webrtc/BUILD.gn
+++ b/chrome/browser/media/webrtc/BUILD.gn
@@ -122,6 +122,7 @@
     "//chrome/app:generated_resources_grit",
     "//chrome/browser:browser_process",
     "//chrome/browser/media/prefs",
+    "//chrome/browser/permissions/system",
     "//chrome/browser/safe_browsing",
     "//chrome/browser/ui:ui_features",
     "//chrome/common",
@@ -198,7 +199,6 @@
 
   if (is_mac) {
     sources += [
-      "media_authorization_wrapper_mac.h",
       "system_media_capture_permissions_mac.h",
       "system_media_capture_permissions_mac.mm",
       "system_media_capture_permissions_stats_mac.h",
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
index 2df907b..73a2657f 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
@@ -382,7 +382,7 @@
     }
 #if BUILDFLAG(IS_MAC)
     if (system_media_permissions::CheckSystemScreenCapturePermission() !=
-        system_media_permissions::SystemPermission::kAllowed) {
+        system_permission_settings::SystemPermission::kAllowed) {
       std::move(pending_request->callback)
           .Run(blink::mojom::StreamDevicesSet(),
                blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
@@ -437,7 +437,7 @@
 #if BUILDFLAG(IS_MAC)
   if (media_id.type != content::DesktopMediaID::TYPE_WEB_CONTENTS &&
       system_media_permissions::CheckSystemScreenCapturePermission() !=
-          system_media_permissions::SystemPermission::kAllowed) {
+          system_permission_settings::SystemPermission::kAllowed) {
     std::move(pending_request->callback)
         .Run(blink::mojom::StreamDevicesSet(),
              blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
diff --git a/chrome/browser/media/webrtc/display_media_access_handler.cc b/chrome/browser/media/webrtc/display_media_access_handler.cc
index 3c97b4f..b856f882 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler.cc
@@ -605,7 +605,7 @@
        (media_id.type == content::DesktopMediaID::TYPE_WINDOW &&
         !media_id.window_id)) &&
       system_media_permissions::CheckSystemScreenCapturePermission() !=
-          system_media_permissions::SystemPermission::kAllowed) {
+          system_permission_settings::SystemPermission::kAllowed) {
     RejectRequest(
         web_contents.get(),
         blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED);
diff --git a/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h b/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h
deleted file mode 100644
index ea4d848..0000000
--- a/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
-#define CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
-
-#import <AVFoundation/AVFoundation.h>
-#import <Foundation/Foundation.h>
-
-#include "base/functional/callback_forward.h"
-
-namespace system_media_permissions {
-
-class MediaAuthorizationWrapper {
- public:
-  virtual ~MediaAuthorizationWrapper() {}
-
-  virtual AVAuthorizationStatus AuthorizationStatusForMediaType(
-      NSString* media_type) = 0;
-  virtual void RequestAccessForMediaType(NSString* media_type,
-                                         base::OnceClosure callback) = 0;
-};
-
-}  // namespace system_media_permissions
-
-#endif  // CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
diff --git a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
index 6be4072..c863aab 100644
--- a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
@@ -42,9 +42,13 @@
 
 #if BUILDFLAG(IS_MAC)
 #include "base/metrics/histogram_macros.h"
-#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
 #include "chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h"
+#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
+#endif
+
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
+#include "chrome/browser/permissions/system/system_permission_settings.h"
 #endif
 
 using content::BrowserThread;
@@ -55,7 +59,7 @@
                             std::unique_ptr<content::MediaStreamUI> ui)>;
 
 #if BUILDFLAG(IS_MAC)
-using system_media_permissions::SystemPermission;
+using system_permission_settings::SystemPermission;
 #endif
 
 namespace {
@@ -368,69 +372,81 @@
 
   blink::mojom::MediaStreamRequestResult final_result = result;
 
-#if BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
   // If the request was approved, ask for system permissions if needed, and run
   // this function again when done.
   if (result == blink::mojom::MediaStreamRequestResult::OK) {
     const content::MediaStreamRequest& request = request_it->second.request;
     if (request.audio_type ==
         blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
-      const SystemPermission system_audio_permission =
-          system_media_permissions::CheckSystemAudioCapturePermission();
+// System-level permission prompt is supported only on macOS.
+#if BUILDFLAG(IS_MAC)
       UMA_HISTOGRAM_ENUMERATION(
           "Media.Audio.Capture.Mac.MicSystemPermission.UserMedia",
-          system_audio_permission);
-      if (system_audio_permission == SystemPermission::kNotDetermined) {
+          system_permission_settings::CheckSystemAudioCapturePermission());
+      if (system_permission_settings::CanPrompt(
+              ContentSettingsType::MEDIASTREAM_MIC)) {
         // Using WeakPtr since callback can come at any time and we might be
         // destroyed.
-        system_media_permissions::RequestSystemAudioCapturePermission(
+        system_permission_settings::RequestSystemAudioCapturePermission(
             base::BindOnce(&PermissionBubbleMediaAccessHandler::
                                OnAccessRequestResponseForBinding,
                            weak_factory_.GetWeakPtr(),
                            base::UnsafeDangling(web_contents), request_id,
                            stream_devices_set.Clone(), result, std::move(ui)));
         return;
-      } else if (system_audio_permission == SystemPermission::kRestricted ||
-                 system_audio_permission == SystemPermission::kDenied) {
+      }
+#endif  // BUILDFLAG(IS_MAC)
+      if (system_permission_settings::IsDenied(
+              ContentSettingsType::MEDIASTREAM_MIC)) {
         content_settings::UpdateLocationBarUiForWebContents(web_contents);
         final_result =
             blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED;
+#if BUILDFLAG(IS_MAC)
         system_media_permissions::SystemAudioCapturePermissionBlocked();
+#endif  // BUILDFLAG(IS_MAC)
       } else {
-        DCHECK_EQ(system_audio_permission, SystemPermission::kAllowed);
+        DCHECK(system_permission_settings::IsAllowed(
+            ContentSettingsType::MEDIASTREAM_MIC));
         content_settings::UpdateLocationBarUiForWebContents(web_contents);
       }
     }
     if (request.video_type ==
         blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
-      const SystemPermission system_video_permission =
-          system_media_permissions::CheckSystemVideoCapturePermission();
+// System-level permission prompt is supported only on macOS.
+#if BUILDFLAG(IS_MAC)
       UMA_HISTOGRAM_ENUMERATION(
           "Media.Video.Capture.Mac.CameraSystemPermission.UserMedia",
-          system_video_permission);
-      if (system_video_permission == SystemPermission::kNotDetermined) {
+          system_permission_settings::CheckSystemVideoCapturePermission());
+      if (system_permission_settings::CanPrompt(
+              ContentSettingsType::MEDIASTREAM_CAMERA)) {
         // Using WeakPtr since callback can come at any time and we might be
         // destroyed.
-        system_media_permissions::RequestSystemVideoCapturePermission(
+        system_permission_settings::RequestSystemVideoCapturePermission(
             base::BindOnce(&PermissionBubbleMediaAccessHandler::
                                OnAccessRequestResponseForBinding,
                            weak_factory_.GetWeakPtr(),
                            base::UnsafeDangling(web_contents), request_id,
                            stream_devices_set.Clone(), result, std::move(ui)));
         return;
-      } else if (system_video_permission == SystemPermission::kRestricted ||
-                 system_video_permission == SystemPermission::kDenied) {
+      }
+#endif  // BUILDFLAG(IS_MAC)
+      if (system_permission_settings::IsDenied(
+              ContentSettingsType::MEDIASTREAM_CAMERA)) {
         content_settings::UpdateLocationBarUiForWebContents(web_contents);
         final_result =
             blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED;
+#if BUILDFLAG(IS_MAC)
         system_media_permissions::SystemVideoCapturePermissionBlocked();
+#endif  // BUILDFLAG(IS_MAC)
       } else {
-        DCHECK_EQ(system_video_permission, SystemPermission::kAllowed);
+        DCHECK(system_permission_settings::IsAllowed(
+            ContentSettingsType::MEDIASTREAM_CAMERA));
         content_settings::UpdateLocationBarUiForWebContents(web_contents);
       }
     }
   }
-#endif  // BUILDFLAG(IS_MAC)
+#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
 
   MediaResponseCallback callback = std::move(request_it->second.callback);
   requests_map.erase(request_it);
diff --git a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h
index c16a577d..8f6a7f6 100644
--- a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h
+++ b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h
@@ -5,38 +5,13 @@
 #ifndef CHROME_BROWSER_MEDIA_WEBRTC_SYSTEM_MEDIA_CAPTURE_PERMISSIONS_MAC_H_
 #define CHROME_BROWSER_MEDIA_WEBRTC_SYSTEM_MEDIA_CAPTURE_PERMISSIONS_MAC_H_
 
-#include "base/functional/callback_forward.h"
+#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
 
 namespace system_media_permissions {
 
-class MediaAuthorizationWrapper;
-
-// System permission state. These are also used in stats - do not remove or
-// re-arrange the values.
-enum class SystemPermission {
-  kNotDetermined = 0,
-  kRestricted = 1,
-  kDenied = 2,
-  kAllowed = 3,
-  kMaxValue = kAllowed
-};
-
-// Returns the system permission to capture audio or video.
-SystemPermission CheckSystemAudioCapturePermission();
-SystemPermission CheckSystemVideoCapturePermission();
-
 // Returns the system permission to capture the screen.
-SystemPermission CheckSystemScreenCapturePermission();
-
-// Requests the system permission to capture audio or video. This call
-// immediately returns. When requesting permission, the OS will show a user
-// dialog and respond asynchronously. At the response, |callback| is posted as a
-// reply on the requesting thread.
-void RequestSystemAudioCapturePermission(base::OnceClosure callback);
-void RequestSystemVideoCapturePermission(base::OnceClosure callback);
-
-// Sets the wrapper object for OS calls. For test mocking purposes.
-void SetMediaAuthorizationWrapperForTesting(MediaAuthorizationWrapper* wrapper);
+system_permission_settings::SystemPermission
+CheckSystemScreenCapturePermission();
 
 }  // namespace system_media_permissions
 
diff --git a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
index 8e84fbe..6c4472c 100644
--- a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
+++ b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
@@ -4,149 +4,20 @@
 
 #include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
 
-#import <AVFoundation/AVFoundation.h>
-
-#include "base/apple/foundation_util.h"
-#include "base/apple/scoped_cftyperef.h"
-#include "base/check.h"
-#include "base/command_line.h"
-#include "base/feature_list.h"
-#include "base/functional/callback.h"
-#include "base/functional/callback_helpers.h"
-#include "base/no_destructor.h"
-#import "base/task/sequenced_task_runner.h"
-#include "base/task/sequenced_task_runner.h"
-#include "chrome/browser/media/webrtc/media_authorization_wrapper_mac.h"
 #include "chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h"
-#include "chrome/common/chrome_features.h"
-#include "media/base/media_switches.h"
-#include "ui/base/cocoa/permissions_utils.h"
 
 namespace system_media_permissions {
 
-namespace {
-
-bool UsingFakeMediaDevices() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kUseFakeDeviceForMediaStream);
-}
-
-// Pointer to OS call wrapper that tests can set.
-MediaAuthorizationWrapper* g_media_authorization_wrapper_for_tests = nullptr;
-
-// Implementation of OS call wrapper that does the actual OS calls.
-class MediaAuthorizationWrapperImpl final : public MediaAuthorizationWrapper {
- public:
-  MediaAuthorizationWrapperImpl() = default;
-
-  MediaAuthorizationWrapperImpl(const MediaAuthorizationWrapperImpl&) = delete;
-  MediaAuthorizationWrapperImpl& operator=(
-      const MediaAuthorizationWrapperImpl&) = delete;
-
-  ~MediaAuthorizationWrapperImpl() override = default;
-
-  AVAuthorizationStatus AuthorizationStatusForMediaType(
-      AVMediaType media_type) override {
-    return [AVCaptureDevice authorizationStatusForMediaType:media_type];
-  }
-
-  void RequestAccessForMediaType(AVMediaType media_type,
-                                 base::OnceClosure callback) override {
-    __block base::OnceClosure block_callback = std::move(callback);
-    __block scoped_refptr<base::SequencedTaskRunner> requesting_thread =
-        base::SequencedTaskRunner::GetCurrentDefault();
-    [AVCaptureDevice requestAccessForMediaType:media_type
-                             completionHandler:^(BOOL granted) {
-                               requesting_thread->PostTask(
-                                   FROM_HERE, std::move(block_callback));
-                             }];
-  }
-};
-
-MediaAuthorizationWrapper& GetMediaAuthorizationWrapper() {
-  if (g_media_authorization_wrapper_for_tests)
-    return *g_media_authorization_wrapper_for_tests;
-
-  static base::NoDestructor<MediaAuthorizationWrapperImpl>
-      media_authorization_wrapper;
-  return *media_authorization_wrapper;
-}
-
-AVAuthorizationStatus MediaAuthorizationStatus(AVMediaType media_type) {
-  return GetMediaAuthorizationWrapper().AuthorizationStatusForMediaType(
-      media_type);
-}
-
-SystemPermission CheckSystemMediaCapturePermission(AVMediaType media_type) {
-  if (UsingFakeMediaDevices()) {
-    return SystemPermission::kAllowed;
-  }
-
-  AVAuthorizationStatus auth_status = MediaAuthorizationStatus(media_type);
-  switch (auth_status) {
-    case AVAuthorizationStatusNotDetermined:
-      return SystemPermission::kNotDetermined;
-    case AVAuthorizationStatusRestricted:
-      return SystemPermission::kRestricted;
-    case AVAuthorizationStatusDenied:
-      return SystemPermission::kDenied;
-    case AVAuthorizationStatusAuthorized:
-      return SystemPermission::kAllowed;
-    default:
-      NOTREACHED();
-  }
-}
-
-void RequestSystemMediaCapturePermission(AVMediaType media_type,
-                                         base::OnceClosure callback) {
-  if (UsingFakeMediaDevices()) {
-    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, std::move(callback));
-    return;
-  }
-
-  GetMediaAuthorizationWrapper().RequestAccessForMediaType(media_type,
-                                                           std::move(callback));
-}
-
-bool IsScreenCaptureAllowed() {
-  if (!base::FeatureList::IsEnabled(
-          features::kMacSystemScreenCapturePermissionCheck)) {
-    return true;
-  }
-
-  bool allowed = ui::IsScreenCaptureAllowed();
-  LogSystemScreenCapturePermission(allowed);
-  return allowed;
-}
-
-}  // namespace
-
-SystemPermission CheckSystemAudioCapturePermission() {
-  return CheckSystemMediaCapturePermission(AVMediaTypeAudio);
-}
-
-SystemPermission CheckSystemVideoCapturePermission() {
-  return CheckSystemMediaCapturePermission(AVMediaTypeVideo);
-}
+using ::system_permission_settings::SystemPermission;
 
 SystemPermission CheckSystemScreenCapturePermission() {
-  return IsScreenCaptureAllowed() ? SystemPermission::kAllowed
-                                  : SystemPermission::kDenied;
-}
+  SystemPermission system_permission =
+      system_permission_settings::CheckSystemScreenCapturePermission();
 
-void RequestSystemAudioCapturePermission(base::OnceClosure callback) {
-  RequestSystemMediaCapturePermission(AVMediaTypeAudio, std::move(callback));
-}
+  LogSystemScreenCapturePermission(system_permission ==
+                                   SystemPermission::kAllowed);
 
-void RequestSystemVideoCapturePermission(base::OnceClosure callback) {
-  RequestSystemMediaCapturePermission(AVMediaTypeVideo, std::move(callback));
-}
-
-void SetMediaAuthorizationWrapperForTesting(
-    MediaAuthorizationWrapper* wrapper) {
-  CHECK(!g_media_authorization_wrapper_for_tests);
-  g_media_authorization_wrapper_for_tests = wrapper;
+  return system_permission;
 }
 
 }  // namespace system_media_permissions
diff --git a/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h b/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h
index 6e80f208..33784d0f 100644
--- a/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h
+++ b/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h
@@ -8,7 +8,7 @@
 #ifndef CHROME_BROWSER_MEDIA_WEBRTC_SYSTEM_MEDIA_CAPTURE_PERMISSIONS_STATS_MAC_H_
 #define CHROME_BROWSER_MEDIA_WEBRTC_SYSTEM_MEDIA_CAPTURE_PERMISSIONS_STATS_MAC_H_
 
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
+#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
 
 class PrefRegistrySimple;
 
@@ -23,8 +23,10 @@
 
 // Called when a system permission goes from "not determined" to another state.
 // The new permission is logged as startup state.
-void SystemAudioCapturePermissionDetermined(SystemPermission permission);
-void SystemVideoCapturePermissionDetermined(SystemPermission permission);
+void SystemAudioCapturePermissionDetermined(
+    system_permission_settings::SystemPermission permission);
+void SystemVideoCapturePermissionDetermined(
+    system_permission_settings::SystemPermission permission);
 
 // Adds a sample of the passed in permission to the screen capture metric.
 // Called when the screen capture permission is checked.
diff --git a/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.mm b/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.mm
index a1ec954..5abb4bcd7 100644
--- a/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.mm
+++ b/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.mm
@@ -13,6 +13,7 @@
 namespace system_media_permissions {
 
 namespace {
+using ::system_permission_settings::SystemPermission;
 
 const char kSystemPermissionMicFirstBlockedTimePref[] =
     "system_permission.mic.first_blocked_time";
@@ -137,11 +138,13 @@
 }
 
 void LogSystemMediaPermissionsStartupStats() {
-  const SystemPermission audio_permission = CheckSystemAudioCapturePermission();
+  const system_permission_settings::SystemPermission audio_permission =
+      system_permission_settings::CheckSystemAudioCapturePermission();
   LogStartupMicSystemPermission(audio_permission);
   MaybeLogAdditionalMicSystemPermissionStats(audio_permission);
 
-  const SystemPermission video_permission = CheckSystemVideoCapturePermission();
+  const system_permission_settings::SystemPermission video_permission =
+      system_permission_settings::CheckSystemVideoCapturePermission();
   LogStartupCameraSystemPermission(video_permission);
   MaybeLogAdditionalCameraSystemPermissionStats(video_permission);
 
diff --git a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
index a37ddf7a..88a0133 100644
--- a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
@@ -180,7 +180,7 @@
 #if BUILDFLAG(IS_MAC)
   if (!is_fake_ui && !is_tab_capture &&
       system_media_permissions::CheckSystemScreenCapturePermission() !=
-          system_media_permissions::SystemPermission::kAllowed) {
+          system_permission_settings::SystemPermission::kAllowed) {
     expect_success = false;
   }
 #endif
diff --git a/chrome/browser/metrics/perf/profile_provider_chromeos.cc b/chrome/browser/metrics/perf/profile_provider_chromeos.cc
index 0386a5d5..21020e5 100644
--- a/chrome/browser/metrics/perf/profile_provider_chromeos.cc
+++ b/chrome/browser/metrics/perf/profile_provider_chromeos.cc
@@ -57,7 +57,7 @@
 ProfileProvider::~ProfileProvider() {
   ash::LoginState::Get()->RemoveObserver(this);
   chromeos::PowerManagerClient::Get()->RemoveObserver(this);
-  base::PowerMonitor::RemovePowerThermalObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerThermalObserver(this);
   if (jank_monitor_) {
     jank_monitor_->RemoveObserver(this);
     jank_monitor_->Destroy();
@@ -82,7 +82,8 @@
 
   // Register as an observer of thermal state changes.
   base::PowerThermalObserver::DeviceThermalState thermal_state =
-      base::PowerMonitor::AddPowerStateObserverAndReturnPowerThermalState(this);
+      base::PowerMonitor::GetInstance()
+          ->AddPowerStateObserverAndReturnPowerThermalState(this);
   OnThermalStateChange(thermal_state);
 
   // Check the login state. At the time of writing, this class is instantiated
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
index e3a12f4..618210c4f 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
@@ -63,12 +63,13 @@
 
 void UmaHistogramCounts10000WithBatteryStateVariant(const char* histogram_name,
                                                     size_t value) {
-  DCHECK(base::PowerMonitor::IsInitialized());
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  DCHECK(power_monitor->IsInitialized());
 
   base::UmaHistogramCounts10000(histogram_name, value);
 
   const char* suffix =
-      base::PowerMonitor::IsOnBatteryPower() ? ".OnBattery" : ".PluggedIn";
+      power_monitor->IsOnBatteryPower() ? ".OnBattery" : ".PluggedIn";
 
   base::UmaHistogramCounts10000(base::StrCat({histogram_name, suffix}), value);
 }
@@ -147,7 +148,7 @@
   }
 
   browser_list->AddObserver(this);
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 
   // Setup daily reporting of the stats aggregated in |tab_stats_data_store|.
   daily_event_->AddObserver(std::make_unique<TabStatsDailyObserver>(
@@ -169,7 +170,7 @@
 TabStatsTracker::~TabStatsTracker() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   BrowserList::GetInstance()->RemoveObserver(this);
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
   g_browser_process->GetTabManager()->RemoveObserver(this);
 }
 
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc b/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc
index b1e0151..9a217c356 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc
@@ -35,8 +35,9 @@
 using TabsStats = TabStatsDataStore::TabsStats;
 
 std::string GetHistogramNameWithBatteryStateSuffix(const char* histogram_name) {
-  const char* suffix =
-      base::PowerMonitor::IsOnBatteryPower() ? ".OnBattery" : ".PluggedIn";
+  const char* suffix = base::PowerMonitor::GetInstance()->IsOnBatteryPower()
+                           ? ".OnBattery"
+                           : ".PluggedIn";
 
   return base::StrCat({histogram_name, suffix});
 }
diff --git a/chrome/browser/metrics/usage_scenario/system_event_provider.cc b/chrome/browser/metrics/usage_scenario/system_event_provider.cc
index 6832a38..3249dcb 100644
--- a/chrome/browser/metrics/usage_scenario/system_event_provider.cc
+++ b/chrome/browser/metrics/usage_scenario/system_event_provider.cc
@@ -9,13 +9,17 @@
 
 SystemEventProvider::SystemEventProvider(UsageScenarioDataStoreImpl* data_store)
     : data_store_(data_store) {
-  if (base::PowerMonitor::IsInitialized())
-    base::PowerMonitor::AddPowerSuspendObserver(this);
+  if (auto* power_monitor = base::PowerMonitor::GetInstance();
+      power_monitor->IsInitialized()) {
+    power_monitor->AddPowerSuspendObserver(this);
+  }
 }
 
 SystemEventProvider::~SystemEventProvider() {
-  if (base::PowerMonitor::IsInitialized())
-    base::PowerMonitor::RemovePowerSuspendObserver(this);
+  if (auto* power_monitor = base::PowerMonitor::GetInstance();
+      power_monitor->IsInitialized()) {
+    power_monitor->RemovePowerSuspendObserver(this);
+  }
 }
 
 void SystemEventProvider::OnSuspend() {
diff --git a/chrome/browser/page_load_metrics/observers/abandoned_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/abandoned_page_load_metrics_observer_browsertest.cc
index bf63fd36..f6f3cf27a 100644
--- a/chrome/browser/page_load_metrics/observers/abandoned_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/abandoned_page_load_metrics_observer_browsertest.cc
@@ -250,7 +250,13 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(web_contents()));
   waiter3->Wait();
+
   ExpectTotalCountForAllNavigationMilestones(/*include_redirect=*/false, 3);
+  // Since we made a backward navigation, we will have metrics with
+  // `ResponseFromCache`.
+  ExpectTotalCountForAllNavigationMilestones(
+      /*include_redirect=*/false, 1,
+      std::string(internal::kSuffixResponseFromCache));
 
   // 4) Navigate forward to B, potentially restoring from BFCache.
   auto waiter4 = CreatePageLoadMetricsTestWaiterForLoading();
@@ -273,6 +279,14 @@
           internal::kRendererProcessCreatedBeforeNavHistogramName,
       expected_count);
 
+  // Since we made a backward navigation, we will have metrics with
+  // `ResponseFromCache` (unless the page is restored from BFCache).
+  expected_count =
+      (content::BackForwardCache::IsBackForwardCacheFeatureEnabled() ? 1 : 2);
+  ExpectTotalCountForAllNavigationMilestones(
+      /*include_redirect=*/false, expected_count,
+      std::string(internal::kSuffixResponseFromCache));
+
   // No abandonment happened, so no abandonment metrics was logged.
   ExpectEmptyNavigationAbandonment();
   EXPECT_TRUE(ukm_recorder.GetEntriesByName("AbandonedSRPNavigation").empty());
diff --git a/chrome/browser/page_load_metrics/observers/gws_abandoned_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/gws_abandoned_page_load_metrics_observer_browsertest.cc
index 3388ea0..f1679451 100644
--- a/chrome/browser/page_load_metrics/observers/gws_abandoned_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/gws_abandoned_page_load_metrics_observer_browsertest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/chrome_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/page_load_metrics/browser/observers/abandoned_page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
 #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
@@ -734,6 +735,11 @@
   EXPECT_TRUE(WaitForLoadStop(web_contents()));
 
   ExpectTotalCountForAllNavigationMilestones(/*include_redirect=*/false, 2);
+  // Since we made a backward navigation, we will have metrics with
+  // `ResponseFromCache`.
+  ExpectTotalCountForAllNavigationMilestones(
+      /*include_redirect=*/false, 1,
+      std::string(internal::kSuffixResponseFromCache));
   ExpectEmptyNavigationAbandonment();
 
   // SRP Navigation #3: Go back to SRP, potentially restoring from BFCache.
@@ -753,6 +759,10 @@
   ExpectTotalCountForAllNavigationMilestones(
       /*include_redirect=*/false,
       content::BackForwardCache::IsBackForwardCacheFeatureEnabled() ? 2 : 3);
+  ExpectTotalCountForAllNavigationMilestones(
+      /*include_redirect=*/false,
+      content::BackForwardCache::IsBackForwardCacheFeatureEnabled() ? 1 : 2,
+      std::string(internal::kSuffixResponseFromCache));
 
   ExpectEmptyNavigationAbandonment();
 }
diff --git a/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.cc
index 40e368e5..5d6297a 100644
--- a/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.cc
@@ -99,6 +99,7 @@
     content::NavigationHandle* navigation_handle,
     const GURL& currently_committed_url,
     bool started_in_foreground) {
+  navigation_id_ = navigation_handle->GetNavigationId();
   auto* navigation_userdata =
       page_load_metrics::NavigationHandleUserData::GetForNavigationHandle(
           *navigation_handle);
@@ -218,6 +219,7 @@
       aft_start_time_ = mark->start_time;
     } else if (mark->mark_name == internal::kGwsAFTEndMarkName) {
       PAGE_LOAD_HISTOGRAM(internal::kHistogramGWSAFTEnd, mark->start_time);
+      aft_end_time_ = mark->start_time;
     } else if (mark->mark_name == internal::kGwsHeaderChunkStartMarkName) {
       PAGE_LOAD_HISTOGRAM(internal::kHistogramGWSHeaderChunkStart,
                           mark->start_time);
@@ -334,6 +336,9 @@
       internal::kHistogramGWSConnectTimingFinalRequestSslDelay,
       timing.final_request_ssl_delay);
 
+  // Record latency trace events.
+  RecordLatencyTraceEvents();
+
   // Record trace events according to the navigation milestone.
   TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
       "loading", "GWSNavigationStartToFirstRequestStart", TRACE_ID_LOCAL(this),
@@ -386,3 +391,48 @@
 
   return histogram_name + suffix;
 }
+
+void GWSPageLoadMetricsObserver::RecordLatencyTraceEvents() {
+  const auto trace_id =
+      TRACE_ID_WITH_SCOPE("GWSLatencyEvent", TRACE_ID_LOCAL(navigation_id_));
+  if (header_chunk_start_time_.has_value()) {
+    // TODO(crbug.com/364278026): SRT starts from the time when the user submits
+    // a query. Using the navigation start time may not perfect to measure SRT.
+    TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+        "navigation", "GWSLatency:SRT", trace_id,
+        GetDelegate().GetNavigationStart());
+    TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+        "navigation", "GWSLatency:SRT", trace_id,
+        GetDelegate().GetNavigationStart() + header_chunk_start_time_.value());
+    if (aft_end_time_.has_value()) {
+      // Currently `aft_start_time_` has the value of the server response time,
+      // but in theory AFT starts at the end of SRT, the time when the client
+      // receives the first byte of the header chunk.
+      TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+          "navigation", "GWSLatency:AFT", trace_id,
+          GetDelegate().GetNavigationStart() +
+              header_chunk_start_time_.value());
+      TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+          "navigation", "GWSLatency:AFT", trace_id,
+          GetDelegate().GetNavigationStart() + aft_end_time_.value());
+    }
+    if (body_chunk_start_time_.has_value()) {
+      TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+          "navigation", "GWSLatency:SCT", trace_id,
+          GetDelegate().GetNavigationStart() +
+              header_chunk_start_time_.value());
+      TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+          "navigation", "GWSLatency:SCT", trace_id,
+          GetDelegate().GetNavigationStart() + body_chunk_start_time_.value());
+    }
+    if (header_chunk_end_time_.has_value()) {
+      TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+          "navigation", "GWSLatency:HCT", trace_id,
+          GetDelegate().GetNavigationStart() +
+              header_chunk_start_time_.value());
+      TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+          "navigation", "GWSLatency:HCT", trace_id,
+          GetDelegate().GetNavigationStart() + header_chunk_end_time_.value());
+    }
+  }
+}
diff --git a/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h
index 9a687e8..d0232dc6b 100644
--- a/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h
@@ -92,6 +92,7 @@
  private:
   void LogMetricsOnComplete();
   void RecordNavigationTimingHistograms();
+  void RecordLatencyTraceEvents();
 
   std::string AddHistogramSuffix(const std::string histogram_name);
 
@@ -101,9 +102,12 @@
   bool is_new_tab_page_ = false;
 
   std::optional<base::TimeDelta> aft_start_time_;
+  std::optional<base::TimeDelta> aft_end_time_;
   std::optional<base::TimeDelta> body_chunk_start_time_;
   std::optional<base::TimeDelta> header_chunk_start_time_;
   std::optional<base::TimeDelta> header_chunk_end_time_;
+
+  int64_t navigation_id_;
 };
 
 #endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_GWS_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn
index 00693ca..9b9786f0 100644
--- a/chrome/browser/password_manager/android/BUILD.gn
+++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -521,6 +521,8 @@
 
 source_set("utils") {
   sources = [
+    "first_cct_page_load_marker.cc",
+    "first_cct_page_load_marker.h",
     "password_generation_element_data.cc",
     "password_generation_element_data.h",
     "password_manager_android_util.cc",
@@ -544,6 +546,7 @@
     "//components/password_manager/core/common:common",
     "//components/prefs:prefs",
     "//components/version_info/android:channel_getter",
+    "//content/public/browser",
   ]
 }
 
@@ -560,6 +563,7 @@
     "credential_leak_controller_android_unittest.cc",
     "fake_password_manager_lifecycle_helper.cc",
     "fake_password_manager_lifecycle_helper.h",
+    "first_cct_page_load_marker_unittest.cc",
     "generated_password_saved_message_delegate_unittest.cc",
     "mock_password_checkup_launcher_helper.cc",
     "mock_password_checkup_launcher_helper.h",
diff --git a/chrome/browser/password_manager/android/access_loss/BUILD.gn b/chrome/browser/password_manager/android/access_loss/BUILD.gn
index 831377a..225a163 100644
--- a/chrome/browser/password_manager/android/access_loss/BUILD.gn
+++ b/chrome/browser/password_manager/android/access_loss/BUILD.gn
@@ -22,7 +22,6 @@
     "//build/android:build_java",
     "//chrome/browser/feedback/android:factory_java",
     "//chrome/browser/feedback/android:java",
-    "//chrome/browser/password_manager/android:java",
     "//chrome/browser/password_manager/android:java_resources",
     "//chrome/browser/password_manager/android/bottom_sheet:java",
     "//chrome/browser/profiles/android:java",
diff --git a/chrome/browser/password_manager/android/access_loss/java/src/org/chromium/chrome/browser/access_loss/PasswordAccessLossWarningHelper.java b/chrome/browser/password_manager/android/access_loss/java/src/org/chromium/chrome/browser/access_loss/PasswordAccessLossWarningHelper.java
index 06ebdc4..995e16f 100644
--- a/chrome/browser/password_manager/android/access_loss/java/src/org/chromium/chrome/browser/access_loss/PasswordAccessLossWarningHelper.java
+++ b/chrome/browser/password_manager/android/access_loss/java/src/org/chromium/chrome/browser/access_loss/PasswordAccessLossWarningHelper.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.access_loss;
 
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.ALL_KEYS;
-import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.BUTTON_ACTION;
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.BUTTON_TITLE;
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.SHEET_TEXT;
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.SHEET_TITLE;
@@ -20,7 +19,6 @@
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetCoordinator;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherFactory;
-import org.chromium.chrome.browser.password_manager.PasswordManagerHelper;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -92,11 +90,6 @@
                         BUTTON_TITLE,
                         mContext.getString(
                                 R.string.pwd_access_loss_warning_no_gms_core_button_text))
-                .with(
-                        BUTTON_ACTION,
-                        () -> {
-                            PasswordManagerHelper.showMainSettingsAndStartExport(mContext);
-                        })
                 .build();
     }
 
@@ -116,11 +109,6 @@
                         BUTTON_TITLE,
                         mContext.getString(
                                 R.string.pwd_access_loss_warning_update_gms_core_button_text))
-                .with(
-                        BUTTON_ACTION,
-                        () -> {
-                            PasswordManagerHelper.launchGmsUpdate(mContext);
-                        })
                 .build();
     }
 
@@ -142,11 +130,6 @@
                         BUTTON_TITLE,
                         mContext.getString(
                                 R.string.pwd_access_loss_warning_manual_migration_button_text))
-                .with(
-                        BUTTON_ACTION,
-                        () -> {
-                            PasswordManagerHelper.showMainSettingsAndStartExport(mContext);
-                        })
                 .build();
     }
 
diff --git a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinator.java b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinator.java
index 978eb8e..fb368724 100644
--- a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinator.java
+++ b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinator.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.bottom_sheet;
 
-import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.BUTTON_ACTION;
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.BUTTON_TITLE;
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.SHEET_TEXT;
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.SHEET_TITLE;
@@ -66,6 +65,5 @@
         assert !TextUtils.isEmpty(model.get(SHEET_TITLE));
         assert !TextUtils.isEmpty(model.get(SHEET_TEXT));
         assert !TextUtils.isEmpty(model.get(BUTTON_TITLE));
-        assert model.get(BUTTON_ACTION) != null;
     }
 }
diff --git a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinatorRobolectricTest.java b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinatorRobolectricTest.java
index 69b444c..7f8ff249 100644
--- a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinatorRobolectricTest.java
+++ b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinatorRobolectricTest.java
@@ -74,7 +74,6 @@
                         .with(SimpleNoticeSheetProperties.SHEET_TITLE, sTitle)
                         .with(SimpleNoticeSheetProperties.SHEET_TEXT, sText)
                         .with(SimpleNoticeSheetProperties.BUTTON_TITLE, sButtonText)
-                        .with(SimpleNoticeSheetProperties.BUTTON_ACTION, () -> {})
                         .build();
         mCoordinator.showSheet(model);
         verify(mBottomSheetController).requestShowContent(any(), anyBoolean());
diff --git a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetProperties.java b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetProperties.java
index f95886f..7f04ca00 100644
--- a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetProperties.java
+++ b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetProperties.java
@@ -17,9 +17,7 @@
             new PropertyModel.WritableObjectPropertyKey<>("sheet_text");
     public static final PropertyModel.WritableObjectPropertyKey<String> BUTTON_TITLE =
             new PropertyModel.WritableObjectPropertyKey<>("button_title");
-    public static final PropertyModel.ReadableObjectPropertyKey<Runnable> BUTTON_ACTION =
-            new PropertyModel.ReadableObjectPropertyKey<>("button_action");
 
     public static final PropertyKey[] ALL_KEYS =
-            new PropertyKey[] {SHEET_TITLE, SHEET_TEXT, BUTTON_TITLE, BUTTON_ACTION};
+            new PropertyKey[] {SHEET_TITLE, SHEET_TEXT, BUTTON_TITLE};
 }
diff --git a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetRenderTest.java b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetRenderTest.java
index 68f3186..6ff1b59 100644
--- a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetRenderTest.java
+++ b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetRenderTest.java
@@ -15,7 +15,6 @@
 import androidx.test.filters.MediumTest;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -126,7 +125,6 @@
                                     .with(SimpleNoticeSheetProperties.SHEET_TITLE, sTitle)
                                     .with(SimpleNoticeSheetProperties.SHEET_TEXT, sText)
                                     .with(SimpleNoticeSheetProperties.BUTTON_TITLE, sButtonText)
-                                    .with(SimpleNoticeSheetProperties.BUTTON_ACTION, Assert::fail)
                                     .build();
                     mCoordinator.showSheet(model);
                 });
diff --git a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetView.java b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetView.java
index f8ccfd0..cb0d5862 100644
--- a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetView.java
+++ b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetView.java
@@ -52,14 +52,6 @@
         button.setText(text);
     }
 
-    void setButtonAction(Runnable runnable) {
-        Button button = mContentView.findViewById(R.id.confirmation_button);
-        button.setOnClickListener(
-                (unusedView) -> {
-                    runnable.run();
-                });
-    }
-
     @Nullable
     @Override
     public View getContentView() {
diff --git a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetViewBinder.java b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetViewBinder.java
index 8090258e..037c4825 100644
--- a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetViewBinder.java
+++ b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetViewBinder.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.bottom_sheet;
 
-import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.BUTTON_ACTION;
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.BUTTON_TITLE;
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.SHEET_TEXT;
 import static org.chromium.chrome.browser.bottom_sheet.SimpleNoticeSheetProperties.SHEET_TITLE;
@@ -32,8 +31,6 @@
             view.setText(model.get(SHEET_TEXT));
         } else if (propertyKey == BUTTON_TITLE) {
             view.setButtonText(model.get(BUTTON_TITLE));
-        } else if (propertyKey == BUTTON_ACTION) {
-            view.setButtonAction(model.get(BUTTON_ACTION));
         } else {
             assert false : "Unhandled update to property:" + propertyKey;
         }
diff --git a/chrome/browser/password_manager/android/first_cct_page_load_marker.cc b/chrome/browser/password_manager/android/first_cct_page_load_marker.cc
new file mode 100644
index 0000000..7770941
--- /dev/null
+++ b/chrome/browser/password_manager/android/first_cct_page_load_marker.cc
@@ -0,0 +1,36 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/android/first_cct_page_load_marker.h"
+
+#include "content/public/browser/web_contents.h"
+
+// static
+void FirstCctPageLoadMarker::CreateForWebContents(
+    content::WebContents* web_contents) {
+  // This should only be created once, when the CCT is initialized.
+  if (web_contents->GetUserData(UserDataKey())) {
+    return;
+  }
+  web_contents->SetUserData(
+      UserDataKey(),
+      base::WrapUnique(new FirstCctPageLoadMarker(web_contents)));
+}
+
+// static
+bool FirstCctPageLoadMarker::ConsumeMarker(content::WebContents* web_contents) {
+  if (web_contents->GetUserData(UserDataKey())) {
+    web_contents->RemoveUserData(UserDataKey());
+    return true;
+  }
+  return false;
+}
+
+FirstCctPageLoadMarker::FirstCctPageLoadMarker(
+    content::WebContents* web_contents)
+    : content::WebContentsUserData<FirstCctPageLoadMarker>(*web_contents) {}
+
+FirstCctPageLoadMarker::~FirstCctPageLoadMarker() = default;
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(FirstCctPageLoadMarker);
diff --git a/chrome/browser/password_manager/android/first_cct_page_load_marker.h b/chrome/browser/password_manager/android/first_cct_page_load_marker.h
new file mode 100644
index 0000000..8d820302
--- /dev/null
+++ b/chrome/browser/password_manager/android/first_cct_page_load_marker.h
@@ -0,0 +1,34 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_FIRST_CCT_PAGE_LOAD_MARKER_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_FIRST_CCT_PAGE_LOAD_MARKER_H_
+
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+// Attached to the WebContents when the CCT is initialized to be
+// consumed by the password manager once the first page was loaded.
+// It identifies the first page loaded by the CCT.
+class FirstCctPageLoadMarker
+    : public content::WebContentsUserData<FirstCctPageLoadMarker> {
+ public:
+  static void CreateForWebContents(content::WebContents* web_contents);
+
+  // Returns whether the `FirstCctPageLoadmarker` exists and removes
+  // if from `web_contents` if it does.
+  static bool ConsumeMarker(content::WebContents* web_contents);
+
+  FirstCctPageLoadMarker(const FirstCctPageLoadMarker&) = delete;
+  FirstCctPageLoadMarker& operator=(const FirstCctPageLoadMarker&) = delete;
+  ~FirstCctPageLoadMarker() override;
+
+ private:
+  explicit FirstCctPageLoadMarker(content::WebContents* contents);
+
+  friend class content::WebContentsUserData<FirstCctPageLoadMarker>;
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_FIRST_CCT_PAGE_LOAD_MARKER_H_
diff --git a/chrome/browser/password_manager/android/first_cct_page_load_marker_unittest.cc b/chrome/browser/password_manager/android/first_cct_page_load_marker_unittest.cc
new file mode 100644
index 0000000..e89afbc3
--- /dev/null
+++ b/chrome/browser/password_manager/android/first_cct_page_load_marker_unittest.cc
@@ -0,0 +1,31 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/android/first_cct_page_load_marker.h"
+
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_web_contents_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+using testing::IsNull;
+using testing::NotNull;
+
+TEST(FirstCctPageLoadMarkerTest, ConsumeMarkerRemovesItFromWebContents) {
+  // Needed by `TestingProfile`.
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  content::TestWebContentsFactory test_web_contents_factory;
+  content::WebContents* web_contents =
+      test_web_contents_factory.CreateWebContents(&profile);
+  FirstCctPageLoadMarker::CreateForWebContents(web_contents);
+  EXPECT_THAT(FirstCctPageLoadMarker::FromWebContents(web_contents), NotNull());
+  EXPECT_TRUE(FirstCctPageLoadMarker::ConsumeMarker(web_contents));
+  EXPECT_THAT(FirstCctPageLoadMarker::FromWebContents(web_contents), IsNull());
+}
+
+}  // namespace
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportErrorDialogFragment.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportErrorDialogFragment.java
index ca307a5..57fc86b 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportErrorDialogFragment.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportErrorDialogFragment.java
@@ -13,6 +13,7 @@
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.DialogFragment;
+
 import org.chromium.chrome.browser.password_manager.R;
 
 /**
@@ -20,9 +21,12 @@
  * optionally helps them to take actions to fix that (learning more, retrying export).
  */
 public class ExportErrorDialogFragment extends DialogFragment {
-    /** Parameters to fill in the strings in the dialog. Pass them through {@link #initialize()}.*/
+    /** Parameters to fill in the strings in the dialog. Pass them through {@link #initialize()}. */
     public static class ErrorDialogParams {
-        /** The string resource ID for the label of the positive button. Required. */
+        /**
+         * The string resource ID for the label of the positive button. If it's 0, no positive
+         * button will be displayed.
+         */
         public int positiveButtonLabelId;
 
         /** The main description of the error. Required. */
@@ -67,13 +71,17 @@
         } else {
             detailedDescription.setVisibility(View.GONE);
         }
-        return new AlertDialog
-                .Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog_NoActionBar)
-                .setView(dialog)
-                .setTitle(R.string.password_settings_export_error_title)
-                .setPositiveButton(mParams.positiveButtonLabelId, mHandler)
-                .setNegativeButton(R.string.close, mHandler)
-                .create();
+        AlertDialog.Builder dialogBuilder =
+                new AlertDialog.Builder(
+                                getActivity(),
+                                R.style.ThemeOverlay_BrowserUI_AlertDialog_NoActionBar)
+                        .setView(dialog)
+                        .setTitle(R.string.password_settings_export_error_title)
+                        .setNegativeButton(R.string.close, mHandler);
+        if (mParams.positiveButtonLabelId != 0) {
+            dialogBuilder.setPositiveButton(mParams.positiveButtonLabelId, mHandler);
+        }
+        return dialogBuilder.create();
     }
 
     @Override
@@ -94,7 +102,6 @@
      */
     public void initialize(ErrorDialogParams params) {
         assert mParams == null && params != null;
-        assert params.positiveButtonLabelId != 0;
         assert params.description != null;
         mParams = params;
     }
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlow.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlow.java
index 8f3c7af..bea8e9a 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlow.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlow.java
@@ -248,7 +248,7 @@
             showExportErrorAndAbort(
                     R.string.password_settings_export_tips,
                     e.getMessage(),
-                    R.string.try_again,
+                    getPositiveButtonLabelId(),
                     HistogramExportResult.WRITE_FAILED);
             return;
         }
@@ -323,7 +323,7 @@
                             showExportErrorAndAbort(
                                     R.string.password_settings_export_tips,
                                     errorMessage,
-                                    R.string.try_again,
+                                    getPositiveButtonLabelId(),
                                     HistogramExportResult.WRITE_FAILED);
                         });
     }
@@ -589,7 +589,7 @@
             showExportErrorAndAbort(
                     R.string.password_settings_export_no_app,
                     e.getMessage(),
-                    R.string.try_again,
+                    getPositiveButtonLabelId(),
                     HistogramExportResult.NO_CONSUMER);
         }
     }
@@ -620,6 +620,14 @@
 
     @Override
     public void savePasswordsToDownloads(Uri passwordsFile) {
+        if (passwordsFile == null) {
+            showExportErrorAndAbort(
+                    R.string.password_settings_export_tips,
+                    "Could not create file.",
+                    getPositiveButtonLabelId(),
+                    HistogramExportResult.WRITE_FAILED);
+            return;
+        }
         new AsyncTask<String>() {
             @Override
             protected String doInBackground() {
@@ -639,7 +647,7 @@
                                 showExportErrorAndAbort(
                                         R.string.password_settings_export_tips,
                                         exceptionMessage,
-                                        R.string.try_again,
+                                        getPositiveButtonLabelId(),
                                         HistogramExportResult.WRITE_FAILED);
                             } else {
                                 mDelegate.onExportFlowSucceeded();
@@ -709,4 +717,20 @@
     public static boolean providesPasswordExport() {
         return ReauthenticationManager.isReauthenticationApiAvailable();
     }
+
+    private int getPositiveButtonLabelId() {
+        // Don't allow to try restarting the export flow from password access loss warning. The
+        // reason: it won't be able to create the file for saving passwords on disk because the
+        // dialog, which owns the export flow would be dismissed. There is a workaround: clicking on
+        // Google Password Manager will propose to restart the export flow.
+        // TODO (crbug.com/364530583): returning 0 here means there should be only one "Close"
+        // button in the dialog. Make error dialog configurable instead of passing a 0 resource into
+        // it.
+        if (ChromeFeatureList.isEnabled(
+                ChromeFeatureList
+                        .UNIFIED_PASSWORD_MANAGER_LOCAL_PASSWORDS_ANDROID_ACCESS_LOSS_WARNING)) {
+            return 0;
+        }
+        return R.string.try_again;
+    }
 }
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index a3a8b75..3489f69d 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/history/history_tab_helper.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/password_manager/account_password_store_factory.h"
+#include "chrome/browser/password_manager/android/first_cct_page_load_marker.h"
 #include "chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h"
 #include "chrome/browser/password_manager/chrome_webauthn_credentials_delegate_factory.h"
 #include "chrome/browser/password_manager/field_info_manager_factory.h"
@@ -120,6 +121,7 @@
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/build_info.h"
 #include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/android/tab_web_contents_delegate_android.h"
 #include "chrome/browser/keyboard_accessory/android/manual_filling_controller.h"
 #include "chrome/browser/keyboard_accessory/android/password_accessory_controller.h"
 #include "chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.h"
@@ -1096,6 +1098,16 @@
   return base::OptionalToPtr(metrics_recorder_);
 }
 
+#if BUILDFLAG(IS_ANDROID)
+password_manager::FirstCctPageLoadPasswordsUkmRecorder*
+ChromePasswordManagerClient::GetFirstCctPageLoadUkmRecorder() {
+  if (first_cct_page_load_metrics_recorder_) {
+    return first_cct_page_load_metrics_recorder_.get();
+  }
+  return nullptr;
+}
+#endif
+
 password_manager::PasswordRequirementsService*
 ChromePasswordManagerClient::GetPasswordRequirementsService() {
   return password_manager::PasswordRequirementsServiceFactory::
@@ -1686,10 +1698,24 @@
     tried_launching_access_loss_warning_on_startup = true;
     TryToShowAccessLossWarningSheet();
   }
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 }
 
 void ChromePasswordManagerClient::PrimaryPageChanged(content::Page& page) {
+#if BUILDFLAG(IS_ANDROID)
+  if (first_cct_page_load_metrics_recorder_) {
+    first_cct_page_load_metrics_recorder_.reset();
+  } else {
+    bool first_cct_page_load =
+        FirstCctPageLoadMarker::ConsumeMarker(web_contents());
+    TabAndroid* tab_android = TabAndroid::FromWebContents(web_contents());
+    if (tab_android && tab_android->IsCustomTab() && first_cct_page_load) {
+      first_cct_page_load_metrics_recorder_ = std::make_unique<
+          password_manager::FirstCctPageLoadPasswordsUkmRecorder>(
+          web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
+    }
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
   // Logging has no sense on WebUI sites.
   log_manager_->SetSuspended(web_contents()->GetWebUI() != nullptr);
 
@@ -1698,9 +1724,9 @@
 
   httpauth_manager_.OnDidFinishMainFrameNavigation();
 
-  // From this point on, the ContentCredentialManager will service API calls in
-  // the context of the new WebContents::GetLastCommittedURL, which may very
-  // well be cross-origin. Disconnect existing client, and drop pending
+  // From this point on, the ContentCredentialManager will service API calls
+  // in the context of the new WebContents::GetLastCommittedURL, which may
+  // very well be cross-origin. Disconnect existing client, and drop pending
   // requests.
   content_credential_manager_.DisconnectBinding();
 
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h
index 68bb4783..51a7f8e 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.h
+++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -36,6 +36,7 @@
 #include "components/safe_browsing/buildflags.h"
 #include "components/signin/public/base/signin_buildflags.h"
 #include "components/sync/service/sync_service.h"
+#include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host_receiver_set.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -48,6 +49,7 @@
 #include "chrome/browser/password_manager/android/password_migration_warning_startup_launcher.h"
 #include "chrome/browser/password_manager/android/save_update_password_message_delegate.h"
 #include "components/password_manager/core/browser/credential_cache.h"
+#include "components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.h"
 
 class PasswordAccessoryController;
 class TouchToFillController;
@@ -260,6 +262,10 @@
   ukm::SourceId GetUkmSourceId() override;
   password_manager::PasswordManagerMetricsRecorder* GetMetricsRecorder()
       override;
+#if BUILDFLAG(IS_ANDROID)
+  password_manager::FirstCctPageLoadPasswordsUkmRecorder*
+  GetFirstCctPageLoadUkmRecorder() override;
+#endif
   password_manager::PasswordRequirementsService*
   GetPasswordRequirementsService() override;
   favicon::FaviconService* GetFaviconService() override;
@@ -525,6 +531,13 @@
   // have been fetched. Only invoked once on startup.
   std::unique_ptr<PasswordMigrationWarningStartupLauncher>
       password_migration_warning_startup_launcher_;
+
+  // Recorder of metrics that is associated with the first page loaded by a
+  // CCT. Created only if the WebContents corresponds to a CCT. Records
+  // metrics on destruction, which happens on navigation.
+  std::unique_ptr<password_manager::FirstCctPageLoadPasswordsUkmRecorder>
+      first_cct_page_load_metrics_recorder_;
+
 #endif  // BUILDFLAG(IS_ANDROID)
 
   // Observes `AutofillManager`s of the `WebContents` that `this` belongs to.
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 07ab18e6..e585f8bc 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -257,6 +257,17 @@
   void FillPasswordSuggestion(const std::u16string& username,
                               const std::u16string& password) override {}
 
+  void FillPasswordSuggestionById(autofill::FieldRendererId username_element_id,
+                                  autofill::FieldRendererId password_element_id,
+                                  const std::u16string& username,
+                                  const std::u16string& password) override {}
+
+  void PreviewPasswordSuggestionById(
+      autofill::FieldRendererId username_element_id,
+      autofill::FieldRendererId password_element_id,
+      const std::u16string& username,
+      const std::u16string& password) override {}
+
   void InformNoSavedCredentials(
       bool should_show_popup_without_passwords) override {}
 
diff --git a/chrome/browser/password_manager/password_manager_uitest_util.cc b/chrome/browser/password_manager/password_manager_uitest_util.cc
index a83a63e..94d3eff 100644
--- a/chrome/browser/password_manager/password_manager_uitest_util.cc
+++ b/chrome/browser/password_manager/password_manager_uitest_util.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/password_manager/password_manager_uitest_util.h"
 
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
+#include "components/autofill/core/browser/autofill_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 void TestGenerationPopupObserver::OnPopupShown(
@@ -65,11 +66,13 @@
   DCHECK(!run_loop_);
 }
 
-void ObservingAutofillClient::ShowAutofillSuggestions(
+autofill::AutofillClient::SuggestionUiSessionId
+ObservingAutofillClient::ShowAutofillSuggestions(
     const autofill::AutofillClient::PopupOpenArgs& open_args,
     base::WeakPtr<autofill::AutofillSuggestionDelegate> delegate) {
   if (run_loop_) {
     run_loop_->Quit();
   }
   run_loop_ = nullptr;
+  return SuggestionUiSessionId();
 }
diff --git a/chrome/browser/password_manager/password_manager_uitest_util.h b/chrome/browser/password_manager/password_manager_uitest_util.h
index c4a4ef6..1403c17 100644
--- a/chrome/browser/password_manager/password_manager_uitest_util.h
+++ b/chrome/browser/password_manager/password_manager_uitest_util.h
@@ -58,7 +58,7 @@
   // Blocks the current thread until ShowAutofillSuggestions() is called.
   void WaitForAutofillPopup();
 
-  void ShowAutofillSuggestions(
+  SuggestionUiSessionId ShowAutofillSuggestions(
       const autofill::AutofillClient::PopupOpenArgs& open_args,
       base::WeakPtr<autofill::AutofillSuggestionDelegate> delegate) override;
 
diff --git a/chrome/browser/pdf/pdf_extension_js_test.cc b/chrome/browser/pdf/pdf_extension_js_test.cc
index e832677..1dc71c5 100644
--- a/chrome/browser/pdf/pdf_extension_js_test.cc
+++ b/chrome/browser/pdf/pdf_extension_js_test.cc
@@ -75,6 +75,12 @@
     RunTestsInJsModuleHelper(filename, pdf_filename, /*new_tab=*/true);
   }
 
+  // Loads `url` either in the current tab or a new tab and wait for it to be
+  // fully loaded before returning. Returns whether the PDF loaded or not.
+  virtual bool LoadPdfAndWait(const GURL& url, bool new_tab) {
+    return new_tab ? LoadPdfInNewTab(url) : LoadPdf(url);
+  }
+
  private:
   // Runs the extensions test at chrome/test/data/pdf/<filename> on the PDF file
   // at chrome/test/data/pdf/<pdf_filename>, where |filename| is loaded as a JS
@@ -82,18 +88,14 @@
   void RunTestsInJsModuleHelper(const std::string& filename,
                                 const std::string& pdf_filename,
                                 bool new_tab) {
-    extensions::ResultCatcher catcher;
-
     GURL url(embedded_test_server()->GetURL("/pdf/" + pdf_filename));
-
-    // Use `LoadPdfInNewTab()` or `LoadPdf()`, which ensures that the PDF is
-    // loaded before continuing.
-    ASSERT_TRUE(new_tab ? LoadPdfInNewTab(url) : LoadPdf(url));
+    ASSERT_TRUE(LoadPdfAndWait(url, new_tab));
     content::RenderFrameHost* extension_host =
         pdf_extension_test_util::GetOnlyPdfExtensionHost(
             GetActiveWebContents());
     ASSERT_TRUE(extension_host);
 
+    extensions::ResultCatcher catcher;
     constexpr char kModuleLoaderTemplate[] =
         R"(var s = document.createElement('script');
            s.type = 'module';
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index a9b1ff1e..3c259db 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -545,7 +545,8 @@
   ASSERT_EQ(browser()->tab_strip_model()->count(), 2);
   WebContents* new_tab_contents =
       browser()->tab_strip_model()->GetWebContentsAt(1);
-  EXPECT_TRUE(EnsureFullPagePDFHasLoadedWithValidFrameTree(new_tab_contents));
+  EXPECT_TRUE(EnsureFullPagePDFHasLoadedWithValidFrameTree(
+      new_tab_contents, /*allow_multiple_frames=*/false));
 }
 
 IN_PROC_BROWSER_TEST_P(PDFExtensionBlobNavigationTest, SameTab) {
@@ -553,8 +554,8 @@
       browser(),
       embedded_test_server()->GetURL("/pdf/blob_navigation_same_tab.html"),
       /*number_of_navigations=*/2));
-  EXPECT_TRUE(
-      EnsureFullPagePDFHasLoadedWithValidFrameTree(GetActiveWebContents()));
+  EXPECT_TRUE(EnsureFullPagePDFHasLoadedWithValidFrameTree(
+      GetActiveWebContents(), /*allow_multiple_frames=*/false));
 }
 
 IN_PROC_BROWSER_TEST_P(PDFExtensionTest, LoadInPlatformApp) {
@@ -1791,7 +1792,8 @@
   const GURL& current_url = web_contents->GetLastCommittedURL();
   ASSERT_EQ("/pdf/test-link.pdf", current_url.path());
 
-  ASSERT_TRUE(EnsureFullPagePDFHasLoadedWithValidFrameTree(web_contents));
+  ASSERT_TRUE(EnsureFullPagePDFHasLoadedWithValidFrameTree(
+      web_contents, /*allow_multiple_frames=*/false));
 
   content::RenderFrameHost* extension_host =
       GetOnlyPdfExtensionHostEnsureValid();
@@ -3085,7 +3087,8 @@
 
   prerender_helper().NavigatePrimaryPage(pdf_url);
   ASSERT_EQ(web_contents->GetLastCommittedURL(), pdf_url);
-  EXPECT_TRUE(EnsureFullPagePDFHasLoadedWithValidFrameTree(web_contents));
+  EXPECT_TRUE(EnsureFullPagePDFHasLoadedWithValidFrameTree(
+      web_contents, /*allow_multiple_frames=*/false));
 }
 
 // TODO(crbug.com/40180674): The PDF viewer cannot currently be prerendered
@@ -3380,8 +3383,9 @@
 
   // Wait for the MimeHandleView guest to be created.  This should return
   // before the actual PDF navigation in the guest is started.
-  GuestViewBase* guest_view = GetGuestViewManager(incognito->profile())
-                                  ->WaitForSingleGuestViewCreated();
+  GuestViewBase* guest_view =
+      GetGuestViewManagerForProfile(incognito->profile())
+          ->WaitForSingleGuestViewCreated();
   ASSERT_TRUE(guest_view);
 
   // Look up the PDF stream URL to which the navigation will take place.
@@ -3493,7 +3497,7 @@
       incognito_browser(), embedded_test_server()->GetURL("/pdf/test.pdf")));
 
   EXPECT_TRUE(EnsureFullPagePDFHasLoadedWithValidFrameTree(
-      GetIncognitoActiveWebContents()));
+      GetIncognitoActiveWebContents(), /*allow_multiple_frames=*/false));
 }
 
 // Test that an embed-embedded PDF viewer successfully loads in incognito.
diff --git a/chrome/browser/pdf/pdf_extension_test_base.cc b/chrome/browser/pdf/pdf_extension_test_base.cc
index e628add3..0110b1d 100644
--- a/chrome/browser/pdf/pdf_extension_test_base.cc
+++ b/chrome/browser/pdf/pdf_extension_test_base.cc
@@ -115,7 +115,8 @@
   EXPECT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
       browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
-  return EnsureFullPagePDFHasLoadedWithValidFrameTree(GetActiveWebContents());
+  return EnsureFullPagePDFHasLoadedWithValidFrameTree(
+      GetActiveWebContents(), /*allow_multiple_frames=*/false);
 }
 
 testing::AssertionResult PDFExtensionTestBase::LoadPdfInFirstChild(
@@ -236,7 +237,11 @@
   return guest ? guest->embedder_web_contents() : nullptr;
 }
 
-TestGuestViewManager* PDFExtensionTestBase::GetGuestViewManager(
+TestGuestViewManager* PDFExtensionTestBase::GetGuestViewManager() {
+  return GetGuestViewManagerForProfile(nullptr);
+}
+
+TestGuestViewManager* PDFExtensionTestBase::GetGuestViewManagerForProfile(
     content::BrowserContext* profile) {
   if (!profile) {
     profile = browser()->profile();
@@ -279,7 +284,7 @@
   return extension_host;
 }
 
-int PDFExtensionTestBase::CountPDFProcesses() {
+int PDFExtensionTestBase::CountPDFProcesses() const {
   return pdf_extension_test_util::CountPdfPluginProcesses(browser());
 }
 
@@ -317,16 +322,6 @@
 }
 
 void PDFExtensionTestBase::SimulateMouseClickAt(
-    extensions::MimeHandlerViewGuest* guest,
-    int modifiers,
-    blink::WebMouseEvent::Button button,
-    const gfx::Point& point_in_guest) {
-  SimulateMouseClickAt(guest->GetGuestMainFrame(),
-                       guest->embedder_web_contents(), modifiers, button,
-                       point_in_guest);
-}
-
-void PDFExtensionTestBase::SimulateMouseClickAt(
     content::RenderFrameHost* extension_host,
     content::WebContents* contents,
     int modifiers,
diff --git a/chrome/browser/pdf/pdf_extension_test_base.h b/chrome/browser/pdf/pdf_extension_test_base.h
index 85ce294f..38ff0ec 100644
--- a/chrome/browser/pdf/pdf_extension_test_base.h
+++ b/chrome/browser/pdf/pdf_extension_test_base.h
@@ -106,8 +106,9 @@
   content::WebContents* GetEmbedderWebContents();
 
  protected:
-  guest_view::TestGuestViewManager* GetGuestViewManager(
-      content::BrowserContext* profile = nullptr);
+  guest_view::TestGuestViewManager* GetGuestViewManager();
+  guest_view::TestGuestViewManager* GetGuestViewManagerForProfile(
+      content::BrowserContext* profile);
 
   pdf::TestPdfViewerStreamManager* GetTestPdfViewerStreamManager(
       content::WebContents* contents);
@@ -116,26 +117,19 @@
 
   content::RenderFrameHost* GetOnlyPdfExtensionHostEnsureValid();
 
-  int CountPDFProcesses();
+  int CountPDFProcesses() const;
 
   // Checks if the full page PDF loaded. The test will fail if it does not meet
   // the requirements of `ValidateFrameTree()`.
   testing::AssertionResult EnsureFullPagePDFHasLoadedWithValidFrameTree(
       content::WebContents* contents,
-      bool allow_multiple_frames = false);
+      bool allow_multiple_frames);
 
   // Check if the PDF loaded in the first child frame of `contents`. The test
   // will fail if it does not meet the requirements of `ValidateFrameTree()`.
   testing::AssertionResult EnsurePDFHasLoadedInFirstChildWithValidFrameTree(
       content::WebContents* contents);
 
-  // TODO(crbug.com/40268279): Remove this once there are no more existing use
-  // cases.
-  void SimulateMouseClickAt(extensions::MimeHandlerViewGuest* guest,
-                            int modifiers,
-                            blink::WebMouseEvent::Button button,
-                            const gfx::Point& point_in_guest);
-
   void SimulateMouseClickAt(content::RenderFrameHost* extension_host,
                             content::WebContents* contents,
                             int modifiers,
diff --git a/chrome/browser/pdf/pdf_extension_test_util.cc b/chrome/browser/pdf/pdf_extension_test_util.cc
index ca675e12..da6dd88 100644
--- a/chrome/browser/pdf/pdf_extension_test_util.cc
+++ b/chrome/browser/pdf/pdf_extension_test_util.cc
@@ -98,7 +98,7 @@
   return plugin_frames;
 }
 
-size_t CountPdfPluginProcesses(Browser* browser) {
+size_t CountPdfPluginProcesses(const Browser* browser) {
   base::flat_set<content::RenderProcessHost*> pdf_processes;
 
   const TabStripModel* tab_strip = browser->tab_strip_model();
diff --git a/chrome/browser/pdf/pdf_extension_test_util.h b/chrome/browser/pdf/pdf_extension_test_util.h
index 4e4a5cfd..bc6a1c4 100644
--- a/chrome/browser/pdf/pdf_extension_test_util.h
+++ b/chrome/browser/pdf/pdf_extension_test_util.h
@@ -67,7 +67,7 @@
     content::WebContents* contents);
 
 // Counts the total number of unique PDF plugin processes.
-size_t CountPdfPluginProcesses(Browser* browser);
+size_t CountPdfPluginProcesses(const Browser* browser);
 
 // Ensures, inside the given `frame`, that a PDF has either finished
 // loading or prompted a password. The result indicates success if the PDF loads
diff --git a/chrome/browser/performance_manager/test_support/fake_power_monitor_source.cc b/chrome/browser/performance_manager/test_support/fake_power_monitor_source.cc
index 6f3cc17..9ff14b00 100644
--- a/chrome/browser/performance_manager/test_support/fake_power_monitor_source.cc
+++ b/chrome/browser/performance_manager/test_support/fake_power_monitor_source.cc
@@ -6,7 +6,7 @@
 
 namespace performance_manager {
 
-bool FakePowerMonitorSource::IsOnBatteryPower() {
+bool FakePowerMonitorSource::IsOnBatteryPower() const {
   return on_battery_power_;
 }
 
diff --git a/chrome/browser/performance_manager/test_support/fake_power_monitor_source.h b/chrome/browser/performance_manager/test_support/fake_power_monitor_source.h
index 871f061..eeb59bcf 100644
--- a/chrome/browser/performance_manager/test_support/fake_power_monitor_source.h
+++ b/chrome/browser/performance_manager/test_support/fake_power_monitor_source.h
@@ -12,7 +12,7 @@
 
 class FakePowerMonitorSource : public base::PowerMonitorSource {
  public:
-  bool IsOnBatteryPower() override;
+  bool IsOnBatteryPower() const override;
   void SetOnBatteryPower(bool on_battery_power);
 
  private:
diff --git a/chrome/browser/performance_manager/test_support/test_user_performance_tuning_manager_environment.cc b/chrome/browser/performance_manager/test_support/test_user_performance_tuning_manager_environment.cc
index ce7ea5b4..bde7276 100644
--- a/chrome/browser/performance_manager/test_support/test_user_performance_tuning_manager_environment.cc
+++ b/chrome/browser/performance_manager/test_support/test_user_performance_tuning_manager_environment.cc
@@ -72,7 +72,7 @@
 #endif
   auto source = std::make_unique<FakePowerMonitorSource>();
   power_monitor_source_ = source.get();
-  base::PowerMonitor::Initialize(std::move(source));
+  base::PowerMonitor::GetInstance()->Initialize(std::move(source));
 
   if (!sampling_event_source) {
     auto test_sampling_event_source =
@@ -114,7 +114,7 @@
   user_performance_tuning_manager_.reset();
   battery_saver_mode_manager_.reset();
   battery_sampler_.reset();
-  base::PowerMonitor::ShutdownForTesting();
+  base::PowerMonitor::GetInstance()->ShutdownForTesting();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (tear_down_power_manager_client_) {
     chromeos::PowerManagerClient::Shutdown();
diff --git a/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager.cc b/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager.cc
index 4e84a73..e1a25cd 100644
--- a/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager.cc
+++ b/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager.cc
@@ -212,7 +212,8 @@
             base::Unretained(this)));
 
     on_battery_power_ =
-        base::PowerMonitor::AddPowerStateObserverAndReturnOnBatteryState(this);
+        base::PowerMonitor::GetInstance()
+            ->AddPowerStateObserverAndReturnOnBatteryState(this);
 
     base::BatteryStateSampler* battery_state_sampler =
         base::BatteryStateSampler::Get();
@@ -226,7 +227,7 @@
   }
 
   ~DesktopBatterySaverProvider() override {
-    base::PowerMonitor::RemovePowerStateObserver(this);
+    base::PowerMonitor::GetInstance()->RemovePowerStateObserver(this);
   }
 
   // BatterySaverProvider:
diff --git a/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager_unittest.cc b/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager_unittest.cc
index e1e25a2c..1d6d8e09 100644
--- a/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager_unittest.cc
+++ b/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager_unittest.cc
@@ -119,7 +119,7 @@
   void SetUp() override {
     auto source = std::make_unique<FakePowerMonitorSource>();
     power_monitor_source_ = source.get();
-    base::PowerMonitor::Initialize(std::move(source));
+    base::PowerMonitor::GetInstance()->Initialize(std::move(source));
 
     performance_manager::user_tuning::prefs::RegisterLocalStatePrefs(
         local_state_.registry());
@@ -147,7 +147,9 @@
     manager()->Start();
   }
 
-  void TearDown() override { base::PowerMonitor::ShutdownForTesting(); }
+  void TearDown() override {
+    base::PowerMonitor::GetInstance()->ShutdownForTesting();
+  }
 
   BatterySaverModeManager* manager() {
     return BatterySaverModeManager::GetInstance();
diff --git a/chrome/browser/performance_manager/user_tuning/performance_detection_manager.cc b/chrome/browser/performance_manager/user_tuning/performance_detection_manager.cc
index 9785295..d0212b3e 100644
--- a/chrome/browser/performance_manager/user_tuning/performance_detection_manager.cc
+++ b/chrome/browser/performance_manager/user_tuning/performance_detection_manager.cc
@@ -101,6 +101,7 @@
 
             policies::PageDiscardingHelper* const helper =
                 policies::PageDiscardingHelper::GetFromGraph(graph);
+            CHECK(helper);
             helper->ImmediatelyDiscardMultiplePages(
                 eligible_nodes, ::mojom::LifecycleUnitDiscardReason::SUGGESTED,
                 std::move(post_discard_cb));
diff --git a/chrome/browser/performance_manager/user_tuning/performance_detection_manager_unittest.cc b/chrome/browser/performance_manager/user_tuning/performance_detection_manager_unittest.cc
index 0b7778d1..c979d65 100644
--- a/chrome/browser/performance_manager/user_tuning/performance_detection_manager_unittest.cc
+++ b/chrome/browser/performance_manager/user_tuning/performance_detection_manager_unittest.cc
@@ -14,10 +14,14 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
+#include "chrome/browser/performance_manager/test_support/page_discarding_utils.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/performance_manager/public/features.h"
+#include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/public/resource_attribution/page_context.h"
+#include "components/performance_manager/test_support/run_in_graph.h"
 #include "components/performance_manager/test_support/test_harness_helper.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
@@ -123,6 +127,13 @@
     ChromeRenderViewHostTestHarness::SetUp();
     pm_harness_.SetUp();
     SetContents(CreateTestWebContents());
+    performance_manager::RunInGraph([](Graph* graph) {
+      auto page_discarding_helper =
+          std::make_unique<policies::PageDiscardingHelper>();
+      page_discarding_helper->SetMockDiscarderForTesting(
+          std::make_unique<testing::MockPageDiscarder>());
+      graph->PassToGraph(std::move(page_discarding_helper));
+    });
   }
 
   void TearDown() override {
diff --git a/chrome/browser/permissions/BUILD.gn b/chrome/browser/permissions/BUILD.gn
index 7c0c9ba..648f4aa8 100644
--- a/chrome/browser/permissions/BUILD.gn
+++ b/chrome/browser/permissions/BUILD.gn
@@ -55,14 +55,12 @@
     "quiet_notification_permission_ui_config.h",
     "quiet_notification_permission_ui_state.cc",
     "quiet_notification_permission_ui_state.h",
-    "system/platform_handle.h",
-    "system/system_permission_settings.cc",
-    "system/system_permission_settings.h",
   ]
 
   public_deps = [
     "//chrome/browser:browser_public_dependencies",
     "//chrome/browser/media/webrtc",
+    "//chrome/browser/permissions/system",
     "//chrome/browser/resource_coordinator:tab_lifecycle_observer",
     "//chrome/browser/ui:browser_navigator_params_headers",
   ]
@@ -70,7 +68,6 @@
   deps = [
     ":permissions_proto",
     "//chrome/browser:browser_process",
-    "//chrome/browser:global_features",
     "//chrome/browser/favicon",
     "//chrome/browser/profiles",
     "//chrome/browser/profiles:profile",
@@ -153,17 +150,6 @@
     ]
   }
 
-  if (is_mac) {
-    sources += [ "system/system_permission_settings_mac.cc" ]
-    deps += [ "//chrome//browser/web_applications" ]
-  } else if (is_chromeos_ash) {
-    sources += [ "system/system_permission_settings_chromeos.cc" ]
-  } else if (is_win) {
-    sources += [ "system/system_permission_settings_win.cc" ]
-  } else {
-    sources += [ "system/system_permission_settings_default.cc" ]
-  }
-
   if (build_with_tflite_lib) {
     sources += [
       "prediction_model_handler_provider_factory.cc",
@@ -175,13 +161,6 @@
     deps += [ "//components/segmentation_platform/internal:optimization_guide_segmentation_handler" ]
   }
 
-  if (os_level_geolocation_permission_supported) {
-    sources += [
-      "system/geolocation_observation.cc",
-      "system/geolocation_observation.h",
-    ]
-  }
-
   if (!is_android) {
     deps += [ "//chrome/browser/ui/permission_bubble" ]
   }
@@ -214,6 +193,7 @@
   deps = [
     ":permissions",
     ":permissions_proto",
+    "//chrome/browser/permissions/system",
     "//chrome/browser/search_engines",
     "//chrome/test:test_support",
     "//components/site_engagement/content",
@@ -265,6 +245,7 @@
       ":permissions",
       "//chrome/browser/media/webrtc",
       "//chrome/browser/optimization_guide:test_support",
+      "//chrome/browser/permissions/system",
       "//chrome/browser/ui:browser_element_identifiers",
       "//chrome/test:test_support",
       "//components/back_forward_cache",
@@ -293,6 +274,7 @@
 
     deps = [
       ":permissions",
+      "//chrome/browser/permissions/system",
       "//chrome/test:test_support",
       "//components/content_settings/browser",
       "//components/embedder_support",
diff --git a/chrome/browser/permissions/chrome_permissions_client.cc b/chrome/browser/permissions/chrome_permissions_client.cc
index 44050b36..8ba434f 100644
--- a/chrome/browser/permissions/chrome_permissions_client.cc
+++ b/chrome/browser/permissions/chrome_permissions_client.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/permissions/prediction_based_permission_ui_selector.h"
 #include "chrome/browser/permissions/pref_based_quiet_permission_ui_selector.h"
 #include "chrome/browser/permissions/quiet_notification_permission_ui_config.h"
+#include "chrome/browser/permissions/system/system_permission_settings.h"
 #include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profiles_state.h"
@@ -104,10 +105,6 @@
 #include "extensions/common/constants.h"
 #endif
 
-#if BUILDFLAG(IS_MAC)
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
-#endif
-
 namespace {
 
 #if BUILDFLAG(IS_ANDROID)
@@ -655,35 +652,17 @@
 bool ChromePermissionsClient::HasDevicePermission(
     ContentSettingsType type) const {
 #if BUILDFLAG(IS_MAC)
-  switch (type) {
-    case ContentSettingsType::MEDIASTREAM_MIC:
-      return system_media_permissions::CheckSystemAudioCapturePermission() ==
-             system_media_permissions::SystemPermission::kAllowed;
-    case ContentSettingsType::MEDIASTREAM_CAMERA:
-    case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
-      return system_media_permissions::CheckSystemVideoCapturePermission() ==
-             system_media_permissions::SystemPermission::kAllowed;
-    default:
-      break;
-  }
-#endif  // BUILDFLAG(IS_MAC)
+  return system_permission_settings::IsAllowed(type);
+#else
   return PermissionsClient::HasDevicePermission(type);
+#endif
 }
 
 bool ChromePermissionsClient::CanRequestDevicePermission(
     ContentSettingsType type) const {
 #if BUILDFLAG(IS_MAC)
-  switch (type) {
-    case ContentSettingsType::MEDIASTREAM_MIC:
-      return system_media_permissions::CheckSystemAudioCapturePermission() !=
-             system_media_permissions::SystemPermission::kRestricted;
-    case ContentSettingsType::MEDIASTREAM_CAMERA:
-    case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
-      return system_media_permissions::CheckSystemVideoCapturePermission() !=
-             system_media_permissions::SystemPermission::kRestricted;
-    default:
-      break;
-  }
-#endif  // BUILDFLAG(IS_MAC)
+  return system_permission_settings::CanPrompt(type);
+#else
   return PermissionsClient::CanRequestDevicePermission(type);
+#endif
 }
diff --git a/chrome/browser/permissions/system/BUILD.gn b/chrome/browser/permissions/system/BUILD.gn
new file mode 100644
index 0000000..4ac8ec7e
--- /dev/null
+++ b/chrome/browser/permissions/system/BUILD.gn
@@ -0,0 +1,58 @@
+# Copyright 2024 The Chromium Authors
+# 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("//services/device/public/cpp/geolocation/buildflags.gni")
+
+source_set("system") {
+  public = [
+    "media_authorization_wrapper_mac.h",
+    "platform_handle.h",
+    "system_permission_settings.h",
+  ]
+
+  sources = [ "system_permission_settings.cc" ]
+
+  public_deps = [
+    "//base",
+    "//components/content_settings/core/common",
+  ]
+
+  deps = [
+    "//chrome/browser:browser_process",
+    "//chrome/browser:global_features",
+    "//chrome/browser/profiles",
+    "//services/device/public/cpp/geolocation:geolocation",
+  ]
+
+  if (is_mac) {
+    public += [ "system_media_capture_permissions_mac.h" ]
+
+    sources += [
+      "system_media_capture_permissions_mac.h",
+      "system_media_capture_permissions_mac.mm",
+      "system_permission_settings_mac.cc",
+    ]
+    deps += [ "//chrome//browser/web_applications" ]
+  } else if (is_chromeos_ash) {
+    sources += [ "system_permission_settings_chromeos.cc" ]
+    deps += [
+      "//chrome/browser:browser_public_dependencies",
+      "//chrome/browser/ash/privacy_hub:privacy_hub",
+      "//chrome/browser/web_applications:web_applications",
+    ]
+  } else if (is_win) {
+    sources += [ "system_permission_settings_win.cc" ]
+    deps += [ "//chrome/browser:browser_public_dependencies" ]
+  } else {
+    sources += [ "system_permission_settings_default.cc" ]
+  }
+
+  if (os_level_geolocation_permission_supported) {
+    sources += [
+      "geolocation_observation.cc",
+      "geolocation_observation.h",
+    ]
+  }
+}
diff --git a/chrome/browser/permissions/system/geolocation_observation.cc b/chrome/browser/permissions/system/geolocation_observation.cc
index 9872140f..4941fc55 100644
--- a/chrome/browser/permissions/system/geolocation_observation.cc
+++ b/chrome/browser/permissions/system/geolocation_observation.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/permissions/system/geolocation_observation.h"
 
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "services/device/public/cpp/geolocation/geolocation_system_permission_manager.h"
 
 static_assert(BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED));
diff --git a/chrome/browser/permissions/system/media_authorization_wrapper_mac.h b/chrome/browser/permissions/system/media_authorization_wrapper_mac.h
new file mode 100644
index 0000000..9b16f35
--- /dev/null
+++ b/chrome/browser/permissions/system/media_authorization_wrapper_mac.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PERMISSIONS_SYSTEM_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
+#define CHROME_BROWSER_PERMISSIONS_SYSTEM_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
+
+#import <AVFoundation/AVFoundation.h>
+#import <Foundation/Foundation.h>
+
+#include "base/functional/callback_forward.h"
+
+namespace system_permission_settings {
+
+class MediaAuthorizationWrapper {
+ public:
+  virtual ~MediaAuthorizationWrapper() {}
+
+  virtual AVAuthorizationStatus AuthorizationStatusForMediaType(
+      NSString* media_type) = 0;
+  virtual void RequestAccessForMediaType(NSString* media_type,
+                                         base::OnceClosure callback) = 0;
+};
+
+}  // namespace system_permission_settings
+
+#endif  // CHROME_BROWSER_PERMISSIONS_SYSTEM_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
diff --git a/chrome/browser/permissions/system/system_media_capture_permissions_mac.h b/chrome/browser/permissions/system/system_media_capture_permissions_mac.h
new file mode 100644
index 0000000..becfdb3
--- /dev/null
+++ b/chrome/browser/permissions/system/system_media_capture_permissions_mac.h
@@ -0,0 +1,43 @@
+// Copyright 2019 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PERMISSIONS_SYSTEM_SYSTEM_MEDIA_CAPTURE_PERMISSIONS_MAC_H_
+#define CHROME_BROWSER_PERMISSIONS_SYSTEM_SYSTEM_MEDIA_CAPTURE_PERMISSIONS_MAC_H_
+
+#include "base/functional/callback_forward.h"
+
+namespace system_permission_settings {
+
+class MediaAuthorizationWrapper;
+
+// System permission state. These are also used in stats - do not remove or
+// re-arrange the values.
+enum class SystemPermission {
+  kNotDetermined = 0,
+  kRestricted = 1,
+  kDenied = 2,
+  kAllowed = 3,
+  kMaxValue = kAllowed
+};
+
+// Returns the system permission to capture audio or video.
+SystemPermission CheckSystemAudioCapturePermission();
+SystemPermission CheckSystemVideoCapturePermission();
+
+// Returns the system permission to capture the screen.
+SystemPermission CheckSystemScreenCapturePermission();
+
+// Requests the system permission to capture audio or video. This call
+// immediately returns. When requesting permission, the OS will show a user
+// dialog and respond asynchronously. At the response, |callback| is posted as a
+// reply on the requesting thread.
+void RequestSystemAudioCapturePermission(base::OnceClosure callback);
+void RequestSystemVideoCapturePermission(base::OnceClosure callback);
+
+// Sets the wrapper object for OS calls. For test mocking purposes.
+void SetMediaAuthorizationWrapperForTesting(MediaAuthorizationWrapper* wrapper);
+
+}  // namespace system_permission_settings
+
+#endif  // CHROME_BROWSER_PERMISSIONS_SYSTEM_SYSTEM_MEDIA_CAPTURE_PERMISSIONS_MAC_H_
diff --git a/chrome/browser/permissions/system/system_media_capture_permissions_mac.mm b/chrome/browser/permissions/system/system_media_capture_permissions_mac.mm
new file mode 100644
index 0000000..f5453d22
--- /dev/null
+++ b/chrome/browser/permissions/system/system_media_capture_permissions_mac.mm
@@ -0,0 +1,150 @@
+// Copyright 2019 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
+
+#import <AVFoundation/AVFoundation.h>
+
+#include "base/apple/foundation_util.h"
+#include "base/apple/scoped_cftyperef.h"
+#include "base/check.h"
+#include "base/command_line.h"
+#include "base/feature_list.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_helpers.h"
+#include "base/no_destructor.h"
+#import "base/task/sequenced_task_runner.h"
+#include "base/task/sequenced_task_runner.h"
+#include "chrome/browser/permissions/system/media_authorization_wrapper_mac.h"
+#include "chrome/common/chrome_features.h"
+#include "media/base/media_switches.h"
+#include "ui/base/cocoa/permissions_utils.h"
+
+namespace system_permission_settings {
+
+namespace {
+
+bool UsingFakeMediaDevices() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kUseFakeDeviceForMediaStream);
+}
+
+// Pointer to OS call wrapper that tests can set.
+MediaAuthorizationWrapper* g_media_authorization_wrapper_for_tests = nullptr;
+
+// Implementation of OS call wrapper that does the actual OS calls.
+class MediaAuthorizationWrapperImpl final : public MediaAuthorizationWrapper {
+ public:
+  MediaAuthorizationWrapperImpl() = default;
+
+  MediaAuthorizationWrapperImpl(const MediaAuthorizationWrapperImpl&) = delete;
+  MediaAuthorizationWrapperImpl& operator=(
+      const MediaAuthorizationWrapperImpl&) = delete;
+
+  ~MediaAuthorizationWrapperImpl() override = default;
+
+  AVAuthorizationStatus AuthorizationStatusForMediaType(
+      AVMediaType media_type) override {
+    return [AVCaptureDevice authorizationStatusForMediaType:media_type];
+  }
+
+  void RequestAccessForMediaType(AVMediaType media_type,
+                                 base::OnceClosure callback) override {
+    __block base::OnceClosure block_callback = std::move(callback);
+    __block scoped_refptr<base::SequencedTaskRunner> requesting_thread =
+        base::SequencedTaskRunner::GetCurrentDefault();
+    [AVCaptureDevice requestAccessForMediaType:media_type
+                             completionHandler:^(BOOL granted) {
+                               requesting_thread->PostTask(
+                                   FROM_HERE, std::move(block_callback));
+                             }];
+  }
+};
+
+MediaAuthorizationWrapper& GetMediaAuthorizationWrapper() {
+  if (g_media_authorization_wrapper_for_tests) {
+    return *g_media_authorization_wrapper_for_tests;
+  }
+
+  static base::NoDestructor<MediaAuthorizationWrapperImpl>
+      media_authorization_wrapper;
+  return *media_authorization_wrapper;
+}
+
+AVAuthorizationStatus MediaAuthorizationStatus(AVMediaType media_type) {
+  return GetMediaAuthorizationWrapper().AuthorizationStatusForMediaType(
+      media_type);
+}
+
+SystemPermission CheckSystemMediaCapturePermission(AVMediaType media_type) {
+  if (UsingFakeMediaDevices()) {
+    return SystemPermission::kAllowed;
+  }
+
+  AVAuthorizationStatus auth_status = MediaAuthorizationStatus(media_type);
+  switch (auth_status) {
+    case AVAuthorizationStatusNotDetermined:
+      return SystemPermission::kNotDetermined;
+    case AVAuthorizationStatusRestricted:
+      return SystemPermission::kRestricted;
+    case AVAuthorizationStatusDenied:
+      return SystemPermission::kDenied;
+    case AVAuthorizationStatusAuthorized:
+      return SystemPermission::kAllowed;
+    default:
+      NOTREACHED();
+  }
+}
+
+void RequestSystemMediaCapturePermission(AVMediaType media_type,
+                                         base::OnceClosure callback) {
+  if (UsingFakeMediaDevices()) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, std::move(callback));
+    return;
+  }
+
+  GetMediaAuthorizationWrapper().RequestAccessForMediaType(media_type,
+                                                           std::move(callback));
+}
+
+bool IsScreenCaptureAllowed() {
+  if (!base::FeatureList::IsEnabled(
+          features::kMacSystemScreenCapturePermissionCheck)) {
+    return true;
+  }
+
+  return ui::IsScreenCaptureAllowed();
+}
+
+}  // namespace
+
+SystemPermission CheckSystemAudioCapturePermission() {
+  return CheckSystemMediaCapturePermission(AVMediaTypeAudio);
+}
+
+SystemPermission CheckSystemVideoCapturePermission() {
+  return CheckSystemMediaCapturePermission(AVMediaTypeVideo);
+}
+
+SystemPermission CheckSystemScreenCapturePermission() {
+  return IsScreenCaptureAllowed() ? SystemPermission::kAllowed
+                                  : SystemPermission::kDenied;
+}
+
+void RequestSystemAudioCapturePermission(base::OnceClosure callback) {
+  RequestSystemMediaCapturePermission(AVMediaTypeAudio, std::move(callback));
+}
+
+void RequestSystemVideoCapturePermission(base::OnceClosure callback) {
+  RequestSystemMediaCapturePermission(AVMediaTypeVideo, std::move(callback));
+}
+
+void SetMediaAuthorizationWrapperForTesting(
+    MediaAuthorizationWrapper* wrapper) {
+  CHECK(!g_media_authorization_wrapper_for_tests);
+  g_media_authorization_wrapper_for_tests = wrapper;
+}
+
+}  // namespace system_permission_settings
diff --git a/chrome/browser/permissions/system/system_permission_settings_chromeos.cc b/chrome/browser/permissions/system/system_permission_settings_chromeos.cc
index e6d96a4..c03a9b1 100644
--- a/chrome/browser/permissions/system/system_permission_settings_chromeos.cc
+++ b/chrome/browser/permissions/system/system_permission_settings_chromeos.cc
@@ -10,7 +10,6 @@
 #include "base/functional/bind.h"
 #include "chrome/browser/ash/privacy_hub/privacy_hub_util.h"
 #include "chrome/browser/permissions/system/platform_handle.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/web_applications/manifest_update_utils.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 
@@ -41,8 +40,7 @@
 
   void OpenSystemSettings(content::WebContents*,
                           ContentSettingsType type) override {
-      ash::privacy_hub_util::OpenSystemSettings(
-          ProfileManager::GetActiveUserProfile(), type);
+    ash::privacy_hub_util::OpenSystemSettings(type);
   }
 
   void Request(ContentSettingsType type,
diff --git a/chrome/browser/permissions/system/system_permission_settings_mac.cc b/chrome/browser/permissions/system/system_permission_settings_mac.cc
index 0b5b2f4..a6c2a972 100644
--- a/chrome/browser/permissions/system/system_permission_settings_mac.cc
+++ b/chrome/browser/permissions/system/system_permission_settings_mac.cc
@@ -12,9 +12,9 @@
 #include "base/mac/mac_util.h"
 #include "base/notreached.h"
 #include "base/scoped_observation.h"
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
 #include "chrome/browser/permissions/system/geolocation_observation.h"
 #include "chrome/browser/permissions/system/platform_handle.h"
+#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
 #include "chrome/browser/web_applications/os_integration/mac/app_shim_registry.h"
 #include "chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
@@ -28,16 +28,18 @@
 namespace system_permission_settings {
 
 namespace {
-bool denied(system_media_permissions::SystemPermission permission) {
-  return system_media_permissions::SystemPermission::kDenied == permission;
+bool denied(system_permission_settings::SystemPermission permission) {
+  return system_permission_settings::SystemPermission::kDenied == permission ||
+         system_permission_settings::SystemPermission::kRestricted ==
+             permission;
 }
 
-bool prompt(system_media_permissions::SystemPermission permission) {
-  return system_media_permissions::SystemPermission::kNotDetermined ==
+bool prompt(system_permission_settings::SystemPermission permission) {
+  return system_permission_settings::SystemPermission::kNotDetermined ==
          permission;
 }
-bool allowed(system_media_permissions::SystemPermission permission) {
-  return system_media_permissions::SystemPermission::kAllowed == permission;
+bool allowed(system_permission_settings::SystemPermission permission) {
+  return system_permission_settings::SystemPermission::kAllowed == permission;
 }
 
 class PlatformHandleImpl : public PlatformHandle {
@@ -45,11 +47,12 @@
   bool CanPrompt(ContentSettingsType type) override {
     switch (type) {
       case ContentSettingsType::MEDIASTREAM_CAMERA:
+      case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
         return prompt(
-            system_media_permissions::CheckSystemVideoCapturePermission());
+            system_permission_settings::CheckSystemVideoCapturePermission());
       case ContentSettingsType::MEDIASTREAM_MIC:
         return prompt(
-            system_media_permissions::CheckSystemAudioCapturePermission());
+            system_permission_settings::CheckSystemAudioCapturePermission());
       case ContentSettingsType::GEOLOCATION:
         return device::GeolocationSystemPermissionManager::GetInstance()
                    ->GetSystemPermission() ==
@@ -62,11 +65,12 @@
   bool IsDenied(ContentSettingsType type) override {
     switch (type) {
       case ContentSettingsType::MEDIASTREAM_CAMERA:
+      case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
         return denied(
-            system_media_permissions::CheckSystemVideoCapturePermission());
+            system_permission_settings::CheckSystemVideoCapturePermission());
       case ContentSettingsType::MEDIASTREAM_MIC:
         return denied(
-            system_media_permissions::CheckSystemAudioCapturePermission());
+            system_permission_settings::CheckSystemAudioCapturePermission());
       case ContentSettingsType::GEOLOCATION:
         return device::GeolocationSystemPermissionManager::GetInstance()
                    ->GetSystemPermission() ==
@@ -79,11 +83,12 @@
   bool IsAllowed(ContentSettingsType type) override {
     switch (type) {
       case ContentSettingsType::MEDIASTREAM_CAMERA:
+      case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
         return allowed(
-            system_media_permissions::CheckSystemVideoCapturePermission());
+            system_permission_settings::CheckSystemVideoCapturePermission());
       case ContentSettingsType::MEDIASTREAM_MIC:
         return allowed(
-            system_media_permissions::CheckSystemAudioCapturePermission());
+            system_permission_settings::CheckSystemAudioCapturePermission());
       case ContentSettingsType::GEOLOCATION:
         return device::GeolocationSystemPermissionManager::GetInstance()
                    ->GetSystemPermission() ==
@@ -107,7 +112,8 @@
             web_app::GetBundleIdentifierForShim(*app_id));
         return;
       }
-      case ContentSettingsType::MEDIASTREAM_CAMERA: {
+      case ContentSettingsType::MEDIASTREAM_CAMERA:
+      case ContentSettingsType::CAMERA_PAN_TILT_ZOOM: {
         base::mac::OpenSystemSettingsPane(
             base::mac::SystemSettingsPane::kPrivacySecurity_Camera);
         return;
@@ -130,13 +136,14 @@
   void Request(ContentSettingsType type,
                SystemPermissionResponseCallback callback) override {
     switch (type) {
-      case ContentSettingsType::MEDIASTREAM_CAMERA: {
-        system_media_permissions::RequestSystemVideoCapturePermission(
+      case ContentSettingsType::MEDIASTREAM_CAMERA:
+      case ContentSettingsType::CAMERA_PAN_TILT_ZOOM: {
+        system_permission_settings::RequestSystemVideoCapturePermission(
             std::move(callback));
         return;
       }
       case ContentSettingsType::MEDIASTREAM_MIC: {
-        system_media_permissions::RequestSystemAudioCapturePermission(
+        system_permission_settings::RequestSystemAudioCapturePermission(
             std::move(callback));
         return;
       }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index b79e4da49..6f41b80 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -2423,6 +2423,9 @@
   gen_ai_default_policies.emplace_back(
       key::kHistorySearchSettings,
       optimization_guide::prefs::kHistorySearchEnterprisePolicyAllowed);
+  gen_ai_default_policies.emplace_back(
+      key::kTabCompareSettings,
+      optimization_guide::prefs::kProductSpecificationsEnterprisePolicyAllowed);
   handlers->AddHandler(std::make_unique<GenAiDefaultSettingsPolicyHandler>(
       std::move(gen_ai_default_policies)));
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
diff --git a/chrome/browser/policy/test/autofill_policy_browsertest.cc b/chrome/browser/policy/test/autofill_policy_browsertest.cc
index 9551b905..c44735a 100644
--- a/chrome/browser/policy/test/autofill_policy_browsertest.cc
+++ b/chrome/browser/policy/test/autofill_policy_browsertest.cc
@@ -141,12 +141,12 @@
     explicit TestAutofillClient(content::WebContents* web_contents)
         : autofill::ChromeAutofillClient(web_contents) {}
 
-    void ShowAutofillSuggestions(
+    SuggestionUiSessionId ShowAutofillSuggestions(
         const autofill::AutofillClient::PopupOpenArgs& open_args,
         base::WeakPtr<autofill::AutofillSuggestionDelegate> delegate) override {
-      autofill::ChromeAutofillClient::ShowAutofillSuggestions(open_args,
-                                                              delegate);
       suggestions_ = open_args.suggestions;
+      return autofill::ChromeAutofillClient::ShowAutofillSuggestions(open_args,
+                                                                     delegate);
     }
 
     const std::vector<autofill::Suggestion>& suggestions() const {
diff --git a/chrome/browser/predictors/loading_predictor.cc b/chrome/browser/predictors/loading_predictor.cc
index 9915491..ce6150c 100644
--- a/chrome/browser/predictors/loading_predictor.cc
+++ b/chrome/browser/predictors/loading_predictor.cc
@@ -93,8 +93,8 @@
 #if BUILDFLAG(IS_ANDROID)
   // Preconnecting is expensive while on battery power and cellular data and
   // the radio signal is weak.
-  if ((base::PowerMonitor::IsInitialized() &&
-       !base::PowerMonitor::IsOnBatteryPower()) ||
+  if (auto* power_monitor = base::PowerMonitor::GetInstance();
+      (power_monitor->IsInitialized() && !power_monitor->IsOnBatteryPower()) ||
       (base::android::RadioUtils::GetConnectionType() !=
        base::android::RadioConnectionType::kCell)) {
     return false;
@@ -113,8 +113,9 @@
   static const bool kEnabled =
       base::FeatureList::IsEnabled(
           blink::features::kSpeculativeServiceWorkerWarmUp) &&
-      blink::features::kSpeculativeServiceWorkerWarmUpFromLoadingPredictor
-          .Get();
+      base::GetFieldTrialParamByFeatureAsBool(
+          blink::features::kSpeculativeServiceWorkerWarmUp,
+          "sw_warm_up_from_loading_predictor", true);
   if (!kEnabled) {
     return;
   }
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc
index 8baae28..73ebfe0 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc
@@ -403,6 +403,7 @@
 
   switch (cookie_controls_mode) {
     case content_settings::CookieControlsMode::kBlockThirdParty:
+    case content_settings::CookieControlsMode::kLimited:
       return false;
     case content_settings::CookieControlsMode::kIncognitoOnly:
     case content_settings::CookieControlsMode::kOff:
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index ed4d72e..85575a9 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -312,6 +312,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_features.h"
+#include "chrome/browser/apps/almanac_api_client/device_info_manager_factory.h"
 #include "chrome/browser/apps/app_preload_service/app_preload_service_factory.h"
 #include "chrome/browser/apps/app_service/publishers/web_apps_crosapi_factory.h"
 #include "chrome/browser/apps/app_service/subscriber_crosapi_factory.h"
@@ -637,6 +638,7 @@
   apps::AppServiceProxyFactory::GetInstance();
 #endif
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+  apps::DeviceInfoManagerFactory::GetInstance();
   apps::StandaloneBrowserExtensionAppsFactoryForApp::GetInstance();
   apps::StandaloneBrowserExtensionAppsFactoryForExtension::GetInstance();
   apps::SubscriberCrosapiFactory::GetInstance();
diff --git a/chrome/browser/readaloud/android/BUILD.gn b/chrome/browser/readaloud/android/BUILD.gn
index 675266c..0e075b13 100644
--- a/chrome/browser/readaloud/android/BUILD.gn
+++ b/chrome/browser/readaloud/android/BUILD.gn
@@ -36,6 +36,7 @@
     "//chrome/browser/tabmodel:java",
     "//chrome/browser/ui/android/appmenu:java",
     "//chrome/browser/ui/android/layouts:java",
+    "//chrome/browser/ui/android/native_page:java",
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//chrome/browser/ui/android/toolbar:java",
     "//chrome/browser/user_education:java",
@@ -237,6 +238,7 @@
     "//chrome/browser/tabmodel:junit",
     "//chrome/browser/ui/android/appmenu:java",
     "//chrome/browser/ui/android/layouts:java",
+    "//chrome/browser/ui/android/native_page:java",
     "//chrome/browser/ui/android/toolbar:java",
     "//chrome/browser/user_education:java",
     "//chrome/test/android:chrome_java_integration_test_support",
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
index 0c8b21e..1df2602 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
@@ -778,7 +778,9 @@
                 || mProfileSupplier.get() == null
                 || !mProfileSupplier.get().isNativeInitialized()
                 || DeviceConditions.getCurrentNetConnectionType(mActivity.getApplicationContext())
-                        == ConnectionType.CONNECTION_NONE) {
+                        == ConnectionType.CONNECTION_NONE
+                // TODO(crbug.com/363326024): Remove once feature is supported for PDF.
+                || (tab.isNativePage() && tab.getNativePage().isPdf())) {
             return false;
         }
 
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
index 1619854..826392a 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
@@ -82,6 +82,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.translate.FakeTranslateBridgeJni;
 import org.chromium.chrome.browser.translate.TranslateBridgeJni;
+import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.modules.readaloud.Playback;
 import org.chromium.chrome.modules.readaloud.Playback.PlaybackTextPart;
 import org.chromium.chrome.modules.readaloud.Playback.PlaybackTextType;
@@ -170,6 +171,7 @@
     @Mock private TemplateUrl mSearchEngine;
     @Mock private SelectionClient mSelectionClient;
     @Mock private SelectionPopupController mSelectionPopupController;
+    @Mock private NativePage mNativePage;
     private GlobalRenderFrameHostId mGlobalRenderFrameHostId = new GlobalRenderFrameHostId(1, 1);
     public UserActionTester mUserActionTester;
     private HistogramWatcher mHighlightingEnabledOnStartupHistogram;
@@ -819,6 +821,14 @@
         when(mTab.getWebContents()).thenReturn(mWebContents);
         doReturn(false).when(mMockProfile).isNativeInitialized();
         assertFalse(mController.isReadable(mTab));
+
+        when(mTab.getUrl()).thenReturn(sTestGURL);
+        when(mTab.getWebContents()).thenReturn(mWebContents);
+        doReturn(true).when(mMockProfile).isNativeInitialized();
+        doReturn(true).when(mTab).isNativePage();
+        doReturn(mNativePage).when(mTab).getNativePage();
+        doReturn(true).when(mNativePage).isPdf();
+        assertFalse(mController.isReadable(mTab));
     }
 
     @Test
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index fcd57509..94cb139c1 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -51,6 +51,11 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/permissions/system/system_permission_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 namespace resource_coordinator {
 
 namespace {
@@ -358,6 +363,12 @@
 }
 
 TEST_F(TabLifecycleUnitTest, CannotDiscardVideoCapture) {
+#if BUILDFLAG(IS_CHROMEOS)
+  // Mock system-level microphone permission.
+  system_permission_settings::ScopedSettingsForTesting mic_settings(
+      ContentSettingsType::MEDIASTREAM_MIC, false);
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
   TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
diff --git a/chrome/browser/resources/commerce/product_specifications/header.html b/chrome/browser/resources/commerce/product_specifications/header.html
index 19287ef..00eacf8 100644
--- a/chrome/browser/resources/commerce/product_specifications/header.html
+++ b/chrome/browser/resources/commerce/product_specifications/header.html
@@ -79,7 +79,8 @@
 </template>
 <template is="dom-if" if="[[!showingInput_]]">
   <div class="title-text" id="subtitle" hidden="[[!subtitle]]"
-      on-click="onRenaming_">
+      on-click="onRenaming_" on-keydown="onSubtitleKeyDown_" role="textbox"
+      tabindex="0">
     [[subtitle]]
   </div>
 </template>
diff --git a/chrome/browser/resources/commerce/product_specifications/header.ts b/chrome/browser/resources/commerce/product_specifications/header.ts
index 665a700..c521007 100644
--- a/chrome/browser/resources/commerce/product_specifications/header.ts
+++ b/chrome/browser/resources/commerce/product_specifications/header.ts
@@ -18,6 +18,7 @@
 
 export interface HeaderElement {
   $: {
+    divider: HTMLElement,
     menuButton: CrIconButtonElement,
     menu: HeaderMenuElement,
   };
@@ -106,6 +107,13 @@
       },
     }));
   }
+
+  private onSubtitleKeyDown_(event: KeyboardEvent) {
+    if (event.key === 'Enter') {
+      event.stopPropagation();
+      this.onRenaming_();
+    }
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/data_sharing/BUILD.gn b/chrome/browser/resources/data_sharing/BUILD.gn
index b3d2d28..e2469f1c 100644
--- a/chrome/browser/resources/data_sharing/BUILD.gn
+++ b/chrome/browser/resources/data_sharing/BUILD.gn
@@ -10,6 +10,7 @@
 }
 build_webui("build") {
   grd_prefix = "data_sharing"
+  ts_composite = true
 
   ts_deps = [
     "//ui/webui/resources/cr_components/color_change_listener:build_ts",
@@ -34,10 +35,9 @@
 
   non_web_component_files = [
     "browser_proxy.ts",
-    "browser_proxy_api.ts",
-    "browser_proxy_base.ts",
     "data_sharing.ts",
     "data_sharing_api.ts",
+    "mojom_conversion_utils.ts",
   ]
 
   if (is_chrome_branded) {
diff --git a/chrome/browser/resources/data_sharing/browser_proxy.ts b/chrome/browser/resources/data_sharing/browser_proxy.ts
index d1818e00..599c8213 100644
--- a/chrome/browser/resources/data_sharing/browser_proxy.ts
+++ b/chrome/browser/resources/data_sharing/browser_proxy.ts
@@ -11,7 +11,6 @@
 
   constructor() {
     this.callbackRouter = new PageCallbackRouter();
-
     this.handler = new PageHandlerRemote();
 
     const factory = PageHandlerFactory.getRemote();
diff --git a/chrome/browser/resources/data_sharing/browser_proxy_api.ts b/chrome/browser/resources/data_sharing/browser_proxy_api.ts
deleted file mode 100644
index 02ee8588..0000000
--- a/chrome/browser/resources/data_sharing/browser_proxy_api.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {BrowserProxyBase} from './browser_proxy_base.js';
-import type {GroupData} from './group_data.mojom-webui.js';
-
-// Browser Proxy for the data sharing service.
-// Only implement APIs related to data sharing service.
-export class BrowserProxyApi extends BrowserProxyBase {
-  constructor() {
-    super();
-
-    this.callbackRouter.readGroups.addListener((groupIds: string[]) => {
-      // Dummy implementation of readGroups API.
-      // TODO(b/346625367): Replace this with real implementation.
-      return new Promise((resolve) => {
-        setTimeout(() => {
-          const groups: GroupData[] = [];
-          for (const groupId of groupIds) {
-            groups.push({
-              groupId: groupId,
-              displayName: 'test',
-              accessToken: 'abc',
-              members: [],
-            });
-          }
-          resolve({groups});
-        }, 1);
-      });
-    });
-  }
-
-  static getInstance(): BrowserProxyApi {
-    return instance || (instance = new BrowserProxyApi());
-  }
-
-  static setInstance(obj: BrowserProxyApi) {
-    instance = obj;
-  }
-}
-
-let instance: BrowserProxyApi|null = null;
diff --git a/chrome/browser/resources/data_sharing/browser_proxy_base.ts b/chrome/browser/resources/data_sharing/browser_proxy_base.ts
deleted file mode 100644
index 7d84eb13..0000000
--- a/chrome/browser/resources/data_sharing/browser_proxy_base.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import type {PageHandlerInterface} from './data_sharing.mojom-webui.js';
-import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote} from './data_sharing.mojom-webui.js';
-
-// Base class of BrowserProxy.
-// Implement common logic i.e. setting mojom pipe.
-export class BrowserProxyBase {
-  callbackRouter: PageCallbackRouter;
-  handler: PageHandlerInterface;
-
-  constructor() {
-    this.callbackRouter = new PageCallbackRouter();
-    this.handler = new PageHandlerRemote();
-
-    const factory = PageHandlerFactory.getRemote();
-    factory.createPageHandler(
-        this.callbackRouter.$.bindNewPipeAndPassRemote(),
-        (this.handler as PageHandlerRemote).$.bindNewPipeAndPassReceiver());
-  }
-}
diff --git a/chrome/browser/resources/data_sharing/data_sharing_api.ts b/chrome/browser/resources/data_sharing/data_sharing_api.ts
index 9eb3d98..2da5e36 100644
--- a/chrome/browser/resources/data_sharing/data_sharing_api.ts
+++ b/chrome/browser/resources/data_sharing/data_sharing_api.ts
@@ -4,11 +4,14 @@
 
 import './data_sharing_sdk.js';
 
-import {BrowserProxyApi} from './browser_proxy_api.js';
+import {BrowserProxy} from './browser_proxy.js';
+import type {GroupData} from './group_data.mojom-webui.js';
+import {toMojomGroupData} from './mojom_conversion_utils.js';
+
 
 let initialized: boolean = false;
 
-const browserProxy: BrowserProxyApi = BrowserProxyApi.getInstance();
+const browserProxy: BrowserProxy = BrowserProxy.getInstance();
 
 browserProxy.callbackRouter.onAccessTokenFetched.addListener(
     (accessToken: string) => {
@@ -19,3 +22,21 @@
       }
     },
 );
+
+browserProxy.callbackRouter.readGroups.addListener((groupIds: string[]) => {
+  return new Promise((resolve) => {
+    window.data_sharing_sdk.readGroups({groupIds})
+        .then(
+            (groups) => {
+              const groupData: GroupData[] = [];
+              for (const group of groups) {
+                groupData.push(toMojomGroupData(group));
+              }
+              resolve({groups: groupData});
+            },
+            (err) => {
+              console.error(err);
+              throw err;
+            });
+  });
+});
diff --git a/chrome/browser/resources/data_sharing/data_sharing_sdk.ts b/chrome/browser/resources/data_sharing/data_sharing_sdk.ts
index ed22089..b46caef 100644
--- a/chrome/browser/resources/data_sharing/data_sharing_sdk.ts
+++ b/chrome/browser/resources/data_sharing/data_sharing_sdk.ts
@@ -125,7 +125,22 @@
 window.data_sharing_sdk = {
   setOauthAccessToken: () => void{},
   createGroup: () => Promise.resolve({id: '', members: []}),
-  readGroups: () => Promise.resolve([]),
+  readGroups: (options: DataSharingSdkGroupIds&{}):
+      Promise<DataSharingSdkGroup[]> => {
+        return new Promise((resolve) => {
+          resolve(options.groupIds!.map(groupId => ({
+                                          id: groupId,
+                                          name: 'GROUP_NAME',
+                                          members: [{
+                                            photoUrl: 'http://example.com',
+                                            displayName: 'MEMBER_NAME',
+                                            profileId: 'GAIA_ID',
+                                            role: 'member',
+                                            displayValue: 'test@gmail.com',
+                                          }],
+                                        })));
+        });
+      },
   runJoinFlow: (): Promise<DataSharingSdkResponse> => {
     appendTextForTesting('A fake join dialog');
     return Promise.resolve({});
diff --git a/chrome/browser/resources/data_sharing/mojom_conversion_utils.ts b/chrome/browser/resources/data_sharing/mojom_conversion_utils.ts
new file mode 100644
index 0000000..a3bf15b
--- /dev/null
+++ b/chrome/browser/resources/data_sharing/mojom_conversion_utils.ts
@@ -0,0 +1,51 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+import './data_sharing_sdk.js';
+
+import type {GroupData, GroupMember} from './group_data.mojom-webui.js';
+import {MemberRole} from './group_data.mojom-webui.js';
+
+// Utilities to convert DataSharingSdkGroup in data sharing sdk to GroupData in
+// group_data.mojom
+// TODO(crbug.com/362524829): Replace any with types once they are exported from
+// data sharing sdk.
+export function toMojomGroupData(group: any): GroupData {
+  const members: GroupMember[] = [];
+  for (const member of group.members) {
+    members.push(toMojomGroupMember(member));
+  }
+  return {
+    groupId: group.id,
+    displayName: group.name || '',
+    // Fetching access token is not yet supported for the API.
+    accessToken: '',
+    members,
+  };
+}
+
+function toMojomGroupMember(member: any): GroupMember {
+  return {
+    gaiaId: member.profileId,
+    displayName: member.displayName,
+    email: member.displayValue,
+    role: toMojomRole(member.role),
+    avatarUrl: {url: member.photoUrl},
+  };
+}
+
+function toMojomRole(role: any): MemberRole {
+  // 'applicant' is not yet supported for the API.
+  switch (role) {
+    case 'invitee':
+      return MemberRole.kInvitee;
+    case 'member':
+      return MemberRole.kMember;
+    case 'owner':
+      return MemberRole.kOwner;
+    default:
+      return MemberRole.kUnspecified;
+  }
+}
diff --git a/chrome/browser/resources/search_engine_choice/BUILD.gn b/chrome/browser/resources/search_engine_choice/BUILD.gn
index f45397f..cbe554a 100644
--- a/chrome/browser/resources/search_engine_choice/BUILD.gn
+++ b/chrome/browser/resources/search_engine_choice/BUILD.gn
@@ -18,16 +18,25 @@
 
   non_web_component_files = [ "browser_proxy.ts" ]
 
+  css_files = [ "app.css" ]
+
   mojo_files_deps = [ "//chrome/browser/ui/webui/search_engine_choice:mojo_bindings_ts__generator" ]
   mojo_files = [ "$root_gen_dir/chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom-webui.ts" ]
 
+  html_to_wrapper_template = "lit"
+
   ts_deps = [
-    "//third_party/polymer/v3_0:library",
+    "//chrome/browser/resources/signin:build_ts",
+    "//third_party/lit/v3_0:build_ts",
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
 
+  ts_path_mappings = [ "/tangible_sync_style_shared_lit.css.js|" + rebase_path(
+                           "$root_gen_dir/chrome/browser/resources/signin/tsc/tangible_sync_style_shared_lit.css.d.ts",
+                           target_gen_dir) ]
+
   ts_definitions = [ "//tools/typescript/definitions/metrics_private.d.ts" ]
   ts_composite = true
   webui_context_type = "trusted"
diff --git a/chrome/browser/resources/search_engine_choice/app.css b/chrome/browser/resources/search_engine_choice/app.css
new file mode 100644
index 0000000..8aa5be8
--- /dev/null
+++ b/chrome/browser/resources/search_engine_choice/app.css
@@ -0,0 +1,281 @@
+/* Copyright 2024 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #import=chrome://resources/cr_elements/cr_icons_lit.css.js
+ * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
+ * #import=/tangible_sync_style_shared_lit.css.js
+ * #scheme=relative
+ * #include=cr-icons-lit tangible-sync-style-shared-lit
+ * #css_wrapper_metadata_end */
+
+:host {
+  --background-color: white;
+  --search-engine-icon-size: 24px;
+  --action-button-margins: 24px;
+  --content-container-width: 500px;
+  --radio-button-horizontal-padding: 16px;
+  --choice-gap: 16px;
+  --search-engine-name-line-height: 20px;
+  --marketing-snippet-line-height: 16px;
+  --choice-height: calc(var(--search-engine-name-line-height) +
+      var(--marketing-snippet-line-height));
+  color: var(--cr-primary-text-color);
+}
+
+.tangible-sync-style-left-banner {
+  content: url(images/left_illustration.svg);
+}
+
+.tangible-sync-style-right-banner {
+  content: url(images/right_illustration.svg);
+}
+
+.tangible-sync-style-left-banner,
+.tangible-sync-style-right-banner {
+  position: fixed;
+}
+
+#actionButton {
+  height: unset;
+  margin: var(--action-button-margins);
+  padding: 6px 16px 6px 12px;
+  pointer-events: all;
+  text-align: center;
+}
+
+.content-container {
+  align-items: center;
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  margin: auto;
+  text-align: center;
+  width: var(--content-container-width);
+}
+
+#choiceList {
+  display: flex;
+  flex-direction: column;
+  text-align: start;
+  width: 100%;
+}
+
+.title {
+  font-size: 1.5rem;
+  font-weight: 500;
+  line-height: 32px;
+  margin: 0;
+}
+
+.subtitle {
+  color: var(--cr-secondary-text-color);
+  font-size: 1rem;
+  font-weight: 400;
+  line-height: 24px;
+  margin: 0 0 24px;
+}
+
+.choice {
+  display: flex;
+  flex-direction: row;
+  gap: var(--choice-gap);
+}
+
+.choice-text {
+  --choice-text-width: calc(var(--content-container-width) -
+      2 * var(--radio-button-horizontal-padding) -
+      var(--search-engine-icon-size) - var(--choice-gap) -
+      var(--cr-radio-button-label-spacing) - var(--cr-radio-button-size));
+  display: flex;
+  flex-direction: column;
+  width: var(--choice-text-width);
+}
+
+.choice-icon {
+  background-size: var(--search-engine-icon-size);
+  border-radius: 4px;
+  flex: none;
+  height: var(--search-engine-icon-size);
+  /* So that the icon doesn't change position when the snippet is expanded. */
+  margin-top: calc((var(--choice-height) - var(--search-engine-icon-size))/2);
+  width: var(--search-engine-icon-size);
+}
+
+.choice-title {
+  align-items: center;
+  display: flex;
+  flex-direction: row;
+  font-family: Roboto, Arial;
+  font-size: 0.8125rem;
+  font-weight: 400;
+  line-height: 20px;
+  text-align: start;
+}
+
+cr-radio-button {
+  /* So that the radio button doesn't change position when the snippet is
+  expanded. */
+  --cr-radio-button-disc-margin-block-start: calc((var(--choice-height) -
+      var(--cr-radio-button-size))/2);
+  --cr-radio-button-label-spacing: 16px;
+  --cr-radio-button-size: 20px;
+  align-items: start;
+  background-color: var(--cr-fallback-color-surface2);
+  border-bottom: 1px solid var(--background-color);
+  border-radius: 2px;
+  padding: 8px var(--radio-button-horizontal-padding);
+}
+
+cr-radio-button:first-of-type {
+  border-top-left-radius: 16px;
+  border-top-right-radius: 16px;
+}
+
+cr-radio-button:last-of-type {
+  border-bottom: unset;
+  border-bottom-left-radius: 16px;
+  border-bottom-right-radius: 16px;
+}
+cr-radio-button[checked] {
+  background-color: var(--cr-fallback-color-tonal-container);
+}
+
+/* Using a class instead of relying only on the pseudo-class allows
+disabling this style in screenshot tests, where the inability to
+control the pointer location causes some flakiness. */
+.hoverable:hover:not([checked]) {
+  background-color: var(--cr-hover-on-subtle-background-color);
+}
+
+.product-logo {
+  height: 48px;
+  margin: 64px 0 16px;
+  width: 48px;
+}
+
+#infoLink {
+  color: var(--google-blue-600);
+  white-space: nowrap;
+}
+
+cr-dialog {
+  --cr-dialog-body-padding-horizontal: 24px;
+  --cr-dialog-button-container-padding-horizontal: 24px;
+  --cr-dialog-button-container-padding-bottom: 24px;
+  --cr-dialog-button-container-padding-top: 20px;
+  --cr-dialog-title-font-size: 1.375rem;
+  --cr-dialog-title-slot-padding-bottom: 24px;
+  --cr-dialog-title-slot-padding-end: 24px;
+  --cr-dialog-title-slot-padding-start: 24px;
+  --cr-dialog-title-slot-padding-top: 24px;
+}
+
+cr-dialog div[slot="title"] {
+  font-size: 1.375rem;
+  font-weight: 400;
+  line-height: 28px;
+}
+
+cr-dialog p {
+  font-family: Roboto, Arial;
+  font-size: 0.875rem;
+  font-weight: 400;
+  line-height: 20px;
+  margin: 0 0 24px;
+}
+
+#buttonContainer {
+  bottom: 0;
+  display: flex;
+  justify-content: flex-end;
+  pointer-events: none;
+  position: fixed;
+  width: 100vw;
+}
+
+.cr-icon {
+  --cr-icon-color: currentColor;
+  --cr-icon-image: url(images/arrow_downward.svg);
+  --cr-icon-ripple-size: 18px;
+  --cr-icon-size: 18px;
+  margin: 0;
+}
+
+#choiceList.overlap-mitigation {
+    --button-container-height: calc(var(--cr-button-height) + 2 *
+        var(--action-button-margins));
+    padding-bottom: var(--button-container-height);
+}
+
+#buttonContainer.overlap-mitigation {
+    background-color: var(--background-color);
+    border-top: var(--cr-separator-line);
+    pointer-events: unset;
+  }
+
+.marketing-snippet {
+  color: var(--cr-secondary-text-color);
+  font-size: 0.75rem;
+  font-weight: 400;
+  line-height: var(--marketing-snippet-line-height);
+}
+
+.truncate-text {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.search-engine-name {
+  font-size: 0.875rem;
+  font-weight: 700;
+  line-height: var(--search-engine-name-line-height);
+}
+
+.info-dialog-illustration {
+  content: url(images/info_dialog_illustration.svg);
+  margin-bottom: 24px;
+  width: 100%;
+}
+
+@media (prefers-color-scheme: dark) {
+  :host {
+    --background-color: var(--md-background-color);
+  }
+
+  .info-dialog-illustration {
+    content: url(images/info_dialog_illustration_dark.svg);
+  }
+
+  .tangible-sync-style-left-banner {
+    content: url(images/left_illustration_dark.svg);
+  }
+
+  .tangible-sync-style-right-banner {
+    content: url(images/right_illustration_dark.svg);
+  }
+
+  #infoLink {
+    color: var(--google-blue-300);
+  }
+
+  cr-radio-button {
+    --cr-radio-button-unchecked-color: var(--google-grey-100);
+  }
+}
+
+@media screen and (max-width: 780px) {
+  .tangible-sync-style-left-banner,
+  .tangible-sync-style-right-banner {
+    display: none;
+  }
+}
+
+@media screen and (max-width: 548px) {
+  :host {
+    --content-container-width: 452px;
+  }
+}
diff --git a/chrome/browser/resources/search_engine_choice/app.html b/chrome/browser/resources/search_engine_choice/app.html
index c873f2a..31ae12e 100644
--- a/chrome/browser/resources/search_engine_choice/app.html
+++ b/chrome/browser/resources/search_engine_choice/app.html
@@ -1,274 +1,3 @@
-<style include="cr-icons tangible-sync-style-shared">
-  :host {
-    --background-color: white;
-    --search-engine-icon-size: 24px;
-    --action-button-margins: 24px;
-    --content-container-width: 500px;
-    --radio-button-horizontal-padding: 16px;
-    --choice-gap: 16px;
-    --search-engine-name-line-height: 20px;
-    --marketing-snippet-line-height: 16px;
-    --choice-height: calc(var(--search-engine-name-line-height) +
-        var(--marketing-snippet-line-height));
-    color: var(--cr-primary-text-color);
-  }
-
-  .tangible-sync-style-left-banner {
-    content: url(images/left_illustration.svg);
-  }
-
-  .tangible-sync-style-right-banner {
-    content: url(images/right_illustration.svg);
-  }
-
-  .tangible-sync-style-left-banner,
-  .tangible-sync-style-right-banner {
-    position: fixed;
-  }
-
-  #actionButton {
-    height: unset;
-    margin: var(--action-button-margins);
-    padding: 6px 16px 6px 12px;
-    pointer-events: all;
-    text-align: center;
-  }
-
-  .content-container {
-    align-items: center;
-    display: flex;
-    flex-direction: column;
-    height: 100vh;
-    margin: auto;
-    text-align: center;
-    width: var(--content-container-width);
-  }
-
-  #choiceList {
-    display: flex;
-    flex-direction: column;
-    text-align: start;
-    width: 100%;
-  }
-
-  .title {
-    font-size: 1.5rem;
-    font-weight: 500;
-    line-height: 32px;
-    margin: 0;
-  }
-
-  .subtitle {
-    color: var(--cr-secondary-text-color);
-    font-size: 1rem;
-    font-weight: 400;
-    line-height: 24px;
-    margin: 0 0 24px;
-  }
-
-  .choice {
-    display: flex;
-    flex-direction: row;
-    gap: var(--choice-gap);
-  }
-
-  .choice-text {
-    --choice-text-width: calc(var(--content-container-width) -
-        2 * var(--radio-button-horizontal-padding) -
-        var(--search-engine-icon-size) - var(--choice-gap) -
-        var(--cr-radio-button-label-spacing) - var(--cr-radio-button-size));
-    display: flex;
-    flex-direction: column;
-    width: var(--choice-text-width);
-  }
-
-  .choice-icon {
-    background-size: var(--search-engine-icon-size);
-    border-radius: 4px;
-    flex: none;
-    height: var(--search-engine-icon-size);
-    /* So that the icon doesn't change position when the snippet is expanded. */
-    margin-top: calc((var(--choice-height) - var(--search-engine-icon-size))/2);
-    width: var(--search-engine-icon-size);
-  }
-
-  .choice-title {
-    align-items: center;
-    display: flex;
-    flex-direction: row;
-    font-family: Roboto, Arial;
-    font-size: 0.8125rem;
-    font-weight: 400;
-    line-height: 20px;
-    text-align: start;
-  }
-
-  cr-radio-button {
-    /* So that the radio button doesn't change position when the snippet is
-    expanded. */
-    --cr-radio-button-disc-margin-block-start: calc((var(--choice-height) -
-        var(--cr-radio-button-size))/2);
-    --cr-radio-button-label-spacing: 16px;
-    --cr-radio-button-size: 20px;
-    align-items: start;
-    background-color: var(--cr-fallback-color-surface2);
-    border-bottom: 1px solid var(--background-color);
-    border-radius: 2px;
-    padding: 8px var(--radio-button-horizontal-padding);
-  }
-
-  cr-radio-button:first-of-type {
-    border-top-left-radius: 16px;
-    border-top-right-radius: 16px;
-  }
-
-  cr-radio-button:last-of-type {
-    border-bottom: unset;
-    border-bottom-left-radius: 16px;
-    border-bottom-right-radius: 16px;
-  }
-  cr-radio-button[checked] {
-    background-color: var(--cr-fallback-color-tonal-container);
-  }
-
-  /* Using a class instead of relying only on the pseudo-class allows
-  disabling this style in screenshot tests, where the inability to
-  control the pointer location causes some flakiness. */
-  .hoverable:hover:not([checked]) {
-    background-color: var(--cr-hover-on-subtle-background-color);
-  }
-
-  .product-logo {
-    height: 48px;
-    margin: 64px 0 16px;
-    width: 48px;
-  }
-
-  #infoLink {
-    color: var(--google-blue-600);
-    white-space: nowrap;
-  }
-
-  cr-dialog {
-    --cr-dialog-body-padding-horizontal: 24px;
-    --cr-dialog-button-container-padding-horizontal: 24px;
-    --cr-dialog-button-container-padding-bottom: 24px;
-    --cr-dialog-button-container-padding-top: 20px;
-    --cr-dialog-title-font-size: 1.375rem;
-    --cr-dialog-title-slot-padding-bottom: 24px;
-    --cr-dialog-title-slot-padding-end: 24px;
-    --cr-dialog-title-slot-padding-start: 24px;
-    --cr-dialog-title-slot-padding-top: 24px;
-  }
-
-  cr-dialog div[slot="title"] {
-    font-size: 1.375rem;
-    font-weight: 400;
-    line-height: 28px;
-  }
-
-  cr-dialog p {
-    font-family: Roboto, Arial;
-    font-size: 0.875rem;
-    font-weight: 400;
-    line-height: 20px;
-    margin: 0 0 24px;
-  }
-
-  #buttonContainer {
-    bottom: 0;
-    display: flex;
-    justify-content: flex-end;
-    pointer-events: none;
-    position: fixed;
-    width: 100vw;
-  }
-
-  .cr-icon {
-    --cr-icon-color: currentColor;
-    --cr-icon-image: url(images/arrow_downward.svg);
-    --cr-icon-ripple-size: 18px;
-    --cr-icon-size: 18px;
-    margin: 0;
-  }
-
-  #choiceList.overlap-mitigation {
-      --button-container-height: calc(var(--cr-button-height) + 2 *
-          var(--action-button-margins));
-      padding-bottom: var(--button-container-height);
-  }
-
-  #buttonContainer.overlap-mitigation {
-      background-color: var(--background-color);
-      border-top: var(--cr-separator-line);
-      pointer-events: unset;
-    }
-
-  .marketing-snippet {
-    color: var(--cr-secondary-text-color);
-    font-size: 0.75rem;
-    font-weight: 400;
-    line-height: var(--marketing-snippet-line-height);
-  }
-
-  .truncate-text {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-  }
-
-  .search-engine-name {
-    font-size: 0.875rem;
-    font-weight: 700;
-    line-height: var(--search-engine-name-line-height);
-  }
-
-  .info-dialog-illustration {
-    content: url(images/info_dialog_illustration.svg);
-    margin-bottom: 24px;
-    width: 100%;
-  }
-
-  @media (prefers-color-scheme: dark) {
-    :host {
-      --background-color: var(--md-background-color);
-    }
-
-    .info-dialog-illustration {
-      content: url(images/info_dialog_illustration_dark.svg);
-    }
-
-    .tangible-sync-style-left-banner {
-      content: url(images/left_illustration_dark.svg);
-    }
-
-    .tangible-sync-style-right-banner {
-      content: url(images/right_illustration_dark.svg);
-    }
-
-    #infoLink {
-      color: var(--google-blue-300);
-    }
-
-    cr-radio-button {
-      --cr-radio-button-unchecked-color: var(--google-grey-100);
-    }
-  }
-
-  @media screen and (max-width: 780px) {
-    .tangible-sync-style-left-banner,
-    .tangible-sync-style-right-banner {
-      display: none;
-    }
-  }
-
-  @media screen and (max-width: 548px) {
-    :host {
-      --content-container-width: 452px;
-    }
-  }
-</style>
-
 <img class="tangible-sync-style-left-banner" id="leftBanner" alt="">
 <img class="tangible-sync-style-right-banner" id="rightBanner" alt="">
 <div class="content-container">
@@ -277,42 +6,42 @@
   <h1 class="title">$i18n{title}</h1>
   <p class="subtitle">
     $i18n{subtitle}
-    <a id="infoLink" href="" on-click="onLinkClicked_"
+    <a id="infoLink" href="" @click="${this.onLinkClicked_}"
         aria-label="$i18n{subtitleInfoLinkA11yLabel}">
       $i18n{subtitleInfoLink}
     </a>
   </p>
   <cr-radio-group id="choiceList"
-      selected="{{selectedChoice_}}" aria-label="$i18n{choiceListA11yLabel}"
-      role="list">
-    <template is="dom-repeat" items="[[choiceList_]]">
-        <cr-radio-button aria-label$="[[item.name]]" role="listitem"
-            class="label-first hoverable"
-            name="[[item.prepopulateId]]">
-          <div class="choice">
-            <div class="choice-icon"
-                style$="background-image: [[item.iconPath]];"></div>
-            <div class="choice-text">
-              <div class="search-engine-name">
-                [[item.name]]
-              </div>
-              <div class$="marketing-snippet
-                  [[getMarketingSnippetClass_(item, snippetDisplayed_)]]">
-                [[item.marketingSnippet]]
-              </div>
+      selected="${this.selectedChoice_}"
+      @selected-changed="${this.onSelectedChoiceChangedByUser_}"
+      aria-label="$i18n{choiceListA11yLabel}" role="list">
+    ${this.choiceList_.map(item => html`
+      <cr-radio-button aria-label="${item.name}" role="listitem"
+          class="label-first hoverable"
+          name="${item.prepopulateId}">
+        <div class="choice">
+          <div class="choice-icon"
+              .style="background-image: ${item.iconPath};"></div>
+          <div class="choice-text">
+            <div class="search-engine-name">${item.name}</div>
+            <div class="marketing-snippet
+                ${this.getMarketingSnippetClass_(item)}">
+              ${item.marketingSnippet}
             </div>
           </div>
-        </cr-radio-button>
-    </template>
+        </div>
+      </cr-radio-button>
+    `)}
   </cr-radio-group>
 </div>
 <div id="buttonContainer">
   <cr-button class="action-button" id="actionButton"
-      on-click="onActionButtonClicked_" disabled="[[isActionButtonDisabled_]]">
-      <div class="cr-icon" slot="prefix-icon" title=""
-          hidden="[[hasUserScrolledToTheBottom_]]">
-      </div>
-      [[actionButtonText_]]
+      @click="${this.onActionButtonClicked_}"
+      ?disabled="${this.isActionButtonDisabled_}">
+    <div class="cr-icon" slot="prefix-icon" title=""
+        ?hidden="${this.hasUserScrolledToTheBottom_}">
+    </div>
+    ${this.actionButtonText_}
   </cr-button>
 </div>
 
@@ -328,7 +57,7 @@
   </div>
   <div slot="button-container">
     <cr-button class="action-button" id="infoDialogButton"
-        on-click="onInfoDialogButtonClicked_">
+        @click="${this.onInfoDialogButtonClicked_}">
       $i18n{infoDialogButtonText}
     </cr-button>
   </div>
diff --git a/chrome/browser/resources/search_engine_choice/app.ts b/chrome/browser/resources/search_engine_choice/app.ts
index 87d6a3a91..e0cdadff 100644
--- a/chrome/browser/resources/search_engine_choice/app.ts
+++ b/chrome/browser/resources/search_engine_choice/app.ts
@@ -2,30 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://search-engine-choice/tangible_sync_style_shared.css.js';
 import 'chrome://resources/cr_components/localized_link/localized_link.js';
-import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js';
 import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
-import 'chrome://resources/cr_elements/cr_icons.css.js';
 import './strings.m.js';
 
 import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {I18nMixinLit} from 'chrome://resources/cr_elements/i18n_mixin_lit.js';
 import {getFaviconForPageURL} from 'chrome://resources/js/icon.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
+import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
-import {getTemplate} from './app.html.js';
+import {getCss} from './app.css.js';
+import {getHtml} from './app.html.js';
 import type {SearchEngineChoice} from './browser_proxy.js';
 import {SearchEngineChoiceBrowserProxy} from './browser_proxy.js';
 import {PageHandler_ScrollState} from './search_engine_choice.mojom-webui.js';
 import type {PageHandlerRemote} from './search_engine_choice.mojom-webui.js';
 
-export interface SearchEngineChoiceAppElement {
+export interface AppElement {
   $: {
     infoDialog: CrDialogElement,
     actionButton: CrButtonElement,
@@ -35,72 +34,50 @@
   };
 }
 
-const SearchEngineChoiceAppElementBase = I18nMixin(PolymerElement);
+const AppElementBase = I18nMixinLit(CrLitElement);
 
-export class SearchEngineChoiceAppElement extends
-    SearchEngineChoiceAppElementBase {
+export class AppElement extends AppElementBase {
   static get is() {
     return 'search-engine-choice-app';
   }
 
-  static get template() {
-    return getTemplate();
+  static override get styles() {
+    return getCss();
   }
 
-  static get properties() {
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
     return {
       /**
        * The choice list is passed as JSON because it doesn't change
        * dynamically, so it would be better to have it available as loadtime
        * data.
        */
-      choiceList_: {
-        type: Array,
-        value() {
-          return JSON.parse(loadTimeData.getString('choiceList'));
-        },
-      },
+      choiceList_: {type: Array},
 
       // The choice will always be > 0 when selected for prepopulated engines
       // and == 0 for a custom search engine.
-      selectedChoice_: {
-        type: Number,
-        value: -1,
-        observer: 'onSelectedChoiceChanged_',
-      },
+      selectedChoice_: {type: Number},
 
-      isActionButtonDisabled_: {
-        type: Boolean,
-        computed: 'computeActionButtonDisabled_(selectedChoice_, ' +
-            'hasUserScrolledToTheBottom_)',
-      },
-
-      actionButtonText_: {
-        type: String,
-        computed: 'getActionButtonText_(hasUserScrolledToTheBottom_)',
-      },
-
-      hasUserScrolledToTheBottom_: {
-        type: Boolean,
-        value: false,
-      },
-
-      snippetDisplayed_: Boolean,
+      isActionButtonDisabled_: {type: Boolean},
+      actionButtonText_: {type: String},
+      hasUserScrolledToTheBottom_: {type: Boolean},
     };
   }
 
-  private choiceList_: SearchEngineChoice[];
-  private selectedChoice_: string;
-  private pageHandler_: PageHandlerRemote;
-  private hasUserScrolledToTheBottom_: boolean;
-  private actionButtonText_: string;
-  private snippetDisplayed_: boolean;
-  private resizeObserver_: ResizeObserver|null = null;
+  protected choiceList_: SearchEngineChoice[] =
+      JSON.parse(loadTimeData.getString('choiceList'));
+  protected selectedChoice_: number = -1;
+  protected isActionButtonDisabled_: boolean = false;
+  protected hasUserScrolledToTheBottom_: boolean = false;
+  protected actionButtonText_: string = '';
 
-  constructor() {
-    super();
-    this.pageHandler_ = SearchEngineChoiceBrowserProxy.getInstance().handler;
-  }
+  private resizeObserver_: ResizeObserver|null = null;
+  private pageHandler_: PageHandlerRemote =
+      SearchEngineChoiceBrowserProxy.getInstance().handler;
 
   override connectedCallback() {
     super.connectedCallback();
@@ -114,16 +91,17 @@
         // Fetch the favicon from the Favicon Service for custom search
         // engines.
         searchEngine.iconPath =
-            getFaviconForPageURL(searchEngine?.url!, false, '', 24);
+            getFaviconForPageURL(searchEngine.url!, false, '', 24);
       } else {
         searchEngine.iconPath = 'image-set(url(' + searchEngine.iconPath +
             ') 1x, url(' + searchEngine.iconPath + '@2x) 2x)';
       }
     });
+    this.requestUpdate();
 
     this.addResizeObserver_();
 
-    afterNextRender(this, () => {
+    this.updateComplete.then(() => {
       const isPageScrollable =
           document.body.scrollHeight > document.body.clientHeight;
 
@@ -145,6 +123,34 @@
     this.resizeObserver_!.disconnect();
   }
 
+  override willUpdate(changedProperties: PropertyValues<this>) {
+    super.willUpdate(changedProperties);
+
+    const changedPrivateProperties =
+        changedProperties as Map<PropertyKey, unknown>;
+
+    if (changedPrivateProperties.has('selectedChoice_')) {
+      this.onSelectedChoiceChanged_(
+          this.selectedChoice_,
+          changedPrivateProperties.get('selectedChoice_') as number |
+              undefined);
+    }
+
+    if (changedPrivateProperties.has('hasUserScrolledToTheBottom_')) {
+      this.actionButtonText_ = this.i18n(
+          this.hasUserScrolledToTheBottom_ ? 'submitButtonText' :
+                                             'moreButtonText');
+    }
+
+    if (changedPrivateProperties.has('hasUserScrolledToTheBottom_') ||
+        changedPrivateProperties.has('selectedChoice_')) {
+      // The action button will be disabled if the user scrolls to the bottom of
+      // the list without making a search engine choice.
+      this.isActionButtonDisabled_ =
+          this.hasUserScrolledToTheBottom_ && this.selectedChoice_ === -1;
+    }
+  }
+
   private addResizeObserver_() {
     function buttonAndListOverlap(
         buttonRect: DOMRect, listRect: DOMRect, offset: number): boolean {
@@ -178,25 +184,14 @@
     this.resizeObserver_.observe(document.body);
   }
 
-  private onLinkClicked_() {
+  protected onLinkClicked_() {
     this.$.infoDialog.showModal();
     this.pageHandler_.handleLearnMoreLinkClicked();
   }
 
-  private needsUserChoice_() {
-    return parseInt(this.selectedChoice_) === -1;
-  }
-
-  // The action button will be disabled if the user scrolls to the bottom of
-  // the list without making a search engine choice.
-  private computeActionButtonDisabled_() {
-    return this.hasUserScrolledToTheBottom_ && this.needsUserChoice_();
-  }
-
-  private onActionButtonClicked_() {
+  protected onActionButtonClicked_() {
     if (this.hasUserScrolledToTheBottom_) {
-      this.pageHandler_.handleSearchEngineChoiceSelected(
-          parseInt(this.selectedChoice_));
+      this.pageHandler_.handleSearchEngineChoiceSelected(this.selectedChoice_);
       return;
     }
 
@@ -219,11 +214,11 @@
     window.removeEventListener('scrollend', this.onPageScrollEnd_.bind(this));
   }
 
-  private getMarketingSnippetClass_(item: SearchEngineChoice) {
+  protected getMarketingSnippetClass_(item: SearchEngineChoice) {
     return item.showMarketingSnippet ? '' : 'truncate-text';
   }
 
-  private onInfoDialogButtonClicked_() {
+  protected onInfoDialogButtonClicked_() {
     this.$.infoDialog.close();
   }
 
@@ -236,7 +231,7 @@
     const choice =
         this.choiceList_.find(elem => elem.prepopulateId === prepopulatedId)!;
     choice.showMarketingSnippet = false;
-    this.snippetDisplayed_ = false;
+    this.requestUpdate();
   }
 
   private showSearchEngineSnippet_(prepopulateId: number) {
@@ -249,19 +244,23 @@
         this.choiceList_.find(elem => elem.prepopulateId === prepopulateId)!;
 
     choice.showMarketingSnippet = true;
-    this.snippetDisplayed_ = true;
+    this.requestUpdate();
   }
 
-  private onSelectedChoiceChanged_(
-      newPrepopulatedId: string, oldPrepopulatedId: string) {
+  protected onSelectedChoiceChangedByUser_(e: CustomEvent<{value: string}>) {
+    this.selectedChoice_ = Number.parseInt(e.detail.value);
+  }
+
+  protected onSelectedChoiceChanged_(
+      newPrepopulatedId: number, oldPrepopulatedId: number|undefined) {
     // No search engine selected.
-    if (parseInt(newPrepopulatedId) === -1) {
+    if (newPrepopulatedId === -1) {
       return;
     }
 
     chrome.metricsPrivate.recordUserAction('ExpandSearchEngineDescription');
-    this.resetSnippetState_(parseInt(oldPrepopulatedId));
-    this.showSearchEngineSnippet_(parseInt(newPrepopulatedId));
+    this.resetSnippetState_(oldPrepopulatedId as number);
+    this.showSearchEngineSnippet_(newPrepopulatedId);
   }
 
   private onPageResize_() {
@@ -288,19 +287,12 @@
     this.pageHandler_.recordScrollState(this.getScrollState_());
     this.handleContentScrollStateUpdate_(/*forceFullyDisplayed=*/ true);
   }
-
-  private getActionButtonText_() {
-    return this.i18n(
-        this.hasUserScrolledToTheBottom_ ? 'submitButtonText' :
-                                           'moreButtonText');
-  }
 }
 
 declare global {
   interface HTMLElementTagNameMap {
-    'search-engine-choice-app': SearchEngineChoiceAppElement;
+    'search-engine-choice-app': AppElement;
   }
 }
 
-customElements.define(
-    SearchEngineChoiceAppElement.is, SearchEngineChoiceAppElement);
+customElements.define(AppElement.is, AppElement);
diff --git a/chrome/browser/resources/settings/site_settings/constants.ts b/chrome/browser/resources/settings/site_settings/constants.ts
index fd455ce..6eb140d 100644
--- a/chrome/browser/resources/settings/site_settings/constants.ts
+++ b/chrome/browser/resources/settings/site_settings/constants.ts
@@ -104,6 +104,7 @@
   OFF = 0,
   BLOCK_THIRD_PARTY = 1,
   INCOGNITO_ONLY = 2,
+  LIMITED = 3,
 }
 
 /**
diff --git a/chrome/browser/resources/signin/BUILD.gn b/chrome/browser/resources/signin/BUILD.gn
index e7bcb88..ad4ecc1 100644
--- a/chrome/browser/resources/signin/BUILD.gn
+++ b/chrome/browser/resources/signin/BUILD.gn
@@ -99,7 +99,6 @@
 
   css_files = [
     "signin_shared.css",
-    "signin_shared_lit.css",
     "signin_vars.css",
     "sync_confirmation/sync_confirmation_app.css",
     "sync_confirmation/sync_disabled_confirmation_app.css",
@@ -139,8 +138,8 @@
   ]
 
   ts_path_mappings = [
-    "/signin_shared_lit.css.js|" +
-        rebase_path("$target_gen_dir/preprocessed/signin_shared_lit.css.ts",
+    "/signin_shared.css.js|" +
+        rebase_path("$target_gen_dir/preprocessed/signin_shared.css.ts",
                     target_gen_dir),
     "/tangible_sync_style_shared_lit.css.js|" + rebase_path(
             "$target_gen_dir/preprocessed/tangible_sync_style_shared_lit.css.ts",
diff --git a/chrome/browser/resources/signin/dice_web_signin_intercept/chrome_signin/chrome_signin_app.css b/chrome/browser/resources/signin/dice_web_signin_intercept/chrome_signin/chrome_signin_app.css
index d083048..9be3c6c 100644
--- a/chrome/browser/resources/signin/dice_web_signin_intercept/chrome_signin/chrome_signin_app.css
+++ b/chrome/browser/resources/signin/dice_web_signin_intercept/chrome_signin/chrome_signin_app.css
@@ -4,10 +4,10 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  # #import=/signin_vars.css.js
  * #scheme=relative
- * #include=signin-shared-lit
+ * #include=signin-shared
  * #css_wrapper_metadata_end */
 
 #interceptDialog {
diff --git a/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.css b/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.css
index 033640f..c1c2dc6 100644
--- a/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.css
+++ b/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.css
@@ -4,10 +4,10 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  # #import=/signin_vars.css.js
  * #scheme=relative
- * #include=signin-shared-lit
+ * #include=signin-shared
  * #css_wrapper_metadata_end */
 
 #interceptDialog {
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.css b/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.css
index 5933ae0..ebfe46d 100644
--- a/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.css
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.css
@@ -5,11 +5,11 @@
 /* #css_wrapper_metadata_start
  * #type=style-lit
  * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  * #import=/signin_vars.css.js
  * #import=/tangible_sync_style_shared_lit.css.js
  * #scheme=relative
- * #include=signin-shared-lit tangible-sync-style-shared-lit
+ * #include=signin-shared tangible-sync-style-shared-lit
  * #css_wrapper_metadata_end */
 
 :host {
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.css b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.css
index ecb8e9f..30682d2 100644
--- a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.css
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.css
@@ -5,11 +5,11 @@
 /* #css_wrapper_metadata_start
  * #type=style-lit
  * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  * #import=/signin_vars.css.js
  * #import=/tangible_sync_style_shared_lit.css.js
  * #scheme=relative
- * #include=signin-shared-lit tangible-sync-style-shared-lit
+ * #include=signin-shared tangible-sync-style-shared-lit
  * #css_wrapper_metadata_end */
 
 :host([use-updated-ui_]) {
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_disclosure.css b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_disclosure.css
index b05605e3..cca723e5 100644
--- a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_disclosure.css
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_disclosure.css
@@ -4,11 +4,11 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  * #import=/signin_vars.css.js
  * #import=/tangible_sync_style_shared_lit.css.js
  * #scheme=relative
- * #include=signin-shared-lit tangible-sync-style-shared-lit
+ * #include=signin-shared tangible-sync-style-shared-lit
  * #css_wrapper_metadata_end */
 
 :host() {
diff --git a/chrome/browser/resources/signin/profile_customization/profile_customization_app.css b/chrome/browser/resources/signin/profile_customization/profile_customization_app.css
index b0e7848..89e663e 100644
--- a/chrome/browser/resources/signin/profile_customization/profile_customization_app.css
+++ b/chrome/browser/resources/signin/profile_customization/profile_customization_app.css
@@ -4,11 +4,11 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  * #import=/signin_vars.css.js
  * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
  * #scheme=relative
- * #include=signin-shared-lit
+ * #include=signin-shared
  * #css_wrapper_metadata_end */
 
 #viewManager,
diff --git a/chrome/browser/resources/signin/signin_email_confirmation/signin_email_confirmation_app.css b/chrome/browser/resources/signin/signin_email_confirmation/signin_email_confirmation_app.css
index 96b4dea..108916e 100644
--- a/chrome/browser/resources/signin/signin_email_confirmation/signin_email_confirmation_app.css
+++ b/chrome/browser/resources/signin/signin_email_confirmation/signin_email_confirmation_app.css
@@ -4,10 +4,10 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
  * #scheme=relative
- * #include=signin-shared-lit
+ * #include=signin-shared
  * #css_wrapper_metadata_end */
 
 .container {
diff --git a/chrome/browser/resources/signin/signin_error/signin_error_app.css b/chrome/browser/resources/signin/signin_error/signin_error_app.css
index 68bce30..cde9fe5 100644
--- a/chrome/browser/resources/signin/signin_error/signin_error_app.css
+++ b/chrome/browser/resources/signin/signin_error/signin_error_app.css
@@ -5,9 +5,9 @@
 /* #css_wrapper_metadata_start
  * #type=style-lit
  * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  * #scheme=relative
- * #include=signin-shared-lit
+ * #include=signin-shared
  * #css_wrapper_metadata_end */
 
 .details {
diff --git a/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.css b/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.css
index cfe5c05..db05c11 100644
--- a/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.css
+++ b/chrome/browser/resources/signin/signin_reauth/signin_reauth_app.css
@@ -5,9 +5,9 @@
 /* #css_wrapper_metadata_start
  * #type=style-lit
  * #import=chrome://resources/cr_elements/cr_hidden_style_lit.css.js
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  * #scheme=relative
- * #include=signin-shared-lit cr-hidden-style-lit
+ * #include=signin-shared cr-hidden-style-lit
  * #css_wrapper_metadata_end */
 
 :host {
diff --git a/chrome/browser/resources/signin/signin_shared.css b/chrome/browser/resources/signin/signin_shared.css
index da3e853..cd7391e 100644
--- a/chrome/browser/resources/signin/signin_shared.css
+++ b/chrome/browser/resources/signin/signin_shared.css
@@ -3,7 +3,7 @@
  * found in the LICENSE file. */
 
 /* #css_wrapper_metadata_start
- * #type=style
+ * #type=style-lit
  * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
  * #import=./signin_vars.css.js
  * #css_wrapper_metadata_end */
diff --git a/chrome/browser/resources/signin/signin_shared_lit.css b/chrome/browser/resources/signin/signin_shared_lit.css
deleted file mode 100644
index 261c6c95..0000000
--- a/chrome/browser/resources/signin/signin_shared_lit.css
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright 2024 The Chromium Authors
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-/* #css_wrapper_metadata_start
- * #type=style-lit
- * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
- * #import=./signin_vars.css.js
- * #css_wrapper_metadata_end */
-
-/* Purposefully empty since this style is generated at build time from the
- * equivalent Polymer version. */
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.css b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.css
index 31cc2550..25f2864 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.css
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.css
@@ -4,11 +4,11 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  * #import=./signin_vars.css.js
  * #import=/tangible_sync_style_shared_lit.css.js
  * #scheme=relative
- * #include=signin-shared-lit tangible-sync-style-shared-lit
+ * #include=signin-shared tangible-sync-style-shared-lit
  * #css_wrapper_metadata_end */
 
 /* TODO look at UI with inspector to see which lines are overridden by the
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_disabled_confirmation_app.css b/chrome/browser/resources/signin/sync_confirmation/sync_disabled_confirmation_app.css
index 7aede05..428ae90 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_disabled_confirmation_app.css
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_disabled_confirmation_app.css
@@ -4,9 +4,9 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
- * #import=/signin_shared_lit.css.js
+ * #import=/signin_shared.css.js
  * #scheme=relative
- * #include=signin-shared-lit
+ * #include=signin-shared
  * #css_wrapper_metadata_end */
 
 .container {
diff --git a/chrome/browser/resources/tab_search/declutter/declutter_page.html.ts b/chrome/browser/resources/tab_search/declutter/declutter_page.html.ts
index 908df78..c356dc5 100644
--- a/chrome/browser/resources/tab_search/declutter/declutter_page.html.ts
+++ b/chrome/browser/resources/tab_search/declutter/declutter_page.html.ts
@@ -8,6 +8,11 @@
 
 export function getHtml(this: DeclutterPageElement) {
   return html`<!--_html_template_start_-->
+  <div id="header">
+    <cr-icon-button iron-icon="cr:arrow-back" @click="${this.onBackClick_}">
+    </cr-icon-button>
+    $i18n{declutterTitle}
+  </div>
   <div id="tabList">
     ${this.staleTabDatas_.map((item, index) => html`
         <tab-search-item class="mwb-list-item" .data="${item}"
diff --git a/chrome/browser/resources/tab_search/declutter/declutter_page.ts b/chrome/browser/resources/tab_search/declutter/declutter_page.ts
index cb41bb10..58cc0875 100644
--- a/chrome/browser/resources/tab_search/declutter/declutter_page.ts
+++ b/chrome/browser/resources/tab_search/declutter/declutter_page.ts
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import 'chrome://resources/cr_elements/icons_lit.html.js';
 import '../tab_search_item.js';
 
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
@@ -56,6 +58,10 @@
         id => this.apiProxy_.getCallbackRouter().removeListener(id));
   }
 
+  protected onBackClick_() {
+    this.fire('back-click');
+  }
+
   protected onCloseTabsClick_() {
     const tabIds = this.staleTabDatas_.map((tabData) => tabData.tab.tabId);
     this.apiProxy_.declutterTabs(tabIds);
diff --git a/chrome/browser/resources/tab_search/tab_organization_selector.html.ts b/chrome/browser/resources/tab_search/tab_organization_selector.html.ts
index a10c302..0c87505 100644
--- a/chrome/browser/resources/tab_search/tab_organization_selector.html.ts
+++ b/chrome/browser/resources/tab_search/tab_organization_selector.html.ts
@@ -35,7 +35,7 @@
 </div>
 
 <div ?hidden=${this.selectedState_ !== OrganizationFeature.DECLUTTER}>
-  <declutter-page></declutter-page>
+  <declutter-page @back-click="${this.onBackClick_}"></declutter-page>
 </div>
 <!--_html_template_end_-->`;
   // clang-format on
diff --git a/chrome/browser/resources/tab_search/tab_organization_selector.ts b/chrome/browser/resources/tab_search/tab_organization_selector.ts
index 699f804..a8c061b 100644
--- a/chrome/browser/resources/tab_search/tab_organization_selector.ts
+++ b/chrome/browser/resources/tab_search/tab_organization_selector.ts
@@ -48,6 +48,10 @@
   protected onDeclutterClick_(): void {
     this.selectedState_ = OrganizationFeature.DECLUTTER;
   }
+
+  protected onBackClick_(): void {
+    this.selectedState_ = OrganizationFeature.NONE;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubMetricUtils.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubMetricUtils.java
index ae45b1c..50eab9c 100644
--- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubMetricUtils.java
+++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubMetricUtils.java
@@ -24,7 +24,7 @@
 
     @VisibleForTesting
     public static final String PERMISSIONS_INTERACTIONS_HISTOGRAM_NAME =
-            "Settings.SafetyCheck.UnusedSitePermissionsModule.Interactions";
+            "Settings.SafetyCheck.UnusedSitePermissionsModuleInteractions";
 
     @VisibleForTesting
     public static final String NOTIFICATIONS_INTERACTIONS_HISTOGRAM_NAME =
@@ -35,8 +35,7 @@
             "Settings.SafetyHub.Dashboard.Interactions";
 
     @VisibleForTesting
-    public static final String MODULE_STATE_HISTOGRAM_NAME =
-            "Settings.SafetyHub.Dashboard.Interactions";
+    public static final String MODULE_STATE_HISTOGRAM_NAME = "Settings.SafetyHub";
 
     /**
      * Interactions on surfaces outside of the Safety Hub settings pages. These can be in the Magic
diff --git a/chrome/browser/safety_hub/android/unused_site_permissions_bridge_unittest.cc b/chrome/browser/safety_hub/android/unused_site_permissions_bridge_unittest.cc
index d83a8be..61573661 100644
--- a/chrome/browser/safety_hub/android/unused_site_permissions_bridge_unittest.cc
+++ b/chrome/browser/safety_hub/android/unused_site_permissions_bridge_unittest.cc
@@ -43,7 +43,8 @@
   void AddRevokedPermissions() {
     base::Value::List revoked_permissions_list;
     for (ContentSettingsType type : kUnusedPermissionList) {
-      revoked_permissions_list.Append(static_cast<int32_t>(type));
+      revoked_permissions_list.Append(
+          UnusedSitePermissionsService::ConvertContentSettingsTypeToKey(type));
     }
     auto dict = base::Value::Dict().Set(permissions::kRevokedKey,
                                         revoked_permissions_list.Clone());
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/EditorScreenshotTask.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/EditorScreenshotTask.java
index ab6a99d..a2714555 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/EditorScreenshotTask.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/EditorScreenshotTask.java
@@ -28,7 +28,7 @@
  * this temporary class and instead move
  * chrome/android/java/src/org/chromium/chrome/browser/feedback/ScreenshotTask.java.
  */
-@JNINamespace("chrome::android")
+@JNINamespace("android")
 public final class EditorScreenshotTask implements EditorScreenshotSource {
     private final Activity mActivity;
     private final BottomSheetController mBottomSheetController;
diff --git a/chrome/browser/share/editor_screenshot_task.cc b/chrome/browser/share/editor_screenshot_task.cc
index 76de08cf..ef407fa 100644
--- a/chrome/browser/share/editor_screenshot_task.cc
+++ b/chrome/browser/share/editor_screenshot_task.cc
@@ -22,7 +22,6 @@
 using jni_zero::AttachCurrentThread;
 using ui::WindowAndroid;
 
-namespace chrome {
 namespace android {
 
 /**
@@ -59,4 +58,3 @@
 }
 
 }  // namespace android
-}  // namespace chrome
diff --git a/chrome/browser/smart_card/smart_card_permission_context.cc b/chrome/browser/smart_card/smart_card_permission_context.cc
index 0cf14db..fff767e 100644
--- a/chrome/browser/smart_card/smart_card_permission_context.cc
+++ b/chrome/browser/smart_card/smart_card_permission_context.cc
@@ -55,11 +55,11 @@
  public:
   explicit PowerSuspendObserver(SmartCardPermissionContext& permission_context)
       : permission_context_(permission_context) {
-    base::PowerMonitor::AddPowerSuspendObserver(this);
+    base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
   }
 
   ~PowerSuspendObserver() override {
-    base::PowerMonitor::RemovePowerSuspendObserver(this);
+    base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
   }
 
   void OnSuspend() override {
diff --git a/chrome/browser/ssl/ssl_client_certificate_selector.h b/chrome/browser/ssl/ssl_client_certificate_selector.h
index f1b838f..584a981 100644
--- a/chrome/browser/ssl/ssl_client_certificate_selector.h
+++ b/chrome/browser/ssl/ssl_client_certificate_selector.h
@@ -20,8 +20,6 @@
 class SSLCertRequestInfo;
 }
 
-namespace chrome {
-
 // Opens a constrained SSL client certificate selection dialog under |parent|,
 // offering certificates in |client_certs| for the host specified by
 // |cert_request_info|. When the user has made a selection, the dialog will
@@ -48,6 +46,4 @@
 void SetShowSSLClientCertificateSelectorHookForTest(
     ShowSSLClientCertificateSelectorTestingHook hook);
 
-}  // namespace chrome
-
 #endif  // CHROME_BROWSER_SSL_SSL_CLIENT_CERTIFICATE_SELECTOR_H_
diff --git a/chrome/browser/storage_access_api/storage_access_api_service_impl_unittest.cc b/chrome/browser/storage_access_api/storage_access_api_service_impl_unittest.cc
index 5111e3ca..af1e12c48 100644
--- a/chrome/browser/storage_access_api/storage_access_api_service_impl_unittest.cc
+++ b/chrome/browser/storage_access_api/storage_access_api_service_impl_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_constraints.h"
+#include "components/safe_browsing/core/common/features.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -33,6 +34,11 @@
   StorageAccessAPIServiceImplTest() = default;
 
   void SetUp() override {
+    // TODO(crbug.com/362466866): Instead of disabling the
+    // `kSafetyHubAbusiveNotificationRevocation` feature, find a stable
+    // fix such that the tests still pass when the feature is enabled.
+    features_.InitWithFeatures(
+        {}, {safe_browsing::kSafetyHubAbusiveNotificationRevocation});
     profile_manager_ = std::make_unique<TestingProfileManager>(
         TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
@@ -55,6 +61,7 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   std::unique_ptr<TestingProfileManager> profile_manager_;
   raw_ptr<Profile> profile_;
+  base::test::ScopedFeatureList features_;
 };
 
 TEST_F(StorageAccessAPIServiceImplTest, RenewPermissionGrant) {
diff --git a/chrome/browser/storage_access_api/storage_access_header_service_factory_unittest.cc b/chrome/browser/storage_access_api/storage_access_header_service_factory_unittest.cc
index 2602b75..490cb94 100644
--- a/chrome/browser/storage_access_api/storage_access_header_service_factory_unittest.cc
+++ b/chrome/browser/storage_access_api/storage_access_header_service_factory_unittest.cc
@@ -10,6 +10,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/safe_browsing/core/common/features.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_task_environment.h"
 #include "net/base/features.h"
@@ -22,9 +23,13 @@
   StorageAccessHeaderServiceFactoryTest() = default;
 
   void SetUp() override {
-    features_.InitWithFeatures({::features::kPersistentOriginTrials,
-                                net::features::kStorageAccessHeadersTrial},
-                               {});
+    // TODO(crbug.com/362466866): Instead of disabling the
+    // `kSafetyHubAbusiveNotificationRevocation` feature, find a stable
+    // fix such that the tests still pass when the feature is enabled.
+    features_.InitWithFeatures(
+        {::features::kPersistentOriginTrials,
+         net::features::kStorageAccessHeadersTrial},
+        {safe_browsing::kSafetyHubAbusiveNotificationRevocation});
     profile_ = std::make_unique<TestingProfile>();
   }
 
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter_browsertest.cc b/chrome/browser/supervised_user/supervised_user_url_filter_browsertest.cc
index 1e78ee5..9733f0c 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_url_filter_browsertest.cc
@@ -658,8 +658,7 @@
               OnURLChecked,
               (const GURL& url,
                supervised_user::FilteringBehavior behavior,
-               supervised_user::FilteringBehaviorReason reason,
-               bool uncertain),
+               supervised_user::FilteringBehaviorDetails details),
               (override));
 
  private:
diff --git a/chrome/browser/tab_resumption/java/res/layout/tab_resumption_module_multi_tile_layout.xml b/chrome/browser/tab_resumption/java/res/layout/tab_resumption_module_multi_tile_layout.xml
index 43ff9de..501b56f6 100644
--- a/chrome/browser/tab_resumption/java/res/layout/tab_resumption_module_multi_tile_layout.xml
+++ b/chrome/browser/tab_resumption/java/res/layout/tab_resumption_module_multi_tile_layout.xml
@@ -47,7 +47,7 @@
             android:textAppearance="@style/TextAppearance.TextMedium.Primary"/>
 
         <TextView
-            android:id="@+id/tile_info_text"
+            android:id="@+id/tile_post_info_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="3dp"
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/SuggestionEntry.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/SuggestionEntry.java
index d7324bf0..1c057ed 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/SuggestionEntry.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/SuggestionEntry.java
@@ -19,13 +19,13 @@
     public final GURL url;
     public final String title;
     public final long lastActiveTime;
-    public final boolean needMatchLocalTab;
 
     @Nullable public final String appId;
     @Nullable public String reasonToShowTab;
     @Nullable public TrainingInfo trainingInfo;
 
     private int mLocalTabId;
+    private boolean mNeedMatchLocalTab;
 
     /**
      * @param type Type of the entry, one of the enum {@link SuggestionEntryType}.
@@ -50,7 +50,7 @@
             @Nullable String reasonToShowTab,
             boolean needMatchLocalTab) {
         this.type = type;
-        this.needMatchLocalTab = needMatchLocalTab;
+        mNeedMatchLocalTab = needMatchLocalTab;
         this.sourceName = sourceName;
         this.url = url;
         this.title = title;
@@ -138,4 +138,14 @@
         assert mLocalTabId == Tab.INVALID_TAB_ID;
         mLocalTabId = tabId;
     }
+
+    /** Gets whether need to match a local Tab for this SuggestionEntry. */
+    public boolean getNeedMatchLocalTab() {
+        return mNeedMatchLocalTab;
+    }
+
+    /** Reset the mNeedMatchLocalTab. */
+    public void resetNeedMatchLocalTab() {
+        mNeedMatchLocalTab = false;
+    }
 }
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMediator.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMediator.java
index 2f1baba..0417536 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMediator.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMediator.java
@@ -12,6 +12,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import org.chromium.base.Callback;
+import org.chromium.base.CallbackController;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.task.PostTask;
@@ -102,6 +104,7 @@
         private boolean mIsStable;
         private SuggestionBundle mBundle;
         private @ResultStrength int mStrength;
+        private final CallbackController mCallbackController;
 
         /**
          * @param dataProvider TabResumptionDataProvider instance owned by this class.
@@ -128,6 +131,7 @@
                         }
                     };
 
+            mCallbackController = new CallbackController();
             mModel.set(
                     TabResumptionModuleProperties.TAB_OBSERVER_CALLBACK,
                     (tab) -> mLocalTabClosureObserver.add(tab));
@@ -140,6 +144,7 @@
                 recordSeenActionForEntries(mBundle.entries);
             }
 
+            mCallbackController.destroy();
             mLocalTabClosureObserver.destroy();
             mDataProvider.setStatusChangedCallback(null);
             mDataProvider.destroy();
@@ -202,6 +207,7 @@
                 TabResumptionModuleMetricsUtils.recordModuleNotShownReason(
                         ModuleNotShownReason.NO_SUGGESTIONS);
             } else {
+                assert isModuleShowConfigFinalized(moduleShowConfig);
                 TabResumptionModuleMetricsUtils.recordModuleShowConfig(moduleShowConfig.intValue());
             }
         }
@@ -298,9 +304,12 @@
             // This directly changes `mShowHideHelper` results.
             mModuleShowConfig = TabResumptionModuleMetricsUtils.computeModuleShowConfig(mBundle);
 
+            @Nullable Callback<Integer> callback = createOnModuleShowConfigFinalizedCallback();
+            boolean shouldLogMetrics = callback == null;
+
             mStrength = result.strength;
             if (mStrength == ResultStrength.TENTATIVE) {
-                setPropertiesAndTriggerRender(mBundle);
+                setPropertiesAndTriggerRender(mBundle, callback);
                 // On first call, start timeout to transition to STABLE and log.
                 if (mHandler == null) {
                     mHandler = new Handler();
@@ -308,8 +317,10 @@
                             () -> {
                                 // Activates if the only strength seen is TENTATIVE. In this case,
                                 // TENTATIVE suggestions is considered stable.
-                                ensureStabilityAndLogMetrics(
-                                        /* recordStabilityDelay= */ false, mModuleShowConfig);
+                                if (shouldLogMetrics) {
+                                    ensureStabilityAndLogMetrics(
+                                            /* recordStabilityDelay= */ false, mModuleShowConfig);
+                                }
                                 // Multiple TENTATIVE suggestions might have repeated attempts to
                                 //  show / hide the module. Finalize if needed.
                                 mShowHideHelper.maybeNotifyModuleDelegate();
@@ -323,17 +334,22 @@
 
             } else if (mStrength == ResultStrength.STABLE) {
                 mShowHideHelper.maybeNotifyModuleDelegate();
-                setPropertiesAndTriggerRender(mBundle);
-                ensureStabilityAndLogMetrics(/* recordStabilityDelay= */ true, mModuleShowConfig);
 
+                setPropertiesAndTriggerRender(mBundle, callback);
+                if (shouldLogMetrics) {
+                    ensureStabilityAndLogMetrics(
+                            /* recordStabilityDelay= */ true, mModuleShowConfig);
+                }
             } else if (mStrength == ResultStrength.FORCED_NULL) {
                 assert !mShowHideHelper.shouldShow();
                 mShowHideHelper.maybeNotifyModuleDelegate();
                 // Activates if STABLE was never encountered. In this case, TENTATIVE suggestions
                 // are considered stable (therefore log `prevModuleShowConfig`).
-                setPropertiesAndTriggerRender(mBundle);
-                ensureStabilityAndLogMetrics(
-                        /* recordStabilityDelay= */ false, prevModuleShowConfig);
+                setPropertiesAndTriggerRender(mBundle, callback);
+                if (shouldLogMetrics) {
+                    ensureStabilityAndLogMetrics(
+                            /* recordStabilityDelay= */ false, prevModuleShowConfig);
+                }
             }
 
             if (mBundle != null) {
@@ -348,6 +364,30 @@
                 }
             }
         }
+
+        @Nullable
+        private Callback<Integer> createOnModuleShowConfigFinalizedCallback() {
+            if (mModuleShowConfig == null || isModuleShowConfigFinalized(mModuleShowConfig)) {
+                return null;
+            }
+
+            return mCallbackController.makeCancelable(
+                    moduleShowConfig -> {
+                        mModuleShowConfig = moduleShowConfig;
+                        ensureStabilityAndLogMetrics(
+                                /* recordStabilityDelay= */ true, mModuleShowConfig);
+                        mModel.set(
+                                TabResumptionModuleProperties
+                                        .ON_MODULE_SHOW_CONFIG_FINALIZED_CALLBACK,
+                                null);
+                    });
+        }
+
+        /** Returns whether the type of ModuleShowConfig is a finalized one. */
+        private boolean isModuleShowConfigFinalized(@ModuleShowConfig int moduleShowConfig) {
+            return moduleShowConfig != ModuleShowConfig.SINGLE_TILE_ANY
+                    && moduleShowConfig != ModuleShowConfig.DOUBLE_TILE_ANY;
+        }
     }
 
     // If TENTATIVE suggestions were received, and the following duration has elapsed without
@@ -494,7 +534,8 @@
     }
 
     /** Computes and sets UI properties and triggers render. */
-    private void setPropertiesAndTriggerRender(@Nullable SuggestionBundle bundle) {
+    private void setPropertiesAndTriggerRender(
+            @Nullable SuggestionBundle bundle, @Nullable Callback<Integer> callback) {
         String title = null;
         boolean isVisible = false;
         if (bundle != null) {
@@ -504,6 +545,8 @@
                             R.plurals.home_modules_tab_resumption_title, bundle.entries.size());
             isVisible = true;
         }
+        mModel.set(
+                TabResumptionModuleProperties.ON_MODULE_SHOW_CONFIG_FINALIZED_CALLBACK, callback);
         mModel.set(TabResumptionModuleProperties.SUGGESTION_BUNDLE, bundle);
         mModel.set(TabResumptionModuleProperties.TITLE, title);
         mModel.set(TabResumptionModuleProperties.IS_VISIBLE, isVisible);
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMetricsUtils.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMetricsUtils.java
index aa0d018..a66aeb9 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMetricsUtils.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMetricsUtils.java
@@ -20,21 +20,25 @@
     // MagicStack.Clank.TabResumption.ClickInfo in enums.xml.
     @IntDef({
         ClickInfo.FOREIGN_SINGLE_FIRST,
-        ClickInfo.FOREIGN_FOREIGN_DOUBLE_FIRST,
-        ClickInfo.FOREIGN_FOREIGN_DOUBLE_SECOND,
         ClickInfo.LOCAL_SINGLE_FIRST,
-        ClickInfo.LOCAL_FOREIGN_DOUBLE_FIRST,
-        ClickInfo.LOCAL_FOREIGN_DOUBLE_SECOND,
+        ClickInfo.HISTORY_SINGLE_FIRST,
+        ClickInfo.FOREIGN_DOUBLE_ANY,
+        ClickInfo.LOCAL_DOUBLE_ANY,
+        ClickInfo.HISTORY_DOUBLE_ANY,
         ClickInfo.NUM_ENTRIES
     })
     @interface ClickInfo {
         int FOREIGN_SINGLE_FIRST = 0;
-        int FOREIGN_FOREIGN_DOUBLE_FIRST = 1;
-        int FOREIGN_FOREIGN_DOUBLE_SECOND = 2;
+        // int FOREIGN_FOREIGN_DOUBLE_FIRST = 1;
+        // int FOREIGN_FOREIGN_DOUBLE_SECOND = 2;
         int LOCAL_SINGLE_FIRST = 3;
-        int LOCAL_FOREIGN_DOUBLE_FIRST = 4;
-        int LOCAL_FOREIGN_DOUBLE_SECOND = 5;
-        int NUM_ENTRIES = 6;
+        // int LOCAL_FOREIGN_DOUBLE_FIRST = 4;
+        // int LOCAL_FOREIGN_DOUBLE_SECOND = 5;
+        int HISTORY_SINGLE_FIRST = 6;
+        int FOREIGN_DOUBLE_ANY = 7;
+        int LOCAL_DOUBLE_ANY = 8;
+        int HISTORY_DOUBLE_ANY = 9;
+        int NUM_ENTRIES = 10;
     }
 
     // These values are persisted to logs. Entries should not be renumbered and numeric values
@@ -66,6 +70,13 @@
         ModuleShowConfig.DOUBLE_TILE_FOREIGN_FOREIGN,
         ModuleShowConfig.SINGLE_TILE_LOCAL,
         ModuleShowConfig.DOUBLE_TILE_LOCAL_FOREIGN,
+        ModuleShowConfig.SINGLE_TILE_HISTORY,
+        ModuleShowConfig.DOUBLE_TILE_FOREIGN_HISTORY,
+        ModuleShowConfig.DOUBLE_TILE_LOCAL_LOCAL,
+        ModuleShowConfig.DOUBLE_TILE_HISTORY_HISTORY,
+        ModuleShowConfig.DOUBLE_TILE_LOCAL_HISTORY,
+        ModuleShowConfig.SINGLE_TILE_ANY,
+        ModuleShowConfig.DOUBLE_TILE_ANY,
         ModuleShowConfig.NUM_ENTRIES
     })
     @interface ModuleShowConfig {
@@ -73,7 +84,14 @@
         int DOUBLE_TILE_FOREIGN_FOREIGN = 1;
         int SINGLE_TILE_LOCAL = 2;
         int DOUBLE_TILE_LOCAL_FOREIGN = 3;
-        int NUM_ENTRIES = 4;
+        int DOUBLE_TILE_FOREIGN_HISTORY = 4;
+        int DOUBLE_TILE_LOCAL_LOCAL = 5;
+        int DOUBLE_TILE_LOCAL_HISTORY = 6;
+        int SINGLE_TILE_HISTORY = 7;
+        int DOUBLE_TILE_HISTORY_HISTORY = 8;
+        int SINGLE_TILE_ANY = 9;
+        int DOUBLE_TILE_ANY = 10;
+        int NUM_ENTRIES = 11;
     }
 
     static final String HISTOGRAM_CLICK_INFO = "MagicStack.Clank.TabResumption.ClickInfo";
@@ -96,31 +114,22 @@
             "MagicStack.Clank.TabResumption.TabRecency.Click";
 
     /** Maps specification of a clicked tile to a ClickInfo for logging. */
-    static @ClickInfo int computeClickInfo(@ModuleShowConfig int moduleShowConfig, int tileIndex) {
-        switch (moduleShowConfig) {
-            case ModuleShowConfig.SINGLE_TILE_FOREIGN:
-                assert tileIndex == 0;
-                return ClickInfo.FOREIGN_SINGLE_FIRST;
-
-            case ModuleShowConfig.DOUBLE_TILE_FOREIGN_FOREIGN:
-                assert tileIndex == 0 || tileIndex == 1;
-                return tileIndex == 0
-                        ? ClickInfo.FOREIGN_FOREIGN_DOUBLE_FIRST
-                        : ClickInfo.FOREIGN_FOREIGN_DOUBLE_SECOND;
-
-            case ModuleShowConfig.SINGLE_TILE_LOCAL:
-                assert tileIndex == 0;
-                return ClickInfo.LOCAL_SINGLE_FIRST;
-
-            case ModuleShowConfig.DOUBLE_TILE_LOCAL_FOREIGN:
-                assert tileIndex == 0 || tileIndex == 1;
-                return tileIndex == 0
-                        ? ClickInfo.LOCAL_FOREIGN_DOUBLE_FIRST
-                        : ClickInfo.LOCAL_FOREIGN_DOUBLE_SECOND;
+    static @ClickInfo int computeClickInfo(SuggestionEntry entry, int size) {
+        boolean isSingle = size == 1;
+        if (isSingle) {
+            if (entry.isLocalTab()) return ClickInfo.LOCAL_SINGLE_FIRST;
+            return entry.type == SuggestionEntryType.FOREIGN_TAB
+                    ? ClickInfo.FOREIGN_SINGLE_FIRST
+                    : ClickInfo.HISTORY_SINGLE_FIRST;
         }
 
-        assert false;
-        return ClickInfo.NUM_ENTRIES;
+        if (entry.isLocalTab()) {
+            return isSingle ? ClickInfo.LOCAL_SINGLE_FIRST : ClickInfo.LOCAL_DOUBLE_ANY;
+        } else if (entry.type == SuggestionEntryType.FOREIGN_TAB) {
+            return isSingle ? ClickInfo.FOREIGN_SINGLE_FIRST : ClickInfo.FOREIGN_DOUBLE_ANY;
+        } else {
+            return isSingle ? ClickInfo.HISTORY_SINGLE_FIRST : ClickInfo.HISTORY_DOUBLE_ANY;
+        }
     }
 
     /** Maps SuggestionBundle to a ModuleShowConfig value, or null if there are no suggestions. */
@@ -128,16 +137,48 @@
             @Nullable SuggestionBundle bundle) {
         if (bundle == null || bundle.entries.size() == 0) return null;
 
-        if (bundle.entries.size() == 1) {
-            return bundle.entries.get(0).isLocalTab()
-                    ? ModuleShowConfig.SINGLE_TILE_LOCAL
-                    : ModuleShowConfig.SINGLE_TILE_FOREIGN;
+        boolean isSingle = bundle.entries.size() == 1;
+        SuggestionEntry entry = bundle.entries.get(0);
+        if (isSingle) {
+            if (entry.isLocalTab()) {
+                return ModuleShowConfig.SINGLE_TILE_LOCAL;
+            } else if (entry.getNeedMatchLocalTab()) {
+                return ModuleShowConfig.SINGLE_TILE_ANY;
+            } else {
+                return entry.type == SuggestionEntryType.FOREIGN_TAB
+                        ? ModuleShowConfig.SINGLE_TILE_FOREIGN
+                        : ModuleShowConfig.SINGLE_TILE_HISTORY;
+            }
         }
 
-        // If Local Tab suggestion exists, it's always at index 0.
-        return bundle.entries.get(0).isLocalTab()
-                ? ModuleShowConfig.DOUBLE_TILE_LOCAL_FOREIGN
-                : ModuleShowConfig.DOUBLE_TILE_FOREIGN_FOREIGN;
+        SuggestionEntry entry1 = bundle.entries.get(1);
+        if (entry.getNeedMatchLocalTab() || entry1.getNeedMatchLocalTab()) {
+            return ModuleShowConfig.DOUBLE_TILE_ANY;
+        }
+
+        if (entry.isLocalTab()) {
+            if (entry1.isLocalTab()) {
+                return ModuleShowConfig.DOUBLE_TILE_LOCAL_LOCAL;
+            } else {
+                return entry1.type == SuggestionEntryType.FOREIGN_TAB
+                        ? ModuleShowConfig.DOUBLE_TILE_LOCAL_FOREIGN
+                        : ModuleShowConfig.DOUBLE_TILE_LOCAL_HISTORY;
+            }
+        } else if (entry.type == SuggestionEntryType.FOREIGN_TAB) {
+            if (entry1.isLocalTab()) {
+                return ModuleShowConfig.DOUBLE_TILE_LOCAL_FOREIGN;
+            }
+            return entry1.type == SuggestionEntryType.FOREIGN_TAB
+                    ? ModuleShowConfig.DOUBLE_TILE_FOREIGN_FOREIGN
+                    : ModuleShowConfig.DOUBLE_TILE_FOREIGN_HISTORY;
+        } else {
+            if (entry1.isLocalTab()) {
+                return ModuleShowConfig.DOUBLE_TILE_LOCAL_HISTORY;
+            }
+            return entry1.type == SuggestionEntryType.FOREIGN_TAB
+                    ? ModuleShowConfig.DOUBLE_TILE_FOREIGN_HISTORY
+                    : ModuleShowConfig.DOUBLE_TILE_HISTORY_HISTORY;
+        }
     }
 
     /** Records info (encoded tile count and index) on a clicked tile. */
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleProperties.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleProperties.java
index 95809890..424b929 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleProperties.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleProperties.java
@@ -33,6 +33,9 @@
     WritableObjectPropertyKey<Callback<Tab>> TAB_OBSERVER_CALLBACK =
             new WritableObjectPropertyKey();
 
+    WritableObjectPropertyKey<Callback<Integer>> ON_MODULE_SHOW_CONFIG_FINALIZED_CALLBACK =
+            new WritableObjectPropertyKey<>();
+
     PropertyKey[] ALL_KEYS =
             new PropertyKey[] {
                 IS_VISIBLE,
@@ -44,5 +47,6 @@
                 TAB_MODEL_SELECTOR_SUPPLIER,
                 TRACKING_TAB,
                 TAB_OBSERVER_CALLBACK,
+                ON_MODULE_SHOW_CONFIG_FINALIZED_CALLBACK,
             };
 }
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleUtils.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleUtils.java
index bb9fc79..ef716f2 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleUtils.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleUtils.java
@@ -177,4 +177,18 @@
         return res.getQuantityString(
                 R.plurals.n_minutes_ago_narrow, (int) minutesElapsed, minutesElapsed);
     }
+
+    /**
+     * Returns whether the suggestions to show are finalized, i.e., don't need to match local Tabs.
+     */
+    static boolean areSuggestionsFinalized(SuggestionBundle bundle) {
+        if (bundle == null || bundle.entries == null) return true;
+
+        if (bundle.entries.size() == 1) {
+            return !bundle.entries.get(0).getNeedMatchLocalTab();
+        } else {
+            return !bundle.entries.get(0).getNeedMatchLocalTab()
+                    && !bundle.entries.get(1).getNeedMatchLocalTab();
+        }
+    }
 }
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleView.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleView.java
index cdf91f1..42546879 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleView.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleView.java
@@ -37,6 +37,7 @@
     private String mAllTilesTexts;
     private ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
     private Callback<Tab> mTabObserverCallback;
+    @Nullable private Callback<Integer> mOnModuleShowConfigFinalizedCallback;
     @Nullable private Tab mTrackingTab;
 
     public TabResumptionModuleView(Context context, @Nullable AttributeSet attrs) {
@@ -113,6 +114,11 @@
         renderIfReady();
     }
 
+    void setOnModuleShowConfigFinalizedCallback(Callback<Integer> callback) {
+        mOnModuleShowConfigFinalizedCallback = callback;
+        renderIfReady();
+    }
+
     TabResumptionTileContainerView getTileContainerViewForTesting() {
         return mTileContainerView;
     }
@@ -122,7 +128,9 @@
                 && mUrlImageProvider != null
                 && mClickCallback != null
                 && mTabModelSelectorSupplier != null
-                && mTabObserverCallback != null) {
+                && mTabObserverCallback != null
+                && (TabResumptionModuleUtils.areSuggestionsFinalized(mBundle)
+                        || mOnModuleShowConfigFinalizedCallback != null)) {
             if (mBundle == null) {
                 mTileContainerView.removeAllViews();
                 mTileContainerView.cancelAllCallbacks();
@@ -137,7 +145,8 @@
                                 mUseSalientImage,
                                 mTabModelSelectorSupplier.get(),
                                 mTrackingTab,
-                                mTabObserverCallback);
+                                mTabObserverCallback,
+                                mOnModuleShowConfigFinalizedCallback);
             }
             setContentDescriptionOfTabResumption();
         }
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleViewBinder.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleViewBinder.java
index 894fea7..1d0a5fa 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleViewBinder.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleViewBinder.java
@@ -37,6 +37,12 @@
         } else if (TabResumptionModuleProperties.TAB_OBSERVER_CALLBACK == propertyKey) {
             moduleView.setTabObserverCallback(
                     model.get(TabResumptionModuleProperties.TAB_OBSERVER_CALLBACK));
+        } else if (TabResumptionModuleProperties.ON_MODULE_SHOW_CONFIG_FINALIZED_CALLBACK
+                == propertyKey) {
+            moduleView.setOnModuleShowConfigFinalizedCallback(
+                    model.get(
+                            TabResumptionModuleProperties
+                                    .ON_MODULE_SHOW_CONFIG_FINALIZED_CALLBACK));
         } else {
             assert false : "Unhandled property detected in TabResumptionModuleViewBinder!";
         }
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileContainerView.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileContainerView.java
index d613b47..fad117f 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileContainerView.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileContainerView.java
@@ -21,6 +21,7 @@
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
@@ -34,6 +35,9 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /** The view containing suggestion tiles on the tab resumption module. */
 public class TabResumptionTileContainerView extends LinearLayout {
     private final Size mThumbnailSize;
@@ -92,7 +96,8 @@
             boolean useSalientImage,
             TabModelSelector tabModelSelector,
             Tab trackingTab,
-            Callback<Tab> tabObserverCallback) {
+            Callback<Tab> tabObserverCallback,
+            @Nullable Callback<Integer> onModuleShowConfigFinalizedCallback) {
         reset();
 
         @ModuleShowConfig
@@ -101,13 +106,14 @@
         String allTilesTexts = "";
         int entryCount = bundle.entries.size();
         boolean isSingle = entryCount == 1;
-        int entryIndex = 0;
+
+        List<Callback<TabModelSelector>> pendingCallbacks = new ArrayList<>();
+        // A flag to indicate if any tile isn't finalized and we should update ModuleShowConfig.
+        boolean shouldUpdateModuleShowConfig = false;
         for (SuggestionEntry entry : bundle.entries) {
             assert moduleShowConfig != null;
             @ClickInfo
-            int clickInfo =
-                    TabResumptionModuleMetricsUtils.computeClickInfo(
-                            moduleShowConfig.intValue(), entryIndex);
+            int clickInfo = TabResumptionModuleMetricsUtils.computeClickInfo(entry, entryCount);
 
             // Add divider if some tile already exists.
             if (getChildCount() > 0) {
@@ -129,17 +135,25 @@
                         suggestionClickCallback.onSuggestionClicked(clickedEntry);
                     };
 
-            if (entry.needMatchLocalTab && entry.getLocalTabId() == Tab.INVALID_TAB_ID) {
-                if (trackingTab != null && entry.url.equals(trackingTab.getUrl())) {
-                    // If the shown Tab matches the tracking Tab, updates the entry with assigned
-                    // Tab Id.
-                    entry.setLocalTabId(trackingTab.getId());
-                    // Registers to listen to the tab's closing event, so the tab resumption module
-                    // will update if the current shown Tab is closed.
-                    tabObserverCallback.onResult(trackingTab);
+            if (entry.getNeedMatchLocalTab()) {
+                shouldUpdateModuleShowConfig = true;
+                if (entry.getLocalTabId() == Tab.INVALID_TAB_ID) {
+                    if (trackingTab != null && entry.url.equals(trackingTab.getUrl())) {
+                        // If the shown Tab matches the tracking Tab, updates the entry with
+                        // assigned Tab Id.
+                        entry.setLocalTabId(trackingTab.getId());
+                        entry.resetNeedMatchLocalTab();
+                        // Registers to listen to the tab's closing event, so the tab resumption
+                        // module will update if the current shown Tab is closed.
+                        tabObserverCallback.onResult(trackingTab);
+                        // Updates the clickInfo before it is used to setup click listener.
+                        clickInfo =
+                                isSingle
+                                        ? ClickInfo.LOCAL_SINGLE_FIRST
+                                        : ClickInfo.LOCAL_DOUBLE_ANY;
+                    }
                 }
             }
-
             if (entry.isLocalTab() && isSingle) {
                 allTilesTexts +=
                         loadLocalTabSingle(
@@ -147,7 +161,7 @@
                                 entry,
                                 urlImageProvider,
                                 suggestionClickCallbackWithLogging,
-                                clickInfo,
+                                ClickInfo.LOCAL_SINGLE_FIRST,
                                 recencyMs);
             } else {
                 int layoutId =
@@ -168,7 +182,7 @@
                         tileView, suggestionClickCallbackWithLogging, entry, clickInfo);
                 addView(tileView);
 
-                if (entry.needMatchLocalTab && entry.getLocalTabId() == Tab.INVALID_TAB_ID) {
+                if (entry.getNeedMatchLocalTab() && entry.getLocalTabId() == Tab.INVALID_TAB_ID) {
                     // For any history or foreign session suggestion which doesn't match the
                     // tracking Tab but still need to check, creates a callback to update the tile
                     // if a match of a local Tab is found after the tab state is initialized.
@@ -180,28 +194,65 @@
                                                 tileView,
                                                 suggestionClickCallback,
                                                 tms,
-                                                tabObserverCallback);
+                                                tabObserverCallback,
+                                                entryCount);
                                     });
-                    TabModelUtils.runOnTabStateInitialized(tabModelSelector, callback);
+                    pendingCallbacks.add(callback);
                 }
             }
-            ++entryIndex;
+        }
+
+        // Setup callbacks after all suggestion(s) are iterated.
+        if (!pendingCallbacks.isEmpty() || shouldUpdateModuleShowConfig) {
+            handleTileMatchAndUpdate(
+                    pendingCallbacks,
+                    onModuleShowConfigFinalizedCallback,
+                    tabModelSelector,
+                    bundle,
+                    shouldUpdateModuleShowConfig);
         }
         return allTilesTexts;
     }
 
+    private void handleTileMatchAndUpdate(
+            List<Callback<TabModelSelector>> pendingCallbacks,
+            Callback<Integer> onModuleShowConfigFinalizedCallback,
+            TabModelSelector tabModelSelector,
+            SuggestionBundle bundle,
+            boolean updateModuleShowConfig) {
+        if (!pendingCallbacks.isEmpty()) {
+            Callback<TabModelSelector> onTabStateInitializedCallback =
+                    mCallbackController.makeCancelable(
+                            (newTabModelSelector) -> {
+                                for (var callback : pendingCallbacks) {
+                                    callback.onResult(newTabModelSelector);
+                                }
+                                pendingCallbacks.clear();
+                                onModuleShowConfigFinalizedCallback.onResult(
+                                        TabResumptionModuleMetricsUtils.computeModuleShowConfig(
+                                                bundle));
+                            });
+            TabModelUtils.runOnTabStateInitialized(tabModelSelector, onTabStateInitializedCallback);
+        } else if (updateModuleShowConfig) {
+            onModuleShowConfigFinalizedCallback.onResult(
+                    TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+        }
+    }
+
     /**
      * Called to update a TabResumptionTileView to track a local Tab. It updates the click listener
      * for the view, and register to observe the Tab's closure state.
      */
     private void updateTile(
-            SuggestionEntry entry,
-            TabResumptionTileView tileView,
-            SuggestionClickCallback suggestionClickCallback,
-            TabModelSelector tabModelSelector,
-            Callback<Tab> tabObserverCallback) {
+            @NonNull SuggestionEntry entry,
+            @NonNull TabResumptionTileView tileView,
+            @NonNull SuggestionClickCallback suggestionClickCallback,
+            @NonNull TabModelSelector tabModelSelector,
+            @NonNull Callback<Tab> tabObserverCallback,
+            int size) {
         TabModel tabModel = tabModelSelector.getModel(false);
         int index = TabModelUtils.getTabIndexByUrl(tabModel, entry.url.getSpec());
+        entry.resetNeedMatchLocalTab();
 
         if (index != TabModel.INVALID_TAB_INDEX) {
             Tab tab = tabModel.getTabAt(index);
@@ -212,7 +263,14 @@
                 // Updates the click listener of the tile to allow switching to an existing Tab,
                 // rather than navigates within the same NTP.
                 bindSuggestionClickCallback(
-                        tileView, suggestionClickCallback, entry, ClickInfo.LOCAL_SINGLE_FIRST);
+                        tileView,
+                        suggestionClickCallback,
+                        entry,
+                        TabResumptionModuleMetricsUtils.computeClickInfo(entry, size));
+                if (entry.type != SuggestionEntryType.LOCAL_TAB) {
+                    // Removes the "device name" from the tile.
+                    tileView.updatePostInfoView(TabResumptionModuleUtils.getDomainUrl(entry.url));
+                }
             }
         }
     }
@@ -225,10 +283,18 @@
             long recencyMs) {
         Resources res = getContext().getResources();
         String domainUrl = TabResumptionModuleUtils.getDomainUrl(entry.url);
+        boolean isHistory = entry.type == SuggestionEntryType.HISTORY;
+        String postInfoText =
+                entry.isLocalTab() || isHistory && TextUtils.isEmpty(entry.sourceName)
+                        ? domainUrl
+                        : res.getString(
+                                R.string.tab_resumption_module_domain_url_and_device_name,
+                                domainUrl,
+                                entry.sourceName);
         if (isSingle) {
             // Single Local Tab suggestion is handled by #loadLocalTabSingle().
             assert !entry.isLocalTab();
-            boolean isHistory = entry.type == SuggestionEntryType.HISTORY;
+
             String appChipText =
                     isHistory
                             ? tileView.maybeShowAppChip(mPackageManager, entry.type, entry.appId)
@@ -237,28 +303,11 @@
                     appChipText == null
                             ? getReasonToShowTab(entry.reasonToShowTab, recencyMs)
                             : null;
-            String postInfoText =
-                    isHistory
-                            ? domainUrl
-                            : res.getString(
-                                    R.string.tab_resumption_module_domain_url_and_device_name,
-                                    domainUrl,
-                                    entry.sourceName);
             return tileView.setSuggestionTextsSingle(
                     preInfoText, appChipText, entry.title, postInfoText);
         }
 
-        String infoText;
-        if (entry.isLocalTab()) {
-            infoText = domainUrl;
-        } else {
-            infoText =
-                    res.getString(
-                            R.string.tab_resumption_module_domain_url_and_device_name,
-                            domainUrl,
-                            entry.sourceName);
-        }
-        return tileView.setSuggestionTextsMulti(entry.title, infoText);
+        return tileView.setSuggestionTextsMulti(entry.title, postInfoText);
     }
 
     private String getReasonToShowTab(String reasonToShowTab, long recencyMs) {
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileView.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileView.java
index 4b59e26..13b0bc9 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileView.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionTileView.java
@@ -32,6 +32,7 @@
     private RoundedCornerImageView mIconView;
     private TextView mTilePreInfoView;
     private TextView mTileDisplayView;
+    private TextView mTilePostInfoView;
 
     private final int mSalientImageCornerRadiusPx;
 
@@ -49,6 +50,7 @@
         mIconView = findViewById(R.id.tile_icon);
         mTilePreInfoView = findViewById(R.id.tile_pre_info_text);
         mTileDisplayView = findViewById(R.id.tile_display_text);
+        mTilePostInfoView = findViewById(R.id.tile_post_info_text);
     }
 
     void destroy() {
@@ -144,16 +146,21 @@
      * Assigns all texts for the "multi-tile" case and returns the content description string.
      *
      * @param displayText Main text (page title).
-     * @param infoText Info to show below main text.
+     * @param postInfoText Info to show below main text.
      */
-    public String setSuggestionTextsMulti(String displayText, String infoText) {
+    public String setSuggestionTextsMulti(String displayText, String postInfoText) {
         ((TextView) findViewById(R.id.tile_display_text)).setText(displayText);
-        ((TextView) findViewById(R.id.tile_info_text)).setText(infoText);
+        ((TextView) findViewById(R.id.tile_post_info_text)).setText(postInfoText);
 
+        // Construct a content description from the TabResumptionTileView. This string will be used
+        // to construct the content description of its parent view TabResumptionModuleView which
+        // currently has no text accessible to TalkBack. When TabResumptionModuleView is focused,
+        // TalkBack will sequentially read all translated strings from its subviews, using
+        // SEPARATE_COMMA as a delimiter between each.
         StringBuilder stringBuilder = new StringBuilder();
         stringBuilder.append(displayText);
         stringBuilder.append(SEPARATE_COMMA);
-        stringBuilder.append(infoText);
+        stringBuilder.append(postInfoText);
         stringBuilder.append(SEPARATE_PERIOD);
 
         String contentDescription = stringBuilder.toString();
@@ -175,4 +182,12 @@
                 mSalientImageCornerRadiusPx,
                 mSalientImageCornerRadiusPx);
     }
+
+    /**
+     * Updates the post info text of the tile. It consists of a domain URL for a local Tab, and a
+     * domain URL + the device info for a remote Tab.
+     */
+    public void updatePostInfoView(String postInfoText) {
+        mTilePostInfoView.setText(postInfoText);
+    }
 }
diff --git a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMetricsUtilsUnitTest.java b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMetricsUtilsUnitTest.java
index 359c88c7..09770bd 100644
--- a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMetricsUtilsUnitTest.java
+++ b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMetricsUtilsUnitTest.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.tab_resumption;
 
+import static org.junit.Assert.assertEquals;
+
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
@@ -12,12 +14,13 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.HistogramWatcher;
+import org.chromium.chrome.browser.tab_resumption.TabResumptionModuleMetricsUtils.ClickInfo;
 import org.chromium.chrome.browser.tab_resumption.TabResumptionModuleMetricsUtils.ModuleShowConfig;
 
 /** Unit tests for {@link TabResumptionModuleMetricsUtils}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-public class TabResumptionModuleMetricsUtilsUnitTest {
+public class TabResumptionModuleMetricsUtilsUnitTest extends TestSupportExtended {
     @Test
     @SmallTest
     public void testRecordSalientImageAvailability() {
@@ -44,4 +47,172 @@
         TabResumptionModuleMetricsUtils.recordSeeMoreLinkClicked(config);
         histogramWatcher.assertExpected();
     }
+
+    @Test
+    @SmallTest
+    public void testComputeModuleShowConfig() {
+        SuggestionBundle bundle = new SuggestionBundle(CURRENT_TIME_MS);
+        int tabId = 10;
+        SuggestionEntry localEntry = createLocalSuggestion(tabId);
+        SuggestionEntry foreignEntry =
+                SuggestionEntry.createFromForeignSessionTab("My Tablet", TAB6);
+        SuggestionEntry foreignEntry1 = SuggestionEntry.createFromForeignSessionTab("Maps", TAB5);
+        SuggestionEntry historyEntry = createHistorySuggestion(/* needMatchLocalTab= */ false);
+
+        bundle.entries.add(localEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.SINGLE_TILE_LOCAL,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(foreignEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_LOCAL_FOREIGN,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(1, localEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_LOCAL_LOCAL,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(1, historyEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_LOCAL_HISTORY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.clear();
+        bundle.entries.add(foreignEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.SINGLE_TILE_FOREIGN,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(foreignEntry1);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_FOREIGN_FOREIGN,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(1, localEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_LOCAL_FOREIGN,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(1, historyEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_FOREIGN_HISTORY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.clear();
+        bundle.entries.add(historyEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.SINGLE_TILE_HISTORY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(foreignEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_FOREIGN_HISTORY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(1, localEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_LOCAL_HISTORY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(1, historyEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_HISTORY_HISTORY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+    }
+
+    @Test
+    @SmallTest
+    public void testComputeModuleShowConfig_WithNotFinalizedSuggestion() {
+        SuggestionBundle bundle = new SuggestionBundle(CURRENT_TIME_MS);
+        int tabId = 10;
+        SuggestionEntry localEntry = createLocalSuggestion(tabId);
+        SuggestionEntry foreignEntry =
+                SuggestionEntry.createFromForeignSessionTab("My Tablet", TAB6);
+        SuggestionEntry foreignEntryNotFinalized =
+                createForeignSuggestion(/* needMatchLocalTab= */ true);
+        SuggestionEntry historyEntryNotFinalized =
+                createHistorySuggestion(/* needMatchLocalTab= */ true);
+
+        bundle.entries.add(localEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.SINGLE_TILE_LOCAL,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.clear();
+        bundle.entries.add(historyEntryNotFinalized);
+        assertEquals(
+                (Integer) ModuleShowConfig.SINGLE_TILE_ANY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(foreignEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_ANY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.clear();
+        bundle.entries.add(foreignEntryNotFinalized);
+        assertEquals(
+                (Integer) ModuleShowConfig.SINGLE_TILE_ANY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+
+        bundle.entries.add(localEntry);
+        assertEquals(
+                (Integer) ModuleShowConfig.DOUBLE_TILE_ANY,
+                TabResumptionModuleMetricsUtils.computeModuleShowConfig(bundle));
+    }
+
+    @Test
+    @SmallTest
+    public void testComputeClickInfo() {
+        int tabId = 10;
+        SuggestionEntry localEntry = createLocalSuggestion(tabId);
+        SuggestionEntry foreignEntry =
+                SuggestionEntry.createFromForeignSessionTab("My Tablet", TAB6);
+        SuggestionEntry foreignEntryNotFinalized =
+                createForeignSuggestion(/* needMatchLocalTab= */ true);
+        SuggestionEntry historyEntry = createHistorySuggestion(/* needMatchLocalTab= */ false);
+        SuggestionEntry historyEntryNotFinalized =
+                createHistorySuggestion(/* needMatchLocalTab= */ true);
+
+        // Cases of size = 1:
+        assertEquals(
+                ClickInfo.LOCAL_SINGLE_FIRST,
+                TabResumptionModuleMetricsUtils.computeClickInfo(localEntry, 1));
+
+        // Verifies that the same ClickInfo is returned for both finalized and not finalized tiles.
+        assertEquals(
+                ClickInfo.FOREIGN_SINGLE_FIRST,
+                TabResumptionModuleMetricsUtils.computeClickInfo(foreignEntry, 1));
+        assertEquals(
+                ClickInfo.FOREIGN_SINGLE_FIRST,
+                TabResumptionModuleMetricsUtils.computeClickInfo(foreignEntryNotFinalized, 1));
+
+        assertEquals(
+                ClickInfo.HISTORY_SINGLE_FIRST,
+                TabResumptionModuleMetricsUtils.computeClickInfo(historyEntry, 1));
+        assertEquals(
+                ClickInfo.HISTORY_SINGLE_FIRST,
+                TabResumptionModuleMetricsUtils.computeClickInfo(historyEntryNotFinalized, 1));
+
+        // Cases of size = 2:
+        assertEquals(
+                ClickInfo.LOCAL_DOUBLE_ANY,
+                TabResumptionModuleMetricsUtils.computeClickInfo(localEntry, 2));
+
+        assertEquals(
+                ClickInfo.FOREIGN_DOUBLE_ANY,
+                TabResumptionModuleMetricsUtils.computeClickInfo(foreignEntry, 2));
+        assertEquals(
+                ClickInfo.FOREIGN_DOUBLE_ANY,
+                TabResumptionModuleMetricsUtils.computeClickInfo(foreignEntryNotFinalized, 2));
+
+        assertEquals(
+                ClickInfo.HISTORY_DOUBLE_ANY,
+                TabResumptionModuleMetricsUtils.computeClickInfo(historyEntry, 2));
+        assertEquals(
+                ClickInfo.HISTORY_DOUBLE_ANY,
+                TabResumptionModuleMetricsUtils.computeClickInfo(historyEntryNotFinalized, 2));
+    }
 }
diff --git a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleUtilsUnitTest.java b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleUtilsUnitTest.java
index 2942afc..63c75a9c 100644
--- a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleUtilsUnitTest.java
+++ b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleUtilsUnitTest.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.tab_resumption;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.content.res.Resources;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -20,7 +23,7 @@
 
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-public class TabResumptionModuleUtilsUnitTest {
+public class TabResumptionModuleUtilsUnitTest extends TestSupportExtended {
 
     @Test
     @SmallTest
@@ -47,4 +50,27 @@
         Assert.assertEquals(
                 "100 days ago", TabResumptionModuleUtils.getRecencyString(res, dayInMs * 100));
     }
+
+    @Test
+    @SmallTest
+    public void testAreSuggestionsFinalized() {
+        assertTrue(TabResumptionModuleUtils.areSuggestionsFinalized(null));
+
+        SuggestionBundle bundle = new SuggestionBundle(CURRENT_TIME_MS);
+        SuggestionEntry entry = createLocalSuggestion(11);
+        bundle.entries.add(entry);
+        assertTrue(TabResumptionModuleUtils.areSuggestionsFinalized(bundle));
+
+        SuggestionEntry entry1 = createLocalSuggestion(12);
+        bundle.entries.add(entry1);
+        assertTrue(TabResumptionModuleUtils.areSuggestionsFinalized(bundle));
+
+        SuggestionEntry entryNotFinalized = createHistorySuggestion(/* needMatchLocalTab= */ true);
+        bundle.entries.add(1, entryNotFinalized);
+        assertFalse(TabResumptionModuleUtils.areSuggestionsFinalized(bundle));
+
+        bundle.entries.clear();
+        bundle.entries.add(entryNotFinalized);
+        assertFalse(TabResumptionModuleUtils.areSuggestionsFinalized(bundle));
+    }
 }
diff --git a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleViewUnitTest.java b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleViewUnitTest.java
index fd1ddcda..4a8330b 100644
--- a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleViewUnitTest.java
+++ b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleViewUnitTest.java
@@ -29,6 +29,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
 
@@ -54,6 +55,8 @@
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab_resumption.TabResumptionModuleMetricsUtils.ClickInfo;
+import org.chromium.chrome.browser.tab_resumption.TabResumptionModuleMetricsUtils.ModuleShowConfig;
 import org.chromium.chrome.browser.tab_resumption.TabResumptionModuleUtils.SuggestionClickCallback;
 import org.chromium.chrome.browser.tab_resumption.UrlImageProvider.UrlImageCallback;
 import org.chromium.chrome.browser.tab_ui.TabThumbnailView;
@@ -66,10 +69,13 @@
 import org.chromium.url.GURL;
 import org.chromium.url.JUnitTestGURLs;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 @EnableFeatures({ChromeFeatureList.TAB_RESUMPTION_MODULE_ANDROID})
-public class TabResumptionModuleViewUnitTest extends TestSupport {
+public class TabResumptionModuleViewUnitTest extends TestSupportExtended {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule public JniMocker mocker = new JniMocker();
 
@@ -86,6 +92,7 @@
     @Mock private TabModelSelector mTabModelSelector;
     @Mock private TabModel mTabModel;
     @Mock private Callback<Tab> mTabObserverCallback;
+    @Mock private Callback<Integer> mOnModuleShowConfigFinalizedCallback;
 
     @Captor private ArgumentCaptor<GURL> mFetchImagePageUrlCaptor;
     @Captor private ArgumentCaptor<Callback<Drawable>> mThumbnailCallbackCaptor;
@@ -104,6 +111,7 @@
     private int mClickCount;
     private Context mContext;
     private ObservableSupplierImpl<TabModelSelector> mTabModelSelectorSupplier;
+    private List<Tab> mTabsInTabModel = new ArrayList<>();
 
     @Before
     public void setUp() {
@@ -141,6 +149,7 @@
 
     @After
     public void tearDown() {
+        mTabsInTabModel.clear();
         mModuleView.destroy();
         assertTrue(mTileContainerView.isCallbackControllerNullForTesting());
         mModuleView = null;
@@ -429,7 +438,7 @@
                         mTab.getId(),
                         /* appId= */ null,
                         /* reasonToShowTab= */ null,
-                        /* reasonToShowTab= */ false);
+                        /* needMatchLocalTab= */ false);
         mSuggestionBundle.entries.add(entry1);
         Assert.assertEquals(0, mTileContainerView.getChildCount());
 
@@ -538,7 +547,7 @@
                 ((TextView) tile1.findViewById(R.id.tile_display_text)).getText());
         Assert.assertEquals(
                 "www.blue.com \u2022 My Tablet",
-                ((TextView) tile1.findViewById(R.id.tile_info_text)).getText());
+                ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
 
         View divider = (View) mTileContainerView.getChildAt(1);
         Assert.assertEquals(View.VISIBLE, divider.getVisibility());
@@ -548,7 +557,7 @@
                 "Google Dog", ((TextView) tile2.findViewById(R.id.tile_display_text)).getText());
         Assert.assertEquals(
                 "www.google.com \u2022 Desktop",
-                ((TextView) tile2.findViewById(R.id.tile_info_text)).getText());
+                ((TextView) tile2.findViewById(R.id.tile_post_info_text)).getText());
 
         // Images are not loaded yet.
         Assert.assertNull(((ImageView) tile1.findViewById(R.id.tile_icon)).getDrawable());
@@ -612,7 +621,7 @@
         Assert.assertEquals(
                 TAB_TITLE, ((TextView) tile1.findViewById(R.id.tile_display_text)).getText());
         Assert.assertEquals(
-                "www.one.com", ((TextView) tile1.findViewById(R.id.tile_info_text)).getText());
+                "www.one.com", ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
 
         View divider = (View) mTileContainerView.getChildAt(1);
         Assert.assertEquals(View.VISIBLE, divider.getVisibility());
@@ -622,7 +631,7 @@
                 "Google Dog", ((TextView) tile2.findViewById(R.id.tile_display_text)).getText());
         Assert.assertEquals(
                 "www.google.com \u2022 Desktop",
-                ((TextView) tile2.findViewById(R.id.tile_info_text)).getText());
+                ((TextView) tile2.findViewById(R.id.tile_post_info_text)).getText());
 
         // Images are not loaded yet.
         Assert.assertNull(((ImageView) tile1.findViewById(R.id.tile_icon)).getDrawable());
@@ -675,7 +684,7 @@
         SuggestionEntry entry1 =
                 new SuggestionEntry(
                         SuggestionEntryType.HISTORY,
-                        "Source not to be shown",
+                        "Device Source",
                         JUnitTestGURLs.GOOGLE_URL_DOG,
                         "Google Dog",
                         makeTimestamp(24 - 3, 0, 0),
@@ -711,7 +720,7 @@
                 "Google Dog", ((TextView) tile1.findViewById(R.id.tile_display_text)).getText());
         // Actual code would remove "www." prefix, but the test's JNI mock doesn't do so.
         Assert.assertEquals(
-                "www.google.com",
+                "www.google.com \u2022 Device Source",
                 ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
 
         // Image is not loaded yet.
@@ -742,7 +751,7 @@
         SuggestionEntry entry1 =
                 new SuggestionEntry(
                         SuggestionEntryType.HISTORY,
-                        "Source not to be shown",
+                        "Device Source",
                         JUnitTestGURLs.GOOGLE_URL_DOG,
                         "Google Dog",
                         makeTimestamp(24 - 3, 0, 0),
@@ -778,7 +787,7 @@
         Assert.assertEquals("Google Dog", displayTextView.getText());
         // Actual code would remove "www." prefix, but the test's JNI mock doesn't do so.
         Assert.assertEquals(
-                "www.google.com",
+                "www.google.com \u2022 Device Source",
                 ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
 
         // Image is not loaded yet.
@@ -808,7 +817,7 @@
         SuggestionEntry entry1 =
                 new SuggestionEntry(
                         SuggestionEntryType.HISTORY,
-                        "Source not to be shown",
+                        "Device Source",
                         JUnitTestGURLs.GOOGLE_URL_DOG,
                         "Google Dog",
                         makeTimestamp(24 - 3, 0, 0),
@@ -843,7 +852,7 @@
                 "Google Dog", ((TextView) tile1.findViewById(R.id.tile_display_text)).getText());
         // Actual code would remove "www." prefix, but the test's JNI mock doesn't do so.
         Assert.assertEquals(
-                "www.google.com",
+                "www.google.com \u2022 Device Source",
                 ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
     }
 
@@ -856,7 +865,7 @@
         SuggestionEntry entry1 =
                 new SuggestionEntry(
                         SuggestionEntryType.HISTORY,
-                        "Source not to be shown",
+                        "Device Source",
                         JUnitTestGURLs.GOOGLE_URL_DOG,
                         "Google Dog",
                         makeTimestamp(24 - 3, 0, 0),
@@ -892,7 +901,7 @@
                 "Google Dog", ((TextView) tile1.findViewById(R.id.tile_display_text)).getText());
         // Actual code would remove "www." prefix, but the test's JNI mock doesn't do so.
         Assert.assertEquals(
-                "www.google.com",
+                "www.google.com \u2022 Device Source",
                 ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
     }
 
@@ -906,7 +915,7 @@
         SuggestionEntry entry1 =
                 new SuggestionEntry(
                         SuggestionEntryType.HISTORY,
-                        "Source not to be shown",
+                        "Device Source",
                         JUnitTestGURLs.BLUE_1,
                         "Google Dog",
                         makeTimestamp(24 - 3, 0, 0),
@@ -916,18 +925,27 @@
                         /* needMatchLocalTab= */ true);
 
         assertTrue(entry1.url.equals(mTrackingTab.getUrl()));
+        assertTrue(entry1.getNeedMatchLocalTab());
         mSuggestionBundle.entries.add(entry1);
 
         Assert.assertEquals(0, mTileContainerView.getChildCount());
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
 
         mModuleView.setSuggestionBundle(mSuggestionBundle);
         Assert.assertEquals(1, mTileContainerView.getChildCount());
-        // Verifies that the suggestion entry is updated to match the tracking Tab.
-        assertEquals(entry1.getLocalTabId(), mTrackingTab.getId());
-        verify(mTabObserverCallback).onResult(eq(mTrackingTab));
 
-        // Verifies that a LocalTileView is created for the history suggestion which matches the
-        // trackingTab.
+        // Verifies that a LocalTileView is created for the history suggestion and is updated to
+        // match the trackingTab.
+        LocalTileView localTileView = (LocalTileView) mTileContainerView.getChildAt(0);
+        verifyTileMatchesALocalTab(
+                entry1,
+                mTrackingTab,
+                localTileView,
+                /* shouldUpdateModuleShowConfig= */ true,
+                /* expectedModuleShowConfig= */ ModuleShowConfig.SINGLE_TILE_LOCAL,
+                ClickInfo.LOCAL_SINGLE_FIRST,
+                /* initialClickCount= */ 0,
+                "www.blue.com");
 
         // Capture call to fetch favicon.
         verify(mUrlImageProvider, atLeastOnce())
@@ -941,9 +959,7 @@
                         eq(mThumbnailSize),
                         mThumbnailCallbackCaptor.capture());
 
-        // Check tile texts.
-        LocalTileView localTileView = (LocalTileView) mTileContainerView.getChildAt(0);
-        // The default reason isn't shown.
+        // Check tile texts. The default reason isn't shown.
         Assert.assertEquals(
                 View.GONE, localTileView.findViewById(R.id.tab_show_reason).getVisibility());
         TextView titleView = localTileView.findViewById(R.id.tab_title_view);
@@ -951,10 +967,6 @@
         // Verifies that the maximum lines are the default 3 lines when the reason chip isn't shown.
         Assert.assertEquals(
                 TabResumptionModuleUtils.DISPLAY_TEXT_MAX_LINES_DEFAULT, titleView.getMaxLines());
-        // Actual code would remove "www." prefix, but the test's JNI mock doesn't do so.
-        Assert.assertEquals(
-                "www.blue.com",
-                ((TextView) localTileView.findViewById(R.id.tab_url_view)).getText());
         // Verifies that a placeholder icon drawable is set for the tab thumbnail.
         Assert.assertNotNull(
                 ((TabThumbnailView) localTileView.findViewById(R.id.tab_thumbnail))
@@ -978,13 +990,6 @@
         Assert.assertNull(
                 ((TabThumbnailView) localTileView.findViewById(R.id.tab_thumbnail))
                         .getIconDrawableForTesting());
-
-        // Simulate click on the local Tab.
-        Assert.assertEquals(0, mClickCount);
-        Assert.assertNull(mLastClickEntry);
-        localTileView.performClick();
-        Assert.assertEquals(1, mClickCount);
-        Assert.assertEquals(TRACKING_TAB_ID, mLastClickEntry.getLocalTabId());
     }
 
     @Test
@@ -997,7 +1002,7 @@
         SuggestionEntry entry1 =
                 new SuggestionEntry(
                         SuggestionEntryType.HISTORY,
-                        "Source not to be shown",
+                        "Device Source",
                         JUnitTestGURLs.URL_1,
                         "Google Dog",
                         makeTimestamp(24 - 3, 0, 0),
@@ -1007,6 +1012,7 @@
                         /* needMatchLocalTab= */ true);
         // The entry1 doesn't match the tracking Tab.
         assertFalse(entry1.url.equals(mTrackingTab.getUrl().getSpec()));
+        assertTrue(entry1.getNeedMatchLocalTab());
         mSuggestionBundle.entries.add(entry1);
 
         Assert.assertEquals(0, mTileContainerView.getChildCount());
@@ -1014,7 +1020,9 @@
         mModuleView.setSuggestionBundle(mSuggestionBundle);
         Assert.assertEquals(1, mTileContainerView.getChildCount());
         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
-        assertEquals(entry1.getLocalTabId(), Tab.INVALID_TAB_ID);
+
+        assertEquals(Tab.INVALID_TAB_ID, entry1.getLocalTabId());
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
 
         // Verifies that a TabResumptionTileView is created for the history suggestion.
         // Capture call to fetch image.
@@ -1037,7 +1045,8 @@
         Assert.assertEquals("Google Dog", displayTextView.getText());
         // Actual code would remove "www." prefix, but the test's JNI mock doesn't do so.
         Assert.assertEquals(
-                "www.one.com", ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
+                "www.one.com \u2022 Device Source",
+                ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
 
         // Image is not loaded yet.
         Assert.assertNull(((ImageView) tile1.findViewById(R.id.tile_icon)).getDrawable());
@@ -1051,26 +1060,30 @@
         Assert.assertEquals(bitmap1, drawable1.getBitmap());
 
         // Simulate click.
-        Assert.assertEquals(0, mClickCount);
-        Assert.assertNull(mLastClickEntry);
-        tile1.performClick();
-        Assert.assertEquals(1, mClickCount);
-        Assert.assertEquals(JUnitTestGURLs.URL_1, mLastClickEntry.url);
+        verifyClickingNotMatchedTile(
+                entry1,
+                tile1,
+                ClickInfo.HISTORY_SINGLE_FIRST,
+                /* initialClickCount= */ 0,
+                /* isTileUpdated= */ false,
+                "www.one.com \u2022 Device Source");
 
         // Sets the TabModel to make entry1 matches the other Tab in the model.
-        when(mTabModel.getCount()).thenReturn(2);
-        when(mTabModel.getTabAt(0)).thenReturn(mTrackingTab);
-        when(mTabModel.getTabAt(1)).thenReturn(mTab);
-        mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+        mTabsInTabModel.add(mTrackingTab);
+        mTabsInTabModel.add(mTab);
+        initializeTabState(mTabsInTabModel);
 
-        // Verifies that the entry1 and its tile are updated.
-        assertEquals(mTab.getId(), entry1.getLocalTabId());
-        verify(mTabObserverCallback).onResult(eq(mTab));
-
-        // Simulate click on the local Tab.
-        tile1.performClick();
-        Assert.assertEquals(2, mClickCount);
-        Assert.assertEquals(TAB_ID, mLastClickEntry.getLocalTabId());
+        // Verifies that the entry1 is updated to match mTab after the tab state initialization is
+        // completed.
+        verifyTileMatchesALocalTab(
+                entry1,
+                mTab,
+                tile1,
+                /* shouldUpdateModuleShowConfig= */ true,
+                ModuleShowConfig.SINGLE_TILE_LOCAL,
+                ClickInfo.LOCAL_SINGLE_FIRST,
+                /* initialClickCount= */ 1,
+                "www.one.com");
     }
 
     @Test
@@ -1083,7 +1096,7 @@
         SuggestionEntry entry1 =
                 new SuggestionEntry(
                         SuggestionEntryType.HISTORY,
-                        "Source not to be shown",
+                        "Device Source",
                         JUnitTestGURLs.URL_2,
                         "Google Dog",
                         makeTimestamp(24 - 3, 0, 0),
@@ -1093,6 +1106,7 @@
                         /* needMatchLocalTab= */ true);
         // The entry1 doesn't match the tracking Tab.
         assertFalse(entry1.url.equals(mTrackingTab.getUrl().getSpec()));
+        assertTrue(entry1.getNeedMatchLocalTab());
         mSuggestionBundle.entries.add(entry1);
 
         Assert.assertEquals(0, mTileContainerView.getChildCount());
@@ -1101,6 +1115,7 @@
         Assert.assertEquals(1, mTileContainerView.getChildCount());
         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
         assertEquals(entry1.getLocalTabId(), Tab.INVALID_TAB_ID);
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
 
         // Verifies that a TabResumptionTileView is created for the history suggestion.
         // Capture call to fetch image.
@@ -1123,7 +1138,8 @@
         Assert.assertEquals("Google Dog", displayTextView.getText());
         // Actual code would remove "www." prefix, but the test's JNI mock doesn't do so.
         Assert.assertEquals(
-                "www.two.com", ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
+                "www.two.com \u2022 Device Source",
+                ((TextView) tile1.findViewById(R.id.tile_post_info_text)).getText());
 
         // Image is not loaded yet.
         Assert.assertNull(((ImageView) tile1.findViewById(R.id.tile_icon)).getDrawable());
@@ -1137,25 +1153,374 @@
         Assert.assertEquals(bitmap1, drawable1.getBitmap());
 
         // Simulate click.
-        Assert.assertEquals(0, mClickCount);
-        Assert.assertNull(mLastClickEntry);
-        tile1.performClick();
-        Assert.assertEquals(1, mClickCount);
-        Assert.assertEquals(JUnitTestGURLs.URL_2, mLastClickEntry.url);
+        verifyClickingNotMatchedTile(
+                entry1,
+                tile1,
+                ClickInfo.HISTORY_SINGLE_FIRST,
+                /* initialClickCount= */ 0,
+                /* isTileUpdated= */ false,
+                "www.two.com \u2022 Device Source");
 
-        when(mTabModel.getCount()).thenReturn(2);
-        when(mTabModel.getTabAt(0)).thenReturn(mTrackingTab);
-        when(mTabModel.getTabAt(1)).thenReturn(mTab);
-        mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+        mTabsInTabModel.add(mTrackingTab);
+        mTabsInTabModel.add(mTab);
+        initializeTabState(mTabsInTabModel);
 
         // Verifies that the entry1 and its tile aren't updated.
-        assertEquals(Tab.INVALID_TAB_ID, entry1.getLocalTabId());
-        verify(mTabObserverCallback, never()).onResult(any());
+        verifyClickingNotMatchedTile(
+                entry1,
+                tile1,
+                ClickInfo.HISTORY_SINGLE_FIRST,
+                /* initialClickCount= */ 1,
+                /* isTileUpdated= */ true,
+                "www.two.com \u2022 Device Source");
+    }
 
-        // The click listener still for a URL click, not local Tab click.
-        tile1.performClick();
-        Assert.assertEquals(2, mClickCount);
-        Assert.assertEquals(Tab.INVALID_TAB_ID, mLastClickEntry.getLocalTabId());
+    @Test
+    @SmallTest
+    public void testHistoryDataTwoTiles_FirstMatchesTrackingTab() {
+        initModuleView();
+        when(mTabModelSelector.isTabStateInitialized()).thenReturn(false);
+
+        SuggestionEntry[] entries =
+                createTwoHistoryTiles(
+                        mTrackingTab.getUrl(),
+                        mTab.getUrl(),
+                        /* needMatchLocalTab1= */ true,
+                        /* needMatchLocalTab2= */ true);
+
+        assertTrue(entries[0].url.equals(mTrackingTab.getUrl()));
+        assertTrue(entries[0].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[0]);
+        assertFalse(entries[1].url.equals(mTrackingTab.getUrl()));
+        assertTrue(entries[1].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[1]);
+        Assert.assertEquals(0, mTileContainerView.getChildCount());
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
+
+        // Renders tiles:
+        mModuleView.setSuggestionBundle(mSuggestionBundle);
+        verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
+        // There is an extra view between the two tile views.
+        Assert.assertEquals(3, mTileContainerView.getChildCount());
+
+        // Verifies that entries[0] has been updated to match the tracking Tab.
+        TabResumptionTileView tile1 = (TabResumptionTileView) mTileContainerView.getChildAt(0);
+        verifyTileMatchesALocalTab(
+                entries[0],
+                mTrackingTab,
+                tile1,
+                /* shouldUpdateModuleShowConfig= */ false,
+                /* expectedModuleShowConfig= */ null,
+                ClickInfo.LOCAL_DOUBLE_ANY,
+                /* initialClickCount= */ 0,
+                "www.blue.com");
+
+        // Sets the TabModel to make entries[1] doesn't match any Tab in the model.
+        mTabsInTabModel.add(mTrackingTab);
+        initializeTabState(mTabsInTabModel);
+
+        // Verifies that the entries[1] and its tile are updated  after the tab state initialization
+        // is completed.
+        TabResumptionTileView tile2 = (TabResumptionTileView) mTileContainerView.getChildAt(2);
+        verifyNotMatchedTileIsUpdated(
+                entries[1],
+                tile2,
+                /* shouldUpdateModuleShowConfig= */ true,
+                ModuleShowConfig.DOUBLE_TILE_LOCAL_HISTORY,
+                ClickInfo.HISTORY_DOUBLE_ANY,
+                /* initialClickCount= */ 1);
+    }
+
+    @Test
+    @SmallTest
+    public void testHistoryDataTwoTiles_SecondMatchesTrackingTab() throws Exception {
+        initModuleView();
+
+        when(mTabModelSelector.isTabStateInitialized()).thenReturn(false);
+        SuggestionEntry[] entries =
+                createTwoHistoryTiles(
+                        JUnitTestGURLs.URL_2,
+                        mTrackingTab.getUrl(),
+                        /* needMatchLocalTab1= */ true,
+                        /* needMatchLocalTab2= */ true);
+
+        assertFalse(entries[0].url.equals(mTrackingTab.getUrl()));
+        assertTrue(entries[0].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[0]);
+        assertTrue(entries[1].url.equals(mTrackingTab.getUrl()));
+        assertTrue(entries[1].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[1]);
+        assertEquals(2, mSuggestionBundle.entries.size());
+        Assert.assertEquals(0, mTileContainerView.getChildCount());
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
+
+        // Renders tiles:
+        mModuleView.setSuggestionBundle(mSuggestionBundle);
+        verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
+        // There is an extra view between the two tile views.
+        Assert.assertEquals(3, mTileContainerView.getChildCount());
+
+        // Verifies that entries[1] has been updated to match the tracking Tab.
+        TabResumptionTileView tile2 = (TabResumptionTileView) mTileContainerView.getChildAt(2);
+        verifyTileMatchesALocalTab(
+                entries[1],
+                mTrackingTab,
+                tile2,
+                /* shouldUpdateModuleShowConfig= */ false,
+                /* expectedModuleShowConfig= */ null,
+                ClickInfo.LOCAL_DOUBLE_ANY,
+                /* initialClickCount= */ 0,
+                "www.blue.com");
+
+        // Sets the TabModel to make entries[0] doesn't match any Tab in the model.
+        mTabsInTabModel.add(mTrackingTab);
+        initializeTabState(mTabsInTabModel);
+
+        // Verifies that the entries[0] and its tile are updated after the tab state initialization
+        // is completed.
+        TabResumptionTileView tile1 = (TabResumptionTileView) mTileContainerView.getChildAt(0);
+        verifyNotMatchedTileIsUpdated(
+                entries[0],
+                tile1,
+                /* shouldUpdateModuleShowConfig= */ true,
+                ModuleShowConfig.DOUBLE_TILE_LOCAL_HISTORY,
+                ClickInfo.HISTORY_DOUBLE_ANY,
+                /* initialClickCount= */ 1);
+    }
+
+    @Test
+    @SmallTest
+    public void testHistoryDataTwoTiles_BothMatchLocalTabs() {
+        initModuleView();
+
+        when(mTabModelSelector.isTabStateInitialized()).thenReturn(false);
+        SuggestionEntry[] entries =
+                createTwoHistoryTiles(
+                        mTrackingTab.getUrl(),
+                        mTab.getUrl(),
+                        /* needMatchLocalTab1= */ true,
+                        /* needMatchLocalTab2= */ true);
+
+        assertTrue(entries[0].url.equals(mTrackingTab.getUrl()));
+        assertTrue(entries[0].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[0]);
+        assertTrue(entries[1].url.equals(mTab.getUrl()));
+        assertTrue(entries[1].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[1]);
+        Assert.assertEquals(0, mTileContainerView.getChildCount());
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
+
+        // Renders tiles:
+        mModuleView.setSuggestionBundle(mSuggestionBundle);
+        verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
+        // There is an extra view between the two tile views.
+        Assert.assertEquals(3, mTileContainerView.getChildCount());
+
+        // Verifies that entries[0] has been updated to match the tracking Tab.
+        TabResumptionTileView tile1 = (TabResumptionTileView) mTileContainerView.getChildAt(0);
+        verifyTileMatchesALocalTab(
+                entries[0],
+                mTrackingTab,
+                tile1,
+                /* shouldUpdateModuleShowConfig= */ false,
+                /* expectedModuleShowConfig= */ null,
+                ClickInfo.LOCAL_DOUBLE_ANY,
+                /* initialClickCount= */ 0,
+                "www.blue.com");
+
+        // Verifies that tiles2 still needs to be matched and isn't updated.
+        TabResumptionTileView tile2 = (TabResumptionTileView) mTileContainerView.getChildAt(2);
+        verifyClickingNotMatchedTile(
+                entries[1],
+                tile2,
+                ClickInfo.HISTORY_DOUBLE_ANY,
+                /* initialClickCount= */ 1,
+                /* isTileUpdated= */ false,
+                "www.one.com \u2022 Device Source");
+
+        // Sets the TabModel to make entries[1] match the other Tab in the model.
+        mTabsInTabModel.add(mTrackingTab);
+        mTabsInTabModel.add(mTab);
+        initializeTabState(mTabsInTabModel);
+
+        // Verifies that the entries[1] matches mTab after the tab state initialization is
+        // completed.
+        verifyTileMatchesALocalTab(
+                entries[1],
+                mTab,
+                tile2,
+                /* shouldUpdateModuleShowConfig= */ true,
+                ModuleShowConfig.DOUBLE_TILE_LOCAL_LOCAL,
+                ClickInfo.LOCAL_DOUBLE_ANY,
+                /* initialClickCount= */ 2,
+                "www.one.com");
+    }
+
+    @Test
+    @SmallTest
+    public void testHistoryDataTwoTiles_NoneMatchLocalTabs() {
+        initModuleView();
+
+        when(mTabModelSelector.isTabStateInitialized()).thenReturn(false);
+        SuggestionEntry[] entries =
+                createTwoHistoryTiles(
+                        JUnitTestGURLs.URL_2,
+                        JUnitTestGURLs.URL_3,
+                        /* needMatchLocalTab1= */ true,
+                        /* needMatchLocalTab2= */ true);
+
+        assertFalse(entries[0].url.equals(mTrackingTab.getUrl()));
+        assertTrue(entries[0].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[0]);
+        assertFalse(entries[1].url.equals(mTrackingTab.getUrl()));
+        assertTrue(entries[1].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[1]);
+        Assert.assertEquals(0, mTileContainerView.getChildCount());
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
+
+        // Renders tiles:
+        mModuleView.setSuggestionBundle(mSuggestionBundle);
+        verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
+
+        // There is an extra view between the two tile views.
+        Assert.assertEquals(3, mTileContainerView.getChildCount());
+
+        // Verifies that no entries are updated.
+        assertEquals(Tab.INVALID_TAB_ID, entries[1].getLocalTabId());
+        assertTrue(entries[1].getNeedMatchLocalTab());
+        assertEquals(Tab.INVALID_TAB_ID, entries[0].getLocalTabId());
+        assertTrue(entries[0].getNeedMatchLocalTab());
+        verify(mTabObserverCallback, never()).onResult(eq(mTrackingTab));
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
+
+        // Sets the TabModel to make both entries don't match any Tab in the model.
+        mTabsInTabModel.add(mTrackingTab);
+        initializeTabState(mTabsInTabModel);
+
+        // Verifies that both entries are updated after the tab state initialization is completed.
+        TabResumptionTileView tile1 = (TabResumptionTileView) mTileContainerView.getChildAt(0);
+        verifyNotMatchedTileIsUpdated(
+                entries[0],
+                tile1,
+                /* shouldUpdateModuleShowConfig= */ true,
+                ModuleShowConfig.DOUBLE_TILE_HISTORY_HISTORY,
+                ClickInfo.HISTORY_DOUBLE_ANY,
+                /* initialClickCount= */ 0);
+
+        TabResumptionTileView tile2 = (TabResumptionTileView) mTileContainerView.getChildAt(2);
+        verifyNotMatchedTileIsUpdated(
+                entries[1],
+                tile2,
+                /* shouldUpdateModuleShowConfig= */ true,
+                ModuleShowConfig.DOUBLE_TILE_HISTORY_HISTORY,
+                ClickInfo.HISTORY_DOUBLE_ANY,
+                /* initialClickCount= */ 1);
+    }
+
+    @Test
+    @SmallTest
+    public void testHistoryDataTwoTiles_FirstMatchedTrackingTab_SecondDoesNotNeedToMatch() {
+        initModuleView();
+
+        when(mTabModelSelector.isTabStateInitialized()).thenReturn(false);
+        SuggestionEntry[] entries =
+                createTwoHistoryTiles(
+                        JUnitTestGURLs.BLUE_1,
+                        JUnitTestGURLs.URL_2,
+                        /* needMatchLocalTab1= */ true,
+                        /* needMatchLocalTab2= */ false);
+
+        assertTrue(entries[0].url.equals(mTrackingTab.getUrl()));
+        assertTrue(entries[0].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[0]);
+        assertFalse(entries[1].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[1]);
+        Assert.assertEquals(0, mTileContainerView.getChildCount());
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
+
+        // Renders tiles:
+        mModuleView.setSuggestionBundle(mSuggestionBundle);
+        verify(mTabModelSelector, never()).addObserver(mTabModelSelectorObserverCaptor.capture());
+        // There is an extra view between the two tile views.
+        Assert.assertEquals(3, mTileContainerView.getChildCount());
+
+        // Verifies that entries[0] has been updated to match the tracking Tab.
+        TabResumptionTileView tile1 = (TabResumptionTileView) mTileContainerView.getChildAt(0);
+        verifyTileMatchesALocalTab(
+                entries[0],
+                mTrackingTab,
+                tile1,
+                /* shouldUpdateModuleShowConfig= */ true,
+                ModuleShowConfig.DOUBLE_TILE_LOCAL_HISTORY,
+                ClickInfo.LOCAL_DOUBLE_ANY,
+                /* initialClickCount= */ 0,
+                "www.blue.com");
+
+        // Simulate click on the second tile view which doesn't need to match a local Tab.
+        TabResumptionTileView tile2 = (TabResumptionTileView) mTileContainerView.getChildAt(2);
+        verifyClickingNotMatchedTile(
+                entries[1],
+                tile2,
+                ClickInfo.HISTORY_DOUBLE_ANY,
+                /* initialClickCount= */ 1,
+                /* isTileUpdated= */ true,
+                "www.two.com \u2022 Device Source");
+    }
+
+    @Test
+    @SmallTest
+    public void testHistoryDataTwoTiles_FirstDoesNotNeedToMatch_SecondMatchesLocalTab() {
+        initModuleView();
+
+        when(mTabModelSelector.isTabStateInitialized()).thenReturn(false);
+        SuggestionEntry[] entries =
+                createTwoHistoryTiles(
+                        JUnitTestGURLs.URL_2,
+                        mTab.getUrl(),
+                        /* needMatchLocalTab1= */ false,
+                        /* needMatchLocalTab2= */ true);
+
+        assertFalse(entries[0].url.equals(mTrackingTab.getUrl()));
+        assertFalse(entries[0].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[0]);
+        assertTrue(entries[1].url.equals(mTab.getUrl()));
+        assertTrue(entries[1].getNeedMatchLocalTab());
+        mSuggestionBundle.entries.add(entries[1]);
+        Assert.assertEquals(0, mTileContainerView.getChildCount());
+
+        // Renders tiles:
+        mModuleView.setSuggestionBundle(mSuggestionBundle);
+        verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
+        // There is an extra view between the two tile views.
+        Assert.assertEquals(3, mTileContainerView.getChildCount());
+        verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
+
+        // Sets the TabModel to make entries[1] match mTab.
+        mTabsInTabModel.add(mTrackingTab);
+        mTabsInTabModel.add(mTab);
+        initializeTabState(mTabsInTabModel);
+
+        // Verifies that entries[1] has been updated to match the mTab.
+        TabResumptionTileView tile2 = (TabResumptionTileView) mTileContainerView.getChildAt(2);
+        verifyTileMatchesALocalTab(
+                entries[1],
+                mTab,
+                tile2,
+                /* shouldUpdateModuleShowConfig= */ true,
+                ModuleShowConfig.DOUBLE_TILE_LOCAL_HISTORY,
+                ClickInfo.LOCAL_DOUBLE_ANY,
+                /* initialClickCount= */ 0,
+                "www.one.com");
+
+        // Simulate click on the entries[0] which doesn't match a local Tab.
+        TabResumptionTileView tile1 = (TabResumptionTileView) mTileContainerView.getChildAt(0);
+        verifyClickingNotMatchedTile(
+                entries[0],
+                tile1,
+                ClickInfo.HISTORY_DOUBLE_ANY,
+                /* initialClickCount= */ 1,
+                /* isTileUpdated= */ true,
+                "www.two.com \u2022 Device Source");
     }
 
     private void initModuleView() {
@@ -1169,6 +1534,160 @@
         mModuleView.setTabModelSelectorSupplier(mTabModelSelectorSupplier);
         mModuleView.setTabObserverCallback(mTabObserverCallback);
         mModuleView.setTrackingTab(mTrackingTab);
+        mModuleView.setOnModuleShowConfigFinalizedCallback(mOnModuleShowConfigFinalizedCallback);
         mTileContainerView = mModuleView.getTileContainerViewForTesting();
     }
+
+    /**
+     * Verifies the suggestionEntry and click listener have been updated for a tile which match a
+     * local Tab.
+     *
+     * @param entry The {@link SuggestionEntry} instance.
+     * @param matchedTab The matched Tab found.
+     * @param tile The {@link TabResumptionTileView} instance.
+     * @param shouldUpdateModuleShowConfig Whether the callback to update ModuleShowConfig should be
+     *     called.
+     * @param expectedModuleShowConfig The expected type of ModuleShowConfig.
+     * @param expectedClickInfo The expected clickInfo to be recorded when clicking the tile.
+     * @param initialClickCount The initial count of a click.
+     * @param expectedPostInfoText The expected post info text to display.
+     */
+    private void verifyTileMatchesALocalTab(
+            SuggestionEntry entry,
+            Tab matchedTab,
+            View tile,
+            boolean shouldUpdateModuleShowConfig,
+            @Nullable @ModuleShowConfig Integer expectedModuleShowConfig,
+            @ClickInfo int expectedClickInfo,
+            int initialClickCount,
+            String expectedPostInfoText) {
+        // Verifies that entry has been updated to match the matchedTab.
+        assertEquals(matchedTab.getId(), entry.getLocalTabId());
+        assertFalse(entry.getNeedMatchLocalTab());
+        verify(mTabObserverCallback).onResult(eq(matchedTab));
+        if (shouldUpdateModuleShowConfig) {
+            // Verifies that the callback to update the ModuleShowConfig is called.
+            verify(mOnModuleShowConfigFinalizedCallback).onResult(expectedModuleShowConfig);
+        } else {
+            verify(mOnModuleShowConfigFinalizedCallback, never()).onResult(anyInt());
+        }
+
+        // Verifies that the device info is removed for the tile.
+        if (tile instanceof LocalTileView) {
+            Assert.assertEquals(
+                    expectedPostInfoText,
+                    ((TextView) tile.findViewById(R.id.tab_url_view)).getText());
+        } else {
+            Assert.assertEquals(
+                    expectedPostInfoText,
+                    ((TextView) tile.findViewById(R.id.tile_post_info_text)).getText());
+        }
+
+        // Simulate click on the tile view which matches the matchedTab.
+        String histogramName = "MagicStack.Clank.TabResumption.ClickInfo";
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(histogramName, expectedClickInfo)
+                        .build();
+        Assert.assertEquals(initialClickCount, mClickCount);
+        tile.performClick();
+        Assert.assertEquals(initialClickCount + 1, mClickCount);
+        Assert.assertEquals(matchedTab.getId(), mLastClickEntry.getLocalTabId());
+        histogramWatcher.assertExpected();
+    }
+
+    /**
+     * Verifies that the suggestionEntry has been updated for a tile which doesn't match any local
+     * Tab, and verifies its clicking behaviour.
+     *
+     * @param entry The {@link SuggestionEntry} instance.
+     * @param tile The {@link TabResumptionTileView} instance.
+     * @param shouldUpdateModuleShowConfig Whether the callback to update ModuleShowConfig should be
+     *     called.
+     * @param expectedModuleShowConfig The expected type of ModuleShowConfig.
+     * @param expectedClickInfo The expected clickInfo to be recorded when clicking the tile.
+     * @param initialClickCount The initial count of a click.
+     */
+    private void verifyNotMatchedTileIsUpdated(
+            SuggestionEntry entry,
+            TabResumptionTileView tile,
+            boolean shouldUpdateModuleShowConfig,
+            @Nullable @ModuleShowConfig Integer expectedModuleShowConfig,
+            @ClickInfo int expectedClickInfo,
+            int initialClickCount) {
+        // Verifies that the entry and its tile are updated.
+        assertEquals(Tab.INVALID_TAB_ID, entry.getLocalTabId());
+        assertFalse(entry.getNeedMatchLocalTab());
+        if (shouldUpdateModuleShowConfig) {
+            // Verifies that the callback to update the ModuleShowConfig is called.
+            verify(mOnModuleShowConfigFinalizedCallback).onResult(expectedModuleShowConfig);
+        } else {
+            verify(mOnModuleShowConfigFinalizedCallback).onResult(anyInt());
+        }
+
+        // Simulate click on the tile view which doesn't match any local Tab.
+        String histogramName = "MagicStack.Clank.TabResumption.ClickInfo";
+        HistogramWatcher histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(histogramName, expectedClickInfo)
+                        .build();
+        Assert.assertEquals(initialClickCount, mClickCount);
+        tile.performClick();
+        Assert.assertEquals(initialClickCount + 1, mClickCount);
+        Assert.assertEquals(Tab.INVALID_TAB_ID, mLastClickEntry.getLocalTabId());
+        histogramWatcher.assertExpected();
+    }
+
+    /**
+     * Verifies the clicking behaviour on a tile which doesn't need to match any local tab.
+     *
+     * @param entry The {@link SuggestionEntry} instance.
+     * @param tile The {@link TabResumptionTileView} instance.
+     * @param expectedClickInfo The expected clickInfo to be recorded when clicking the tile.
+     * @param initialClickCount The initial count of a click.
+     * @param isTileUpdated Whether the SuggestionEntry of the tile is expected to be updated.
+     * @param expectedPostInfoText The expected post info text to display.
+     */
+    private void verifyClickingNotMatchedTile(
+            SuggestionEntry entry,
+            TabResumptionTileView tile,
+            @ClickInfo int expectedClickInfo,
+            int initialClickCount,
+            boolean isTileUpdated,
+            String expectedPostInfoText) {
+        // Verifies that the entry and its tile are updated.
+        assertEquals(Tab.INVALID_TAB_ID, entry.getLocalTabId());
+        if (isTileUpdated) {
+            assertFalse(entry.getNeedMatchLocalTab());
+        }
+        // Verifies that the device info was added for the tile.
+        Assert.assertEquals(
+                expectedPostInfoText,
+                ((TextView) tile.findViewById(R.id.tile_post_info_text)).getText());
+
+        // Simulate click on the tile view which doesn't match any local Tab.
+        String histogramName = "MagicStack.Clank.TabResumption.ClickInfo";
+        HistogramWatcher histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(histogramName, expectedClickInfo)
+                        .build();
+        Assert.assertEquals(initialClickCount, mClickCount);
+        tile.performClick();
+        Assert.assertEquals(initialClickCount + 1, mClickCount);
+        Assert.assertEquals(Tab.INVALID_TAB_ID, mLastClickEntry.getLocalTabId());
+        histogramWatcher.assertExpected();
+    }
+
+    /**
+     * Adds the provided tabs to the TabModel, and notifies the onTabStateInitialized() event.
+     *
+     * @param tabs The list of Tabs to add to the TabModel.
+     */
+    private void initializeTabState(List<Tab> tabs) {
+        when(mTabModel.getCount()).thenReturn(tabs.size());
+        for (int i = 0; i < tabs.size(); i++) {
+            when(mTabModel.getTabAt(i)).thenReturn(tabs.get(i));
+        }
+        mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+    }
 }
diff --git a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TestSupportExtended.java b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TestSupportExtended.java
index 5981bdb..7b70acc 100644
--- a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TestSupportExtended.java
+++ b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TestSupportExtended.java
@@ -58,4 +58,71 @@
         verify(mModuleDelegate, times(expectOnDataFetchFailedCalls)).onDataFetchFailed(anyInt());
         verify(mModuleDelegate, times(expectRemoveModuleCalls)).removeModule(anyInt());
     }
+
+    protected SuggestionEntry createHistorySuggestion(boolean needMatchLocalTab) {
+        return new SuggestionEntry(
+                SuggestionEntryType.HISTORY,
+                "Source not to be shown",
+                JUnitTestGURLs.URL_1,
+                "Tab Title",
+                makeTimestamp(24 - 3, 0, 0),
+                Tab.INVALID_TAB_ID,
+                /* appId= */ null,
+                /* reasonToShowTab= */ null,
+                /* needMatchLocalTab= */ needMatchLocalTab);
+    }
+
+    protected SuggestionEntry createLocalSuggestion(int tabId) {
+        return new SuggestionEntry(
+                SuggestionEntryType.LOCAL_TAB,
+                /* sourceName= */ "",
+                JUnitTestGURLs.URL_1,
+                "Tab Title",
+                makeTimestamp(24 - 3, 0, 0),
+                tabId,
+                /* appId= */ null,
+                /* reasonToShowTab= */ null,
+                /* needMatchLocalTab= */ false);
+    }
+
+    protected SuggestionEntry createForeignSuggestion(boolean needMatchLocalTab) {
+        return new SuggestionEntry(
+                SuggestionEntryType.FOREIGN_TAB,
+                "Source not to be shown",
+                JUnitTestGURLs.URL_1,
+                "Tab Title",
+                makeTimestamp(24 - 3, 0, 0),
+                Tab.INVALID_TAB_ID,
+                /* appId= */ null,
+                /* reasonToShowTab= */ null,
+                /* needMatchLocalTab= */ needMatchLocalTab);
+    }
+
+    protected SuggestionEntry[] createTwoHistoryTiles(
+            GURL url1, GURL url2, boolean needMatchLocalTab1, boolean needMatchLocalTab2) {
+        SuggestionEntry[] entries = new SuggestionEntry[2];
+        entries[0] =
+                new SuggestionEntry(
+                        SuggestionEntryType.HISTORY,
+                        "Device Source",
+                        url1,
+                        "Tab Title",
+                        makeTimestamp(24 - 3, 0, 0),
+                        Tab.INVALID_TAB_ID,
+                        /* appId= */ null,
+                        /* reasonToShowTab= */ null,
+                        /* needMatchLocalTab= */ needMatchLocalTab1);
+        entries[1] =
+                new SuggestionEntry(
+                        SuggestionEntryType.HISTORY,
+                        "Device Source",
+                        url2,
+                        "Tab Title",
+                        makeTimestamp(24 - 3, 0, 0),
+                        Tab.INVALID_TAB_ID,
+                        /* appId= */ null,
+                        /* reasonToShowTab= */ null,
+                        /* needMatchLocalTab= */ needMatchLocalTab2);
+        return entries;
+    }
 }
diff --git a/chrome/browser/tab_resumption/visited_url_ranking_backend.cc b/chrome/browser/tab_resumption/visited_url_ranking_backend.cc
index 7fa6f8a..314dca3 100644
--- a/chrome/browser/tab_resumption/visited_url_ranking_backend.cc
+++ b/chrome/browser/tab_resumption/visited_url_ranking_backend.cc
@@ -162,7 +162,7 @@
                                      : SuggestionEntryType::kForeignTab)),
                     base::android::ConvertUTF8ToJavaString(
                         env_,
-                        tab_data.last_active_tab.session_name.value_or("?")),
+                        tab_data.last_active_tab.session_name.value_or("")),
                     url::GURLAndroid::FromNativeGURL(
                         env_, tab_data.last_active_tab.visit.url),
                     base::android::ConvertUTF16ToJavaString(
@@ -185,7 +185,7 @@
                     env_, jobj_,
                     JniIntWrapper(
                         static_cast<int>(SuggestionEntryType::kHistory)),
-                    base::android::ConvertUTF8ToJavaString(env_, "?"),
+                    base::android::ConvertUTF8ToJavaString(env_, ""),
                     url::GURLAndroid::FromNativeGURL(
                         env_, history_data.last_visited.url_row.url()),
                     base::android::ConvertUTF16ToJavaString(
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc
index 680b8e5..2b7e98f 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc
@@ -242,12 +242,10 @@
   manager_->client().HideAutofillSuggestions(
       SuggestionHidingReason::kOverlappingWithTouchToFillSurface);
   if (absl::get_if<std::vector<CreditCard>>(&dry_run.items_to_suggest)) {
-    manager_->DidShowSuggestions(
-        std::vector<SuggestionType>({SuggestionType::kCreditCardEntry}), form,
-        field);
+    manager_->DidShowSuggestions({SuggestionType::kCreditCardEntry}, form,
+                                 field);
   } else {
-    manager_->DidShowSuggestions(
-        std::vector<SuggestionType>({SuggestionType::kIbanEntry}), form, field);
+    manager_->DidShowSuggestions({SuggestionType::kIbanEntry}, form, field);
   }
   return true;
 }
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
index 96dfc8d1..9f4e4da 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
@@ -1,6 +1,7 @@
 // Copyright 2022 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 #include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h"
 
 #include "base/test/metrics/histogram_tester.h"
@@ -149,7 +150,7 @@
                const AutofillTriggerDetails& trigger_details));
   MOCK_METHOD(void,
               DidShowSuggestions,
-              (base::span<const SuggestionType> shown_suggestions_types,
+              (DenseSet<SuggestionType> shown_suggestion_types,
                const FormData& form,
                const FormFieldData& field),
               (override));
diff --git a/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_unittest.cc b/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_unittest.cc
index 2fd32c9..61c2267 100644
--- a/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_unittest.cc
+++ b/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_unittest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/trusted_vault/trusted_vault_service_factory.h"
 #include "chrome/common/trusted_vault_encryption_keys_extension.mojom.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/safe_browsing/core/common/features.h"
 #include "components/site_isolation/features.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/site_isolation_policy.h"
@@ -36,14 +37,18 @@
     // on Android so that ApplyGlobalIsolatedOrigins() takes effect regardless
     // of available memory when running the test (otherwise low-memory bots may
     // run into test failures).
-    feature_list_.InitAndEnableFeatureWithParameters(
-        site_isolation::features::kSiteIsolationMemoryThresholds,
-        {{site_isolation::features::
-              kStrictSiteIsolationMemoryThresholdParamName,
-          "0"},
-         {site_isolation::features::
-              kPartialSiteIsolationMemoryThresholdParamName,
-          "0"}});
+    // TODO(crbug.com/362466866): Instead of disabling the
+    // `kSafetyHubAbusiveNotificationRevocation` feature, find a stable
+    // fix such that the tests still pass when the feature is enabled.
+    feature_list_.InitWithFeaturesAndParameters(
+        {{site_isolation::features::kSiteIsolationMemoryThresholds,
+          {{site_isolation::features::
+                kStrictSiteIsolationMemoryThresholdParamName,
+            "0"},
+           {site_isolation::features::
+                kPartialSiteIsolationMemoryThresholdParamName,
+            "0"}}}},
+        {safe_browsing::kSafetyHubAbusiveNotificationRevocation});
   }
 
   ~TrustedVaultEncryptionKeysTabHelperTest() override = default;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 4d863b9..9e9c056 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -64,6 +64,7 @@
     "autofill/autofill_popup_hide_helper.h",
     "autofill/autofill_popup_view.h",
     "autofill/autofill_popup_view_delegate.h",
+    "autofill/autofill_suggestion_controller.cc",
     "autofill/autofill_suggestion_controller.h",
     "autofill/autofill_suggestion_controller_utils.cc",
     "autofill/autofill_suggestion_controller_utils.h",
@@ -1099,14 +1100,12 @@
       "browser_view_prefs.cc",
       "browser_view_prefs.h",
       "browser_window.h",
-      "browser_window/browser_window_features.cc",
       "browser_window_state.cc",
       "browser_window_state.h",
       "chrome_pages.cc",
       "chrome_pages.h",
       "collected_cookies_infobar_delegate.cc",
       "collected_cookies_infobar_delegate.h",
-      "commerce/commerce_prompt.h",
       "confirm_bubble_model.cc",
       "confirm_bubble_model.h",
       "content_settings/content_setting_bubble_model.cc",
@@ -1995,6 +1994,7 @@
       "//chrome/browser/ui/apps",
       "//chrome/browser/ui/apps:impl",
       "//chrome/browser/ui/browser_window",
+      "//chrome/browser/ui/browser_window:impl",
       "//chrome/browser/ui/color:color_headers",
       "//chrome/browser/ui/color:mixers",
       "//chrome/browser/ui/exclusive_access",
@@ -2122,6 +2122,7 @@
       # //c/b/ui/apps/directory_access_confirmation_dialog.cc includes
       # //c/b/iu/tab_modal_confirm_dialog_delegate.h.
       "//chrome/browser/ui/apps:impl",
+      "//chrome/browser/ui/browser_window:impl",
       "//chrome/browser/ui/exclusive_access",
       "//chrome/browser/ui/toasts:impl",
       "//chrome/browser/ui/views/toolbar",
@@ -2253,18 +2254,6 @@
       "webui/nearby_share/nearby_share_dialog_ui.h",
       "webui/nearby_share/shared_resources.cc",
       "webui/nearby_share/shared_resources.h",
-      "webui/signin/ash/inline_login_dialog.cc",
-      "webui/signin/ash/inline_login_dialog.h",
-      "webui/signin/ash/inline_login_dialog_onboarding.cc",
-      "webui/signin/ash/inline_login_dialog_onboarding.h",
-      "webui/signin/ash/inline_login_handler_impl.cc",
-      "webui/signin/ash/inline_login_handler_impl.h",
-      "webui/signin/ash/inline_login_handler_modal_delegate.cc",
-      "webui/signin/ash/inline_login_handler_modal_delegate.h",
-      "webui/signin/ash/signin_helper.cc",
-      "webui/signin/ash/signin_helper.h",
-      "webui/signin/ash/user_cloud_signin_restriction_policy_fetcher.cc",
-      "webui/signin/ash/user_cloud_signin_restriction_policy_fetcher.h",
     ]
 
     deps += [
@@ -2628,6 +2617,7 @@
       "//chrome/browser/ui/webui/ash/smb_shares",
       "//chrome/browser/ui/webui/ash/sys_internals",
       "//chrome/browser/ui/webui/ash/vc_tray_tester",
+      "//chrome/browser/ui/webui/signin/ash",
       "//chrome/browser/web_applications",
       "//chrome/browser/webshare:storage",
       "//chrome/services/file_util/public/cpp",
@@ -3064,6 +3054,7 @@
       "//chrome/browser/ui/webui/ash/settings/search",
       "//chrome/browser/ui/webui/ash/skyvault",
       "//chrome/browser/ui/webui/ash/smb_shares",
+      "//chrome/browser/ui/webui/signin/ash",
     ]
 
     if (is_chrome_branded) {
@@ -4014,18 +4005,6 @@
       "autofill/update_address_bubble_controller.cc",
       "autofill/update_address_bubble_controller.h",
       "bubble_anchor_util.h",
-      "commerce/commerce_page_action_controller.cc",
-      "commerce/commerce_page_action_controller.h",
-      "commerce/commerce_ui_tab_helper.cc",
-      "commerce/commerce_ui_tab_helper.h",
-      "commerce/discounts_page_action_controller.cc",
-      "commerce/discounts_page_action_controller.h",
-      "commerce/price_tracking_page_action_controller.cc",
-      "commerce/price_tracking_page_action_controller.h",
-      "commerce/product_specifications_entry_point_controller.cc",
-      "commerce/product_specifications_entry_point_controller.h",
-      "commerce/product_specifications_page_action_controller.cc",
-      "commerce/product_specifications_page_action_controller.h",
       "dialogs/outdated_upgrade_bubble.cc",
       "dialogs/outdated_upgrade_bubble.h",
       "plus_addresses/plus_address_creation_controller_desktop.h",
@@ -4040,16 +4019,6 @@
       "sharing_hub/screenshot/screenshot_captured_bubble_controller.h",
       "sharing_hub/sharing_hub_bubble_controller.h",
       "sharing_hub/sharing_hub_bubble_view.h",
-      "toolbar_controller_util.cc",
-      "toolbar_controller_util.h",
-      "views/send_tab_to_self/send_tab_to_self_bubble.cc",
-      "views/send_tab_to_self/send_tab_to_self_bubble_controller.cc",
-      "views/send_tab_to_self/send_tab_to_self_bubble_controller.h",
-      "views/send_tab_to_self/send_tab_to_self_bubble_view.cc",
-      "views/send_tab_to_self/send_tab_to_self_bubble_view.h",
-
-      # This test header is included because it contains forward declarations
-      # needed for "friend" statements for use in tests.
       "side_search/side_search_config.cc",
       "side_search/side_search_config.h",
       "side_search/side_search_metrics.cc",
@@ -4066,7 +4035,8 @@
       "sync/one_click_signin_links_delegate.h",
       "sync/one_click_signin_links_delegate_impl.cc",
       "sync/one_click_signin_links_delegate_impl.h",
-      "translate/translate_bubble_test_utils.h",
+      "toolbar_controller_util.cc",
+      "toolbar_controller_util.h",
       "views/accelerator_table.cc",
       "views/accelerator_table.h",
       "views/accessibility/caption_bubble_context_views.cc",
@@ -4939,8 +4909,13 @@
       "views/safe_browsing/tailored_security_unconsented_modal.h",
       "views/send_tab_to_self/manage_account_devices_link_view.cc",
       "views/send_tab_to_self/manage_account_devices_link_view.h",
+      "views/send_tab_to_self/send_tab_to_self_bubble.cc",
+      "views/send_tab_to_self/send_tab_to_self_bubble_controller.cc",
+      "views/send_tab_to_self/send_tab_to_self_bubble_controller.h",
       "views/send_tab_to_self/send_tab_to_self_bubble_device_button.cc",
       "views/send_tab_to_self/send_tab_to_self_bubble_device_button.h",
+      "views/send_tab_to_self/send_tab_to_self_bubble_view.cc",
+      "views/send_tab_to_self/send_tab_to_self_bubble_view.h",
       "views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.cc",
       "views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.h",
       "views/send_tab_to_self/send_tab_to_self_promo_bubble_view.cc",
@@ -5289,6 +5264,10 @@
       "webauthn/webauthn_ui_helpers.h",
       "webui/side_panel/customize_chrome/customize_chrome_section.h",
       "window_name_prompt/window_name_prompt.cc",
+
+      # This test header is included because it contains forward declarations
+      # needed for "friend" statements for use in tests.
+      "translate/translate_bubble_test_utils.h",
     ]
 
     sources += get_target_outputs(":webui_name_variants")
@@ -5308,6 +5287,8 @@
       "//chrome/browser/ui/actions:actions",
       "//chrome/browser/ui/actions:actions_headers",
       "//chrome/browser/ui/autofill/payments",
+      "//chrome/browser/ui/commerce",
+      "//chrome/browser/ui/commerce:impl",
       "//chrome/browser/ui/views",
       "//chrome/browser/ui/views/bubble",
       "//components/commerce/core:metrics",
@@ -5364,6 +5345,7 @@
     allow_circular_includes_from += [
       "//chrome/browser/ui/views",
       "//chrome/browser/ui/views/bubble",
+      "//chrome/browser/ui/commerce:impl",
     ]
 
     if (is_chrome_branded && !is_android) {
diff --git a/chrome/browser/ui/android/plus_addresses/BUILD.gn b/chrome/browser/ui/android/plus_addresses/BUILD.gn
index 0c30eca..d587d96 100644
--- a/chrome/browser/ui/android/plus_addresses/BUILD.gn
+++ b/chrome/browser/ui/android/plus_addresses/BUILD.gn
@@ -28,6 +28,7 @@
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationBottomSheetContent.java",
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationCoordinator.java",
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationDelegate.java",
+    "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationErrorStateInfo.java",
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationMediator.java",
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridge.java",
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusProfile.java",
@@ -60,6 +61,7 @@
     "java/res/color/refresh_plus_address_icon_tint_list.xml",
     "java/res/drawable/proposed_plus_address_background.xml",
     "java/res/layout/all_plus_addresses_bottom_sheet.xml",
+    "java/res/layout/plus_address_creation_error_state.xml",
     "java/res/layout/plus_address_creation_normal_state.xml",
     "java/res/layout/plus_address_creation_prompt.xml",
     "java/res/layout/plus_profile_info_view.xml",
@@ -78,6 +80,7 @@
   sources = [
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/AllPlusAddressesBottomSheetBridge.java",
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/AllPlusAddressesBottomSheetUIInfo.java",
+    "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationErrorStateInfo.java",
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridge.java",
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusProfile.java",
     "java/src/org/chromium/chrome/browser/ui/plus_addresses/utils/PlusAddressesHelper.java",
diff --git a/chrome/browser/ui/android/plus_addresses/all_plus_addresses_bottom_sheet_view.cc b/chrome/browser/ui/android/plus_addresses/all_plus_addresses_bottom_sheet_view.cc
index fec921a..45f270e 100644
--- a/chrome/browser/ui/android/plus_addresses/all_plus_addresses_bottom_sheet_view.cc
+++ b/chrome/browser/ui/android/plus_addresses/all_plus_addresses_bottom_sheet_view.cc
@@ -90,7 +90,8 @@
     return java_object_internal_;
   }
   if (!controller_->GetNativeView() ||
-      !controller_->GetNativeView()->GetWindowAndroid()) {
+      !controller_->GetNativeView()->GetWindowAndroid() ||
+      !controller_->GetNativeView()->GetWindowAndroid()->GetJavaObject()) {
     return nullptr;  // No window attached (yet or anymore).
   }
   return java_object_internal_ = Java_AllPlusAddressesBottomSheetBridge_create(
diff --git a/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_error_state.xml b/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_error_state.xml
new file mode 100644
index 0000000..5d60cdcb
--- /dev/null
+++ b/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_error_state.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2024 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/plus_address_error_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <ImageView
+        android:id="@+id/plus_address_error_icon"
+        android:layout_width="@dimen/plus_address_title_icon_size"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="@dimen/plus_address_list_item_small_margin"
+        android:layout_marginBottom="24dp"
+        android:src="@drawable/ic_error_outline_red_24dp"
+        android:importantForAccessibility="no"
+        android:adjustViewBounds="true"
+        android:scaleType="fitXY" />
+
+    <TextView
+        android:id="@+id/plus_address_error_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAlignment="center"
+        android:layout_marginBottom="8dp"
+        style="@style/TextAppearance.Headline.Primary" />
+
+    <TextView
+        android:id="@+id/plus_address_error_description"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAlignment="center"
+        android:breakStrategy="simple"
+        android:layout_marginBottom="@dimen/list_item_default_margin"
+        android:textAppearance="@style/TextAppearance.TextMedium.Secondary"/>
+
+    <org.chromium.ui.widget.ButtonCompat
+        android:id="@+id/plus_address_error_ok_button"
+        style="@style/FilledButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAlignment="center"
+        android:layout_marginBottom="@dimen/plus_address_list_item_small_margin"
+        android:minHeight="@dimen/min_touch_target_size"
+        android:minWidth="@dimen/min_touch_target_size" />
+
+    <org.chromium.ui.widget.ButtonCompat
+        android:id="@+id/plus_address_error_cancel_button"
+        style="@style/TextButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAlignment="center"
+        android:layout_marginBottom="@dimen/autofill_bottom_sheet_spacing_extra_small"
+        android:minHeight="@dimen/min_touch_target_size"
+        android:minWidth="@dimen/min_touch_target_size" />
+
+</LinearLayout>
diff --git a/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_normal_state.xml b/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_normal_state.xml
index 99ef924..ae851753 100644
--- a/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_normal_state.xml
+++ b/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_normal_state.xml
@@ -14,7 +14,7 @@
 
     <ImageView
       android:id="@+id/plus_address_logo"
-      android:layout_width="36dp"
+      android:layout_width="@dimen/plus_address_title_icon_size"
       android:layout_height="wrap_content"
       android:layout_gravity="center_horizontal"
       android:layout_marginBottom="@dimen/list_item_default_margin"
diff --git a/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_prompt.xml b/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_prompt.xml
index 922d7dc..292aa07f 100644
--- a/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_prompt.xml
+++ b/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_prompt.xml
@@ -14,7 +14,7 @@
         android:orientation="vertical"
         android:paddingHorizontal="@dimen/dialog_padding_sides">
 
-  <ImageView
+    <ImageView
         android:id="@+id/drag_handlebar"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
@@ -26,6 +26,12 @@
         android:importantForAccessibility="no"
         app:srcCompat="@drawable/drag_handlebar" />
 
-  <include layout="@layout/plus_address_creation_normal_state" />
+    <include layout="@layout/plus_address_creation_normal_state" />
+
+    <ViewStub
+        android:id="@+id/plus_address_error_container_stub"
+        android:inflatedId="@+id/plus_address_error_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
 
 </LinearLayout>
diff --git a/chrome/browser/ui/android/plus_addresses/java/res/values/dimens.xml b/chrome/browser/ui/android/plus_addresses/java/res/values/dimens.xml
index 9f21d8f..097593f 100644
--- a/chrome/browser/ui/android/plus_addresses/java/res/values/dimens.xml
+++ b/chrome/browser/ui/android/plus_addresses/java/res/values/dimens.xml
@@ -6,7 +6,9 @@
 -->
 
 <resources xmlns:tools="http://schemas.android.com/tools">
-    <!-- Proposed plus address background rounded cornders radiums. -->
+    <!-- Plus address creation bottom sheet. -->
+    <dimen name="plus_address_title_icon_size">36dp</dimen>
+    <dimen name="plus_address_list_item_small_margin">8dp</dimen>
     <dimen name="plus_address_rounded_corner_radius">4dp</dimen>
 
     <!-- All plus addresses bottom sheet. -->
diff --git a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationBottomSheetContent.java b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationBottomSheetContent.java
index 667c4b5..6515833 100644
--- a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationBottomSheetContent.java
+++ b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationBottomSheetContent.java
@@ -11,6 +11,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewStub;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -157,20 +158,50 @@
         showLoadingIndicator();
     }
 
-    public void showError() {
-        mContentView.findViewById(R.id.proposed_plus_address_container).setVisibility(View.GONE);
-        TextViewWithClickableSpans plusAddressErrorReportView =
-                mContentView.findViewById(R.id.plus_address_modal_error_report);
-        plusAddressErrorReportView.setVisibility(View.VISIBLE);
+    /**
+     * Shows error message UI to the user.
+     *
+     * @param errorStateInfo necassary UI information to show a meaningful error message to the
+     *     user. This is {@code null} if the enhanced error UI is not enabled, otherwise this is
+     *     always not {@code null}.
+     */
+    public void showError(@Nullable PlusAddressCreationErrorStateInfo errorStateInfo) {
+        if (errorStateInfo == null) {
+            mContentView
+                    .findViewById(R.id.proposed_plus_address_container)
+                    .setVisibility(View.GONE);
+            TextViewWithClickableSpans plusAddressErrorReportView =
+                    mContentView.findViewById(R.id.plus_address_modal_error_report);
+            plusAddressErrorReportView.setVisibility(View.VISIBLE);
 
-        hideLoadingIndicator();
+            hideLoadingIndicator();
 
-        // Disable Confirm button if attempts to Confirm() fail.
-        mPlusAddressConfirmButton.setVisibility(View.VISIBLE);
-        mPlusAddressConfirmButton.setEnabled(false);
-        if (mShowingNotice) {
-            mPlusAddressCancelButton.setVisibility(View.VISIBLE);
+            // Disable Confirm button if attempts to Confirm() fail.
+            mPlusAddressConfirmButton.setVisibility(View.VISIBLE);
+            mPlusAddressConfirmButton.setEnabled(false);
+            if (mShowingNotice) {
+                mPlusAddressCancelButton.setVisibility(View.VISIBLE);
+            }
+            return;
         }
+
+        View plusAddressContent = mContentView.findViewById(R.id.plus_address_content);
+        plusAddressContent.setVisibility(View.GONE);
+
+        ViewStub errorContentStub =
+                mContentView.findViewById(R.id.plus_address_error_container_stub);
+        errorContentStub.setLayoutResource(R.layout.plus_address_creation_error_state);
+        errorContentStub.inflate();
+
+        TextView title = mContentView.findViewById(R.id.plus_address_error_title);
+        TextView description = mContentView.findViewById(R.id.plus_address_error_description);
+        Button okButton = mContentView.findViewById(R.id.plus_address_error_ok_button);
+        Button cancelButton = mContentView.findViewById(R.id.plus_address_error_cancel_button);
+
+        title.setText(errorStateInfo.getTitle());
+        description.setText(errorStateInfo.getDescription());
+        okButton.setText(errorStateInfo.getOkText());
+        cancelButton.setText(errorStateInfo.getCancelText());
     }
 
     public void hideRefreshButton() {
diff --git a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationCoordinator.java b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationCoordinator.java
index 16d80aae..a1744d1 100644
--- a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationCoordinator.java
+++ b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationCoordinator.java
@@ -66,8 +66,8 @@
         mMediator.updateProposedPlusAddress(plusAddress);
     }
 
-    public void showError() {
-        mMediator.showError();
+    public void showError(@Nullable PlusAddressCreationErrorStateInfo errorStateInfo) {
+        mMediator.showError(errorStateInfo);
     }
 
     public void hideRefreshButton() {
diff --git a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationErrorStateInfo.java b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationErrorStateInfo.java
new file mode 100644
index 0000000..7b52d5b
--- /dev/null
+++ b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationErrorStateInfo.java
@@ -0,0 +1,49 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ui.plus_addresses;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
+
+@JNINamespace("plus_addresses")
+/** Contains necessary information to show a meaningful error message to the user. */
+class PlusAddressCreationErrorStateInfo {
+    private final String mTitle;
+    private final String mDescription;
+    private final String mOkText;
+    private final String mCancelText;
+
+    @VisibleForTesting
+    @CalledByNative
+    PlusAddressCreationErrorStateInfo(
+            @JniType("std::u16string") String title,
+            @JniType("std::u16string") String description,
+            @JniType("std::u16string") String okText,
+            @JniType("std::u16string") String cancelText) {
+        mTitle = title;
+        mDescription = description;
+        mOkText = okText;
+        mCancelText = cancelText;
+    }
+
+    String getTitle() {
+        return mTitle;
+    }
+
+    String getDescription() {
+        return mDescription;
+    }
+
+    String getOkText() {
+        return mOkText;
+    }
+
+    String getCancelText() {
+        return mCancelText;
+    }
+}
diff --git a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationMediator.java b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationMediator.java
index 2c272347..89ae6f1 100644
--- a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationMediator.java
+++ b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationMediator.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.ui.plus_addresses;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.layouts.LayoutType;
@@ -79,8 +81,8 @@
         mBottomSheetContent.setProposedPlusAddress(plusAddress);
     }
 
-    void showError() {
-        mBottomSheetContent.showError();
+    void showError(@Nullable PlusAddressCreationErrorStateInfo errorStateInfo) {
+        mBottomSheetContent.showError(errorStateInfo);
     }
 
     void hideRefreshButton() {
diff --git a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridge.java b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridge.java
index 651ed927..8a51134 100644
--- a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridge.java
+++ b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridge.java
@@ -38,14 +38,16 @@
     @VisibleForTesting
     /*package*/ PlusAddressCreationViewBridge(
             long nativePlusAddressCreationPromptAndroid,
-            WindowAndroid window,
+            Activity activity,
+            BottomSheetController bottomSheetController,
+            LayoutStateProvider layoutStateProvider,
             TabModel tabModel,
             TabModelSelector tabModelSelector,
             CoordinatorFactory coordinatorFactory) {
         mNativePlusAddressCreationPromptAndroid = nativePlusAddressCreationPromptAndroid;
-        mActivity = window.getActivity().get();
-        mBottomSheetController = BottomSheetControllerProvider.from(window);
-        mLayoutStateProvider = LayoutManagerProvider.from(window);
+        mActivity = activity;
+        mBottomSheetController = bottomSheetController;
+        mLayoutStateProvider = layoutStateProvider;
         mTabModel = tabModel;
         mTabModelSelector = tabModelSelector;
         mCoordinatorFactory = coordinatorFactory;
@@ -74,13 +76,18 @@
 
     @CalledByNative
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @Nullable
     static PlusAddressCreationViewBridge create(
             long nativePlusAddressCreationPromptAndroid, WindowAndroid window, TabModel tabModel) {
         var tabModelSelector = TabModelSelectorSupplier.getValueOrNullFrom(window);
-        assert tabModelSelector != null : "No TabModelSelector available.";
+        if (tabModelSelector == null || window.getActivity().get() == null) {
+            return null;
+        }
         return new PlusAddressCreationViewBridge(
                 nativePlusAddressCreationPromptAndroid,
-                window,
+                window.getActivity().get(),
+                BottomSheetControllerProvider.from(window),
+                LayoutManagerProvider.from(window),
                 tabModel,
                 tabModelSelector,
                 PlusAddressCreationCoordinator::new);
@@ -135,10 +142,16 @@
         }
     }
 
+    /**
+     * TODO: crbug.com/354881207 - Remove `@Nullable` when enhanced error handling is launched.
+     *
+     * @param errorStateInfo necassary UI information to show a meaningful error message to the
+     *     user.
+     */
     @CalledByNative
-    void showError() {
+    void showError(@Nullable PlusAddressCreationErrorStateInfo errorStateInfo) {
         if (mNativePlusAddressCreationPromptAndroid != 0 && mCoordinator != null) {
-            mCoordinator.showError();
+            mCoordinator.showError(errorStateInfo);
         }
     }
 
diff --git a/chrome/browser/ui/android/plus_addresses/javatests/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationRenderTest.java b/chrome/browser/ui/android/plus_addresses/javatests/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationRenderTest.java
index 9abb1b6..ea18b00 100644
--- a/chrome/browser/ui/android/plus_addresses/javatests/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationRenderTest.java
+++ b/chrome/browser/ui/android/plus_addresses/javatests/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationRenderTest.java
@@ -28,6 +28,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.DoNotBatch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -58,6 +59,8 @@
 public class PlusAddressCreationRenderTest {
     private static final String MANAGE_PLUS_ADDRESSES_DESCRIPTION = "For example@gmail.com.";
     private static final String PROPOSED_PLUS_ADDRESS = "example.foo@gmail.com";
+    private static final PlusAddressCreationErrorStateInfo ERROR_STATE =
+            new PlusAddressCreationErrorStateInfo("Title", "Description", "Ok", "Cancel");
 
     @ParameterAnnotations.ClassParameter
     private static List<ParameterSet> sClassParams =
@@ -206,11 +209,11 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    public void testErrorShown() throws IOException {
+    public void testLegacyErrorScreen() throws IOException {
         openBottomSheet(MANAGE_PLUS_ADDRESSES_DESCRIPTION, /* refreshSupported= */ false);
         runOnUiThreadBlocking(
                 () -> {
-                    mCoordinator.showError();
+                    mCoordinator.showError(/* errorStateInfo= */ null);
                 });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
 
@@ -219,4 +222,21 @@
         mRenderTestRule.render(
                 bottomSheetView, "plus_address_bottom_sheet_error_shown_with_redesign");
     }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    @DisabledTest(message = "crbug.com/351911806")
+    public void testReserveErrorMessageContent() throws IOException {
+        openBottomSheet(MANAGE_PLUS_ADDRESSES_DESCRIPTION, /* refreshSupported= */ false);
+        runOnUiThreadBlocking(
+                () -> {
+                    mCoordinator.showError(ERROR_STATE);
+                });
+        BottomSheetTestSupport.waitForOpen(mBottomSheetController);
+
+        View bottomSheetView =
+                mActivityTestRule.getActivity().findViewById(R.id.plus_address_dialog);
+        mRenderTestRule.render(bottomSheetView, "plus_address_bottom_sheet_reserve_error_shown");
+    }
 }
diff --git a/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationBottomSheetContentTest.java b/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationBottomSheetContentTest.java
index 84039e1..69e924af 100644
--- a/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationBottomSheetContentTest.java
+++ b/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationBottomSheetContentTest.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.ui.plus_addresses;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -18,7 +21,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -61,6 +63,8 @@
     private static final GURL LEARN_MORE_URL = new GURL("learn.more.com");
     private static final GURL ERROR_URL = new GURL("bug.com");
     private static final boolean REFRESH_SUPPORTED = true;
+    private static final PlusAddressCreationErrorStateInfo RESERVE_ERROR_STATE =
+            new PlusAddressCreationErrorStateInfo("Title", "Description", "Ok", "Cancel");
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private PlusAddressCreationDelegate mDelegate;
@@ -104,17 +108,16 @@
         Button modalConfirmButton =
                 mBottomSheetContent.getContentView().findViewById(R.id.plus_address_confirm_button);
 
-        Assert.assertEquals(modalTitleView.getText().toString(), MODAL_TITLE);
-        Assert.assertEquals(
-                modalDescriptionView.getText().toString(), MODAL_PLUS_ADDRESS_DESCRIPTION);
-        Assert.assertEquals(
+        assertEquals(modalTitleView.getText().toString(), MODAL_TITLE);
+        assertEquals(modalDescriptionView.getText().toString(), MODAL_PLUS_ADDRESS_DESCRIPTION);
+        assertEquals(
                 modalPlusAddressPlaceholderView.getText().toString(),
                 MODAL_PROPOSED_PLUS_ADDRESS_PLACEHOLDER);
-        Assert.assertEquals(modalConfirmButton.getText().toString(), MODAL_OK);
+        assertEquals(modalConfirmButton.getText().toString(), MODAL_OK);
 
         // Validate updates to the bottomsheet.
         mBottomSheetContent.setProposedPlusAddress(MODAL_PROPOSED_PLUS_ADDRESS);
-        Assert.assertEquals(
+        assertEquals(
                 modalPlusAddressPlaceholderView.getText().toString(), MODAL_PROPOSED_PLUS_ADDRESS);
     }
 
@@ -123,10 +126,10 @@
     public void testRefreshButton_RefreshSupported() {
         ImageView refreshIcon =
                 mBottomSheetContent.getContentView().findViewById(R.id.refresh_plus_address_icon);
-        Assert.assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
+        assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
 
         mBottomSheetContent.hideRefreshButton();
-        Assert.assertEquals(refreshIcon.getVisibility(), View.GONE);
+        assertEquals(refreshIcon.getVisibility(), View.GONE);
     }
 
     @Test
@@ -147,7 +150,7 @@
                         /* refreshSupported= */ false);
         ImageView refreshIcon =
                 bottomSheetContent.getContentView().findViewById(R.id.refresh_plus_address_icon);
-        Assert.assertEquals(refreshIcon.getVisibility(), View.GONE);
+        assertEquals(refreshIcon.getVisibility(), View.GONE);
     }
 
     @Test
@@ -159,8 +162,8 @@
                         .findViewById(R.id.plus_address_first_time_use_notice);
         Button cancelButton =
                 mBottomSheetContent.getContentView().findViewById(R.id.plus_address_cancel_button);
-        Assert.assertEquals(firstTimeNotice.getVisibility(), View.VISIBLE);
-        Assert.assertEquals(cancelButton.getVisibility(), View.VISIBLE);
+        assertEquals(firstTimeNotice.getVisibility(), View.VISIBLE);
+        assertEquals(cancelButton.getVisibility(), View.VISIBLE);
 
         cancelButton.callOnClick();
         verify(mDelegate).onCanceled();
@@ -188,8 +191,8 @@
                         .findViewById(R.id.plus_address_first_time_use_notice);
         Button cancelButton =
                 bottomSheetContent.getContentView().findViewById(R.id.plus_address_cancel_button);
-        Assert.assertEquals(firstTimeNotice.getVisibility(), View.GONE);
-        Assert.assertEquals(cancelButton.getVisibility(), View.GONE);
+        assertEquals(firstTimeNotice.getVisibility(), View.GONE);
+        assertEquals(cancelButton.getVisibility(), View.GONE);
     }
 
     @Test
@@ -197,7 +200,7 @@
     public void testRefreshButton_NotClickableUntilPlusAddressIsSet() {
         ImageView refreshIcon =
                 mBottomSheetContent.getContentView().findViewById(R.id.refresh_plus_address_icon);
-        Assert.assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
+        assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
         refreshIcon.callOnClick();
         verifyNoInteractions(mDelegate);
     }
@@ -207,7 +210,7 @@
     public void testRefreshButton_ClickableAfterPlusAddressIsSet() {
         ImageView refreshIcon =
                 mBottomSheetContent.getContentView().findViewById(R.id.refresh_plus_address_icon);
-        Assert.assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
+        assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
         mBottomSheetContent.setProposedPlusAddress(MODAL_PROPOSED_PLUS_ADDRESS);
 
         refreshIcon.callOnClick();
@@ -219,7 +222,7 @@
     public void testRefreshButton_OnlyOneClickIsHandledPerRefresh() {
         ImageView refreshIcon =
                 mBottomSheetContent.getContentView().findViewById(R.id.refresh_plus_address_icon);
-        Assert.assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
+        assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
         mBottomSheetContent.setProposedPlusAddress(MODAL_PROPOSED_PLUS_ADDRESS);
 
         refreshIcon.callOnClick();
@@ -232,7 +235,7 @@
     public void testRefreshButton_RefreshSeveralTimes() {
         ImageView refreshIcon =
                 mBottomSheetContent.getContentView().findViewById(R.id.refresh_plus_address_icon);
-        Assert.assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
+        assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
         mBottomSheetContent.setProposedPlusAddress(MODAL_PROPOSED_PLUS_ADDRESS);
 
         refreshIcon.callOnClick();
@@ -247,10 +250,10 @@
     public void testRefreshButton_HideRefreshButton() {
         ImageView refreshIcon =
                 mBottomSheetContent.getContentView().findViewById(R.id.refresh_plus_address_icon);
-        Assert.assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
+        assertEquals(refreshIcon.getVisibility(), View.VISIBLE);
 
         mBottomSheetContent.hideRefreshButton();
-        Assert.assertEquals(refreshIcon.getVisibility(), View.GONE);
+        assertEquals(refreshIcon.getVisibility(), View.GONE);
     }
 
     @Test
@@ -259,67 +262,90 @@
         Button modalConfirmButton =
                 mBottomSheetContent.getContentView().findViewById(R.id.plus_address_confirm_button);
 
-        Assert.assertFalse(modalConfirmButton.isEnabled());
+        assertFalse(modalConfirmButton.isEnabled());
         // Update the bottomsheet to show the plus address.
         mBottomSheetContent.setProposedPlusAddress(MODAL_PROPOSED_PLUS_ADDRESS);
-        Assert.assertTrue(modalConfirmButton.isEnabled());
+        assertTrue(modalConfirmButton.isEnabled());
 
         // Updating it while the button is enabled doesn't have an effect.
         mBottomSheetContent.setProposedPlusAddress("other@plus.plus");
-        Assert.assertTrue(modalConfirmButton.isEnabled());
+        assertTrue(modalConfirmButton.isEnabled());
     }
 
     @Test
     @SmallTest
-    public void testConfirmButton_disabledIfConfirmRequestFails() {
+    public void testLegacyErrorHandling_confirmDisabledIfConfirmRequestFails() {
         Button modalConfirmButton =
                 mBottomSheetContent.getContentView().findViewById(R.id.plus_address_confirm_button);
         // Set the plus address to enable the Confirm button.
         mBottomSheetContent.setProposedPlusAddress(MODAL_PROPOSED_PLUS_ADDRESS);
-        Assert.assertTrue(modalConfirmButton.isEnabled());
+        assertTrue(modalConfirmButton.isEnabled());
 
         // Assume a Confirm request was made and failed.
-        mBottomSheetContent.showError();
-        Assert.assertFalse(modalConfirmButton.isEnabled());
+        mBottomSheetContent.showError(/* errorStateInfo= */ null);
+        assertFalse(modalConfirmButton.isEnabled());
     }
 
     @Test
     @SmallTest
-    public void testShowError_displaysErrorMessage() {
+    public void testLegacyErrorHandling_displaysErrorMessage() {
         TextView modalPlusAddressPlaceholderView =
                 mBottomSheetContent.getContentView().findViewById(R.id.proposed_plus_address);
-        Assert.assertEquals(
+        assertEquals(
                 modalPlusAddressPlaceholderView.getText().toString(),
                 MODAL_PROPOSED_PLUS_ADDRESS_PLACEHOLDER);
-        Assert.assertEquals(modalPlusAddressPlaceholderView.getVisibility(), View.VISIBLE);
+        assertEquals(modalPlusAddressPlaceholderView.getVisibility(), View.VISIBLE);
 
         TextViewWithClickableSpans plusAddressErrorReportView =
                 mBottomSheetContent
                         .getContentView()
                         .findViewById(R.id.plus_address_modal_error_report);
-        Assert.assertEquals(plusAddressErrorReportView.getVisibility(), View.GONE);
+        assertEquals(plusAddressErrorReportView.getVisibility(), View.GONE);
 
-        mBottomSheetContent.showError();
-        Assert.assertEquals(
+        mBottomSheetContent.showError(/* errorStateInfo= */ null);
+        assertEquals(
                 mBottomSheetContent
                         .getContentView()
                         .findViewById(R.id.proposed_plus_address_container)
                         .getVisibility(),
                 View.GONE);
-        Assert.assertEquals(plusAddressErrorReportView.getVisibility(), View.VISIBLE);
-        Assert.assertEquals(
+        assertEquals(plusAddressErrorReportView.getVisibility(), View.VISIBLE);
+        assertEquals(
                 plusAddressErrorReportView.getText().toString(), MODAL_FORMATTED_ERROR_MESSAGE);
     }
 
     @Test
     @SmallTest
+    public void testReserveError() {
+        View contentView = mBottomSheetContent.getContentView();
+
+        mBottomSheetContent.showError(RESERVE_ERROR_STATE);
+        assertEquals(
+                contentView.findViewById(R.id.plus_address_content).getVisibility(), View.GONE);
+        assertEquals(
+                contentView.findViewById(R.id.plus_address_error_container).getVisibility(),
+                View.VISIBLE);
+
+        TextView title = contentView.findViewById(R.id.plus_address_error_title);
+        TextView description = contentView.findViewById(R.id.plus_address_error_description);
+        Button okButton = contentView.findViewById(R.id.plus_address_error_ok_button);
+        Button cancelButton = contentView.findViewById(R.id.plus_address_error_cancel_button);
+
+        assertEquals(title.getText(), RESERVE_ERROR_STATE.getTitle());
+        assertEquals(description.getText(), RESERVE_ERROR_STATE.getDescription());
+        assertEquals(okButton.getText(), RESERVE_ERROR_STATE.getOkText());
+        assertEquals(cancelButton.getText(), RESERVE_ERROR_STATE.getCancelText());
+    }
+
+    @Test
+    @SmallTest
     public void testBottomsheetLinkClicked_callsDelegateOpenErrorReportLink() {
         TextViewWithClickableSpans errorReportInstruction =
                 mBottomSheetContent
                         .getContentView()
                         .findViewById(R.id.plus_address_modal_error_report);
         ClickableSpan[] spans = errorReportInstruction.getClickableSpans();
-        Assert.assertEquals(spans.length, 1);
+        assertEquals(spans.length, 1);
         spans[0].onClick(errorReportInstruction);
 
         verify(mDelegate).openUrl(ERROR_URL);
@@ -333,7 +359,7 @@
                         .getContentView()
                         .findViewById(R.id.plus_address_first_time_use_notice);
         ClickableSpan[] spans = learnMoreInstruction.getClickableSpans();
-        Assert.assertEquals(spans.length, 1);
+        assertEquals(spans.length, 1);
         spans[0].onClick(learnMoreInstruction);
 
         verify(mDelegate).openUrl(LEARN_MORE_URL);
@@ -348,7 +374,7 @@
 
         ImageView refreshIcon =
                 mBottomSheetContent.getContentView().findViewById(R.id.refresh_plus_address_icon);
-        Assert.assertFalse(refreshIcon.isEnabled());
+        assertFalse(refreshIcon.isEnabled());
 
         verify(mDelegate).onConfirmRequested();
 
@@ -368,45 +394,45 @@
 
         // Before clicking confirm, there is no loading indicator, but both
         // a confirmation and a cancel button.
-        Assert.assertEquals(loadingView.getVisibility(), View.GONE);
+        assertEquals(loadingView.getVisibility(), View.GONE);
         Button modalConfirmButton =
                 mBottomSheetContent.getContentView().findViewById(R.id.plus_address_confirm_button);
         Button modalCancelButton =
                 mBottomSheetContent.getContentView().findViewById(R.id.plus_address_cancel_button);
-        Assert.assertEquals(modalConfirmButton.getVisibility(), View.VISIBLE);
-        Assert.assertEquals(modalCancelButton.getVisibility(), View.VISIBLE);
+        assertEquals(modalConfirmButton.getVisibility(), View.VISIBLE);
+        assertEquals(modalCancelButton.getVisibility(), View.VISIBLE);
 
         // Show the loading indicator and hide the buttons once we click the confirm button.
         modalConfirmButton.callOnClick();
         verify(mDelegate).onConfirmRequested();
-        Assert.assertEquals(modalConfirmButton.getVisibility(), View.GONE);
-        Assert.assertEquals(modalCancelButton.getVisibility(), View.GONE);
-        Assert.assertEquals(loadingView.getVisibility(), View.VISIBLE);
+        assertEquals(modalConfirmButton.getVisibility(), View.GONE);
+        assertEquals(modalCancelButton.getVisibility(), View.GONE);
+        assertEquals(loadingView.getVisibility(), View.VISIBLE);
 
         // Hide the loading indicator and resurface the buttons if we show an error.
-        mBottomSheetContent.showError();
+        mBottomSheetContent.showError(/* errorStateInfo= */ null);
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
-        Assert.assertEquals(loadingView.getVisibility(), View.GONE);
-        Assert.assertEquals(modalConfirmButton.getVisibility(), View.VISIBLE);
-        Assert.assertEquals(modalCancelButton.getVisibility(), View.VISIBLE);
+        assertEquals(loadingView.getVisibility(), View.GONE);
+        assertEquals(modalConfirmButton.getVisibility(), View.VISIBLE);
+        assertEquals(modalCancelButton.getVisibility(), View.VISIBLE);
     }
 
     @Test
     @SmallTest
     public void testBottomSheetOverriddenAttributes() {
-        Assert.assertEquals(mBottomSheetContent.getToolbarView(), null);
-        Assert.assertEquals(mBottomSheetContent.getPriority(), ContentPriority.HIGH);
-        Assert.assertEquals(mBottomSheetContent.swipeToDismissEnabled(), true);
-        Assert.assertEquals(mBottomSheetContent.getPeekHeight(), HeightMode.DISABLED);
-        Assert.assertEquals(mBottomSheetContent.getHalfHeightRatio(), HeightMode.DISABLED, 0.1);
-        Assert.assertEquals(mBottomSheetContent.getFullHeightRatio(), HeightMode.WRAP_CONTENT, 0.1);
-        Assert.assertEquals(
+        assertEquals(mBottomSheetContent.getToolbarView(), null);
+        assertEquals(mBottomSheetContent.getPriority(), ContentPriority.HIGH);
+        assertEquals(mBottomSheetContent.swipeToDismissEnabled(), true);
+        assertEquals(mBottomSheetContent.getPeekHeight(), HeightMode.DISABLED);
+        assertEquals(mBottomSheetContent.getHalfHeightRatio(), HeightMode.DISABLED, 0.1);
+        assertEquals(mBottomSheetContent.getFullHeightRatio(), HeightMode.WRAP_CONTENT, 0.1);
+        assertEquals(
                 mBottomSheetContent.getSheetContentDescriptionStringId(),
                 R.string.plus_address_bottom_sheet_content_description);
-        Assert.assertEquals(
+        assertEquals(
                 mBottomSheetContent.getSheetFullHeightAccessibilityStringId(),
                 R.string.plus_address_bottom_sheet_content_description);
-        Assert.assertEquals(
+        assertEquals(
                 mBottomSheetContent.getSheetClosedAccessibilityStringId(),
                 R.string.plus_address_bottom_sheet_content_description);
     }
diff --git a/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationCoordinatorTest.java b/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationCoordinatorTest.java
index a09f380..6c4cd488 100644
--- a/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationCoordinatorTest.java
+++ b/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationCoordinatorTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.ui.plus_addresses;
 
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
@@ -44,6 +45,8 @@
     private static final GURL LEARN_MORE_URL = new GURL("learn.more.com");
     private static final GURL ERROR_URL = new GURL("error.com");
     private static final boolean REFRESH_SUPPORTED = true;
+    private static final PlusAddressCreationErrorStateInfo ERROR_STATE =
+            new PlusAddressCreationErrorStateInfo("Title", "Description", "Ok", "Cancel");
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private Profile mProfile;
@@ -105,8 +108,8 @@
     @Test
     @SmallTest
     public void testShowError_callsMediator() {
-        mCoordinator.showError();
-        verify(mMediator).showError();
+        mCoordinator.showError(ERROR_STATE);
+        verify(mMediator).showError(eq(ERROR_STATE));
     }
 
     @Test
diff --git a/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationMediatorTest.java b/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationMediatorTest.java
index 713371a8..b63f5cdd 100644
--- a/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationMediatorTest.java
+++ b/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationMediatorTest.java
@@ -38,6 +38,8 @@
     private static final int TAB1_ID = 1;
     private static final int TAB2_ID = 2;
     private static final String PROPOSED_PLUS_ADDRESS = "foo@bar.com";
+    private static final PlusAddressCreationErrorStateInfo ERROR_STATE =
+            new PlusAddressCreationErrorStateInfo("Title", "Description", "Ok", "Cancel");
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
@@ -85,8 +87,8 @@
 
     @Test
     public void testShowError_callsBottomSheetShowError() {
-        mMediator.showError();
-        verify(mBottomSheetContent).showError();
+        mMediator.showError(ERROR_STATE);
+        verify(mBottomSheetContent).showError(eq(ERROR_STATE));
     }
 
     @Test
diff --git a/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridgeTest.java b/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridgeTest.java
index 77aa448c..a42c5544 100644
--- a/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridgeTest.java
+++ b/chrome/browser/ui/android/plus_addresses/junit/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationViewBridgeTest.java
@@ -15,7 +15,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -27,15 +26,12 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.layouts.LayoutManagerAppUtils;
 import org.chromium.chrome.browser.layouts.ManagedLayoutManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerFactory;
 import org.chromium.components.browser_ui.bottomsheet.ManagedBottomSheetController;
 import org.chromium.ui.base.TestActivity;
-import org.chromium.ui.base.WindowAndroid;
 import org.chromium.url.GURL;
 
 @RunWith(BaseRobolectricTestRunner.class)
@@ -54,6 +50,8 @@
     private static final String LEARN_MORE_URL = "learn.more.com";
     private static final String ERROR_URL = "bug.com";
     private static final boolean REFRESH_SUPPORTED = true;
+    private static final PlusAddressCreationErrorStateInfo ERROR_STATE =
+            new PlusAddressCreationErrorStateInfo("Title", "Description", "Ok", "Cancel");
 
     @Rule public JniMocker mJniMocker = new JniMocker();
     @Mock private Profile mProfile;
@@ -66,21 +64,19 @@
 
     private Activity mActivity;
     private MockTabModel mTabModel;
-    private WindowAndroid mWindow;
     private PlusAddressCreationViewBridge mPlusAddressCreationViewBridge;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mActivity = Robolectric.setupActivity(TestActivity.class);
-        mWindow = new WindowAndroid(mActivity);
         mTabModel = new MockTabModel(mProfile, null);
-        BottomSheetControllerFactory.attach(mWindow, mBottomSheetController);
-        LayoutManagerAppUtils.attach(mWindow, mLayoutManager);
         mPlusAddressCreationViewBridge =
                 new PlusAddressCreationViewBridge(
                         NATIVE_PLUS_ADDRESS_CREATION_VIEW,
-                        mWindow,
+                        mActivity,
+                        mBottomSheetController,
+                        mLayoutManager,
                         mTabModel,
                         mTabModelSelector,
                         mCoordinatorFactory);
@@ -88,13 +84,6 @@
         mJniMocker.mock(PlusAddressCreationViewBridgeJni.TEST_HOOKS, mBridgeNatives);
     }
 
-    @After
-    public void tearDown() {
-        BottomSheetControllerFactory.detach(mBottomSheetController);
-        LayoutManagerAppUtils.detach(mLayoutManager);
-        mWindow.destroy();
-    }
-
     private void setupCoordinatorFactory() {
         when(mCoordinatorFactory.create(
                         mActivity,
@@ -235,8 +224,8 @@
     public void testShowError_callsCoordinator() {
         setupCoordinatorFactory();
         showBottomSheet();
-        mPlusAddressCreationViewBridge.showError();
-        verify(mCoordinator, times(1)).showError();
+        mPlusAddressCreationViewBridge.showError(ERROR_STATE);
+        verify(mCoordinator, times(1)).showError(eq(ERROR_STATE));
     }
 
     @Test
@@ -261,7 +250,7 @@
     @SmallTest
     public void testwhenCoordinatorHasNotBeenCreated() {
         mPlusAddressCreationViewBridge.updateProposedPlusAddress(MODAL_PROPOSED_PLUS_ADDRESS);
-        mPlusAddressCreationViewBridge.showError();
+        mPlusAddressCreationViewBridge.showError(ERROR_STATE);
         mPlusAddressCreationViewBridge.finishConfirm();
         mPlusAddressCreationViewBridge.destroy();
         verifyNoInteractions(mCoordinator);
diff --git a/chrome/browser/ui/android/plus_addresses/plus_address_creation_controller_android.cc b/chrome/browser/ui/android/plus_addresses/plus_address_creation_controller_android.cc
index a948225..f86b350e3 100644
--- a/chrome/browser/ui/android/plus_addresses/plus_address_creation_controller_android.cc
+++ b/chrome/browser/ui/android/plus_addresses/plus_address_creation_controller_android.cc
@@ -11,11 +11,14 @@
 #include "chrome/browser/plus_addresses/plus_address_service_factory.h"
 #include "chrome/browser/plus_addresses/plus_address_setting_service_factory.h"
 #include "chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.h"
+#include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #include "components/plus_addresses/features.h"
 #include "components/plus_addresses/metrics/plus_address_metrics.h"
 #include "components/plus_addresses/plus_address_service.h"
 #include "components/plus_addresses/plus_address_types.h"
 #include "components/plus_addresses/settings/plus_address_setting_service.h"
+#include "ui/gfx/native_widget_types.h"
 
 namespace plus_addresses {
 // static
@@ -59,9 +62,10 @@
                             should_show_notice);
   modal_shown_time_ = base::TimeTicks::Now();
   if (!suppress_ui_for_testing_) {
-    view_ = std::make_unique<PlusAddressCreationViewAndroid>(GetWeakPtr(),
-                                                             &GetWebContents());
+    view_ = std::make_unique<PlusAddressCreationViewAndroid>(GetWeakPtr());
     view_->ShowInit(
+        GetWebContents().GetNativeView(),
+        TabModelList::GetTabModelForWebContents(&GetWebContents()),
         maybe_email.value(),
         plus_address_service->IsRefreshingSupported(relevant_origin_),
         /*has_accepted_notice=*/!should_show_notice);
diff --git a/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.cc b/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.cc
index 86e3aeb..0d0d5133 100644
--- a/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.cc
+++ b/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.cc
@@ -8,9 +8,8 @@
 #include "base/feature_list.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/android/plus_addresses/plus_address_creation_controller_android.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
-#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
-#include "chrome/browser/ui/plus_addresses/plus_address_creation_controller.h"
 #include "components/plus_addresses/features.h"
 #include "components/plus_addresses/plus_address_types.h"
 #include "components/strings/grit/components_strings.h"
@@ -20,18 +19,39 @@
 #include "ui/base/l10n/l10n_util.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
+#include "chrome/browser/ui/android/plus_addresses/jni_headers/PlusAddressCreationErrorStateInfo_jni.h"
 #include "chrome/browser/ui/android/plus_addresses/jni_headers/PlusAddressCreationViewBridge_jni.h"
 
 namespace plus_addresses {
 
+namespace {
+
 using base::android::ConvertUTF16ToJavaString;
 using base::android::ConvertUTF8ToJavaString;
 using base::android::ScopedJavaLocalRef;
 
+ScopedJavaLocalRef<jobject> GetReserveErrorStateInfo() {
+  if (!base::FeatureList::IsEnabled(
+          features::kPlusAddressAndroidErrorStatesEnabled)) {
+    return ScopedJavaLocalRef<jobject>();
+  }
+  return Java_PlusAddressCreationErrorStateInfo_Constructor(
+      base::android::AttachCurrentThread(),
+      l10n_util::GetStringUTF16(
+          IDS_PLUS_ADDRESS_BOTTOMSHEET_RESERVE_ERROR_TITLE_ANDROID),
+      l10n_util::GetStringUTF16(
+          IDS_PLUS_ADDRESS_BOTTOMSHEET_RESERVE_ERROR_DESCRIPTION_ANDROID),
+      l10n_util::GetStringUTF16(
+          IDS_PLUS_ADDRESS_BOTTOMSHEET_RESERVE_ERROR_OK_BUTTON_ANDROID),
+      l10n_util::GetStringUTF16(
+          IDS_PLUS_ADDRESS_BOTTOMSHEET_RESERVE_ERROR_CANCEL_BUTTON_ANDROID));
+}
+
+}  // namespace
+
 PlusAddressCreationViewAndroid::PlusAddressCreationViewAndroid(
-    base::WeakPtr<PlusAddressCreationController> controller,
-    content::WebContents* web_contents)
-    : controller_(controller), web_contents_(web_contents) {}
+    base::WeakPtr<PlusAddressCreationController> controller)
+    : controller_(controller) {}
 
 PlusAddressCreationViewAndroid::~PlusAddressCreationViewAndroid() {
   if (java_object_) {
@@ -41,21 +61,18 @@
 }
 
 void PlusAddressCreationViewAndroid::ShowInit(
+    gfx::NativeView native_view,
+    TabModel* tab_model,
     const std::string& primary_email_address,
     bool refresh_supported,
     bool has_accepted_notice) {
   JNIEnv* env = base::android::AttachCurrentThread();
-  TabModel* tab_model = TabModelList::GetTabModelForWebContents(web_contents_);
-  if (!tab_model) {
-    // TODO(crbug.com/40276862): Verify expected behavior in this case.
+  base::android::ScopedJavaGlobalRef<jobject> java_object =
+      GetOrCreateJavaObject(native_view, tab_model);
+  if (!java_object) {
     return;
   }
 
-  java_object_.Reset(Java_PlusAddressCreationViewBridge_create(
-      env, reinterpret_cast<intptr_t>(this),
-      web_contents_->GetTopLevelNativeWindow()->GetJavaObject(),
-      tab_model->GetJavaObject()));
-
   // TODO(b/303054310): Once project exigencies allow for it, convert all of
   // these back to the android view XML.
   ScopedJavaLocalRef<jstring> j_title;
@@ -141,6 +158,9 @@
 
 void PlusAddressCreationViewAndroid::ShowReserveResult(
     const PlusProfileOrError& maybe_plus_profile) {
+  if (!java_object_) {
+    return;
+  }
   JNIEnv* env = base::android::AttachCurrentThread();
   if (maybe_plus_profile.has_value()) {
     ScopedJavaLocalRef<jstring> j_proposed_plus_address =
@@ -149,22 +169,48 @@
     Java_PlusAddressCreationViewBridge_updateProposedPlusAddress(
         env, java_object_, j_proposed_plus_address);
   } else {
-    Java_PlusAddressCreationViewBridge_showError(env, java_object_);
+    Java_PlusAddressCreationViewBridge_showError(env, java_object_,
+                                                 GetReserveErrorStateInfo());
   }
 }
 
 void PlusAddressCreationViewAndroid::ShowConfirmResult(
     const PlusProfileOrError& maybe_plus_profile) {
+  if (!java_object_) {
+    return;
+  }
   JNIEnv* env = base::android::AttachCurrentThread();
   if (maybe_plus_profile.has_value()) {
     Java_PlusAddressCreationViewBridge_finishConfirm(env, java_object_);
   } else {
-    Java_PlusAddressCreationViewBridge_showError(env, java_object_);
+    // TODO: crbug.com/354881207 - Pass a proper confirm  error information.
+    Java_PlusAddressCreationViewBridge_showError(env, java_object_,
+                                                 ScopedJavaLocalRef<jobject>());
   }
 }
 
 void PlusAddressCreationViewAndroid::HideRefreshButton() {
+  if (!java_object_) {
+    return;
+  }
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_PlusAddressCreationViewBridge_hideRefreshButton(env, java_object_);
 }
+
+base::android::ScopedJavaGlobalRef<jobject>
+PlusAddressCreationViewAndroid::GetOrCreateJavaObject(
+    gfx::NativeView native_view,
+    TabModel* tab_model) {
+  if (java_object_) {
+    return java_object_;
+  }
+  if (!tab_model || !native_view || !native_view->GetWindowAndroid()) {
+    return nullptr;  // No window attached (yet or anymore).
+  }
+  return java_object_ = Java_PlusAddressCreationViewBridge_create(
+             base::android::AttachCurrentThread(),
+             reinterpret_cast<intptr_t>(this),
+             native_view->GetWindowAndroid()->GetJavaObject(),
+             tab_model->GetJavaObject());
+}
 }  // namespace plus_addresses
diff --git a/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.h b/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.h
index 53e66df..9159e6a 100644
--- a/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.h
+++ b/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.h
@@ -7,26 +7,30 @@
 
 #include <jni.h>
 
-#include "chrome/browser/ui/plus_addresses/plus_address_creation_controller.h"
 #include "components/plus_addresses/plus_address_types.h"
-#include "content/public/browser/web_contents.h"
+#include "ui/gfx/native_widget_types.h"
+
+class TabModel;
 
 namespace plus_addresses {
 
+class PlusAddressCreationController;
+
 // A class intended as a thin wrapper around a Java object, which calls out to
-// the `PlusAddressCreationController`. This shields the controller from JNI
-// complications, allowing a consistent interface for clients (e.g., autofill).
-// Note that it is likely that either the controller will morph to do what this
-// class does now, or a similar wrapper will be created for desktop, with a
-// single controller implementation.
+// the `PlusAddressCreationControllerAndroid`. This shields the controller from
+// JNI complications, allowing a consistent interface for clients (e.g.,
+// autofill). Note that it is likely that either the controller will morph to do
+// what this class does now, or a similar wrapper will be created for desktop,
+// with a single controller implementation.
 class PlusAddressCreationViewAndroid {
  public:
-  PlusAddressCreationViewAndroid(
-      base::WeakPtr<PlusAddressCreationController> controller,
-      content::WebContents* web_contents);
+  explicit PlusAddressCreationViewAndroid(
+      base::WeakPtr<PlusAddressCreationController> controller);
   ~PlusAddressCreationViewAndroid();
 
-  void ShowInit(const std::string& primary_email_address,
+  void ShowInit(gfx::NativeView native_view,
+                TabModel* tab_model,
+                const std::string& primary_email_address,
                 bool refresh_supported,
                 bool has_accepted_notice);
   void OnRefreshClicked(JNIEnv* env,
@@ -47,10 +51,17 @@
   void HideRefreshButton();
 
  private:
+  // Returns either the fully initialized java counterpart of this bridge or
+  // a is_null() reference if the creation failed. By using this method, the
+  // bridge will try to recreate the java object if it failed previously (e.g.
+  // because there was no native window available).
+  base::android::ScopedJavaGlobalRef<jobject> GetOrCreateJavaObject(
+      gfx::NativeView native_view,
+      TabModel* tab_model);
+
   // The corresponding java object.
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
   base::WeakPtr<PlusAddressCreationController> controller_;
-  raw_ptr<content::WebContents> web_contents_;
 };
 
 }  // namespace plus_addresses
diff --git a/chrome/browser/ui/android/ssl_client_certificate_selector.cc b/chrome/browser/ui/android/ssl_client_certificate_selector.cc
index 0e2f394..de69508 100644
--- a/chrome/browser/ui/android/ssl_client_certificate_selector.cc
+++ b/chrome/browser/ui/android/ssl_client_certificate_selector.cc
@@ -11,8 +11,6 @@
 #include "content/public/browser/client_certificate_delegate.h"
 #include "net/ssl/ssl_private_key.h"
 
-namespace chrome {
-
 namespace {
 
 // Returns the storage of a test hook for `ShowSSLClientCertificateSelector()`.
@@ -44,4 +42,3 @@
   GetShowSSLClientCertificateSelectorTestingHook() = std::move(hook);
 }
 
-}  // namespace chrome
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index b56e8de..c4257fcd 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -353,6 +353,7 @@
     "//chrome/browser/ui/android/edge_to_edge:java",
     "//chrome/browser/ui/android/edge_to_edge/internal:java",
     "//chrome/browser/ui/android/layouts:java",
+    "//chrome/browser/ui/android/native_page:java",
     "//chrome/browser/ui/android/omnibox:java",
     "//chrome/browser/ui/android/omnibox:java_resources",
     "//chrome/browser/ui/android/theme:java",
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonController.java
index 2e9a4af..d1e6f4b 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonController.java
@@ -77,7 +77,7 @@
     @Override
     protected boolean shouldShowButton(Tab tab) {
         if (!super.shouldShowButton(tab)) return false;
-
+        if (tab.isNativePage() && tab.getNativePage().isPdf()) return false;
         return UrlUtilities.isHttpOrHttps(tab.getUrl());
     }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonControllerUnitTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonControllerUnitTest.java
index 5bfc946..b7ce9f2f 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonControllerUnitTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonControllerUnitTest.java
@@ -26,6 +26,7 @@
 import org.chromium.chrome.browser.toolbar.ButtonData;
 import org.chromium.chrome.browser.translate.TranslateBridge;
 import org.chromium.chrome.browser.translate.TranslateBridgeJni;
+import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.content_public.browser.WebContents;
@@ -42,6 +43,7 @@
     @Mock private Drawable mDrawable;
     @Mock private Tracker mTracker;
     @Mock TranslateBridge.Natives mMockTranslateBridge;
+    @Mock private NativePage mNativePage;
 
     private UserActionTester mActionTester;
 
@@ -97,4 +99,20 @@
         Assert.assertTrue(buttonData.isEnabled());
         Assert.assertNotNull(buttonData.getButtonSpec());
     }
+
+    @Test
+    public void testShouldNotShowUpPdfUrls() {
+        when(mTab.getUrl()).thenReturn(JUnitTestGURLs.HTTP_URL);
+        when(mTab.isNativePage()).thenReturn(true);
+        when(mTab.getNativePage()).thenReturn(mNativePage);
+        when(mNativePage.isPdf()).thenReturn(true);
+        TranslateToolbarButtonController translateToolbarButtonController =
+                new TranslateToolbarButtonController(
+                        () -> mTab, mDrawable, "Translate button description", () -> mTracker);
+        ButtonData buttonData = translateToolbarButtonController.get(mTab);
+
+        Assert.assertFalse(buttonData.canShow());
+        Assert.assertTrue(buttonData.isEnabled());
+        Assert.assertNotNull(buttonData.getButtonSpec());
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediator.java
index b62062f..fad30a68 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediator.java
@@ -167,15 +167,19 @@
                                     TopToolbarOverlayProperties.CONTENT_OFFSET,
                                     mBrowserControlsStateProvider.getContentOffset());
                         }
-                        updateShadowState();
-                        updateVisibility();
+                        if (!ChromeFeatureList.sBcivZeroBrowserFrames.isEnabled()) {
+                            updateShadowState();
+                            updateVisibility();
+                        }
                     }
 
                     @Override
                     public void onAndroidControlsVisibilityChanged(int visibility) {
                         if (ToolbarFeatures.shouldSuppressCaptures()) {
                             mIsBrowserControlsAndroidViewVisible = visibility == View.VISIBLE;
-                            updateShadowState();
+                            if (!ChromeFeatureList.sBcivZeroBrowserFrames.isEnabled()) {
+                                updateShadowState();
+                            }
                         }
                     }
 
diff --git a/chrome/browser/ui/ash/main_extra_parts/DEPS b/chrome/browser/ui/ash/main_extra_parts/DEPS
index 9d84b15..c2d7acc1 100644
--- a/chrome/browser/ui/ash/main_extra_parts/DEPS
+++ b/chrome/browser/ui/ash/main_extra_parts/DEPS
@@ -12,6 +12,7 @@
   # Whenever possible, avoid adding new //chrome dependencies to this list.
   "+chrome/browser/ash/app_list",
   "+chrome/browser/ash/arc/util",
+  "+chrome/browser/ash/auth",
   "+chrome/browser/ash/boca",
   "+chrome/browser/ash/crosapi",
   "+chrome/browser/ash/geolocation",
diff --git a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
index bf28030..50b7406 100644
--- a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
@@ -28,6 +28,7 @@
 #include "base/trace_event/trace_event.h"
 #include "chrome/browser/ash/app_list/app_list_client_impl.h"
 #include "chrome/browser/ash/arc/util/arc_window_watcher.h"
+#include "chrome/browser/ash/auth/active_session_fingerprint_client_impl.h"
 #include "chrome/browser/ash/boca/boca_app_client_impl.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ash/geolocation/system_geolocation_source.h"
@@ -255,6 +256,9 @@
   in_session_auth_token_provider_ =
       std::make_unique<ash::InSessionAuthTokenProviderImpl>();
 
+  active_session_fingerprint_client_ =
+      std::make_unique<ash::ActiveSessionFingerprintClientImpl>();
+
   // NOTE: The WallpaperControllerClientImpl must be initialized before the
   // session controller, because the session controller triggers the loading
   // of users, which itself calls a code path which eventually reaches the
diff --git a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.h
index bd72b748..e8cf37a 100644
--- a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.h
@@ -15,6 +15,7 @@
 
 namespace ash {
 class ArcWindowWatcher;
+class ActiveSessionFingerprintClient;
 class InSessionAuthTokenProviderImpl;
 class MagicBoostStateAsh;
 class NetworkPortalNotificationController;
@@ -135,6 +136,8 @@
   std::unique_ptr<ash::boca::BocaAppClientImpl> boca_client_;
   std::unique_ptr<ImeControllerClientImpl> ime_controller_client_;
   std::unique_ptr<InSessionAuthDialogClient> in_session_auth_dialog_client_;
+  std::unique_ptr<ash::ActiveSessionFingerprintClient>
+      active_session_fingerprint_client_;
   std::unique_ptr<ash::InSessionAuthTokenProviderImpl>
       in_session_auth_token_provider_;
   std::unique_ptr<ScreenOrientationDelegateChromeos>
diff --git a/chrome/browser/ui/autofill/autofill_context_menu_manager.cc b/chrome/browser/ui/autofill/autofill_context_menu_manager.cc
index f414b436..7475e0c 100644
--- a/chrome/browser/ui/autofill/autofill_context_menu_manager.cc
+++ b/chrome/browser/ui/autofill/autofill_context_menu_manager.cc
@@ -183,7 +183,7 @@
       params.field_renderer_id);
   return password_manager_driver.GetPasswordManager()
       ->GetPasswordFormCache()
-      ->HasPasswordForm(&password_manager_driver, current_field_renderer_id);
+      ->GetPasswordForm(&password_manager_driver, current_field_renderer_id);
 }
 
 // Returns true if the user has autofillable passwords saved.
diff --git a/chrome/browser/ui/autofill/autofill_context_menu_manager_browsertest.cc b/chrome/browser/ui/autofill/autofill_context_menu_manager_browsertest.cc
index 50dc54f..cbf583d 100644
--- a/chrome/browser/ui/autofill/autofill_context_menu_manager_browsertest.cc
+++ b/chrome/browser/ui/autofill/autofill_context_menu_manager_browsertest.cc
@@ -1517,11 +1517,21 @@
       case AutofillSuggestionTriggerSource::kManualFallbackAddress:
         return CreateAndAttachAutocompleteUnrecognizedForm();
       case AutofillSuggestionTriggerSource::kManualFallbackPasswords: {
-        FormData form = CreateAndAttachPasswordForm();
         // Create a password form manager for this form, to simulate that its
         // fields are classified as password form fields.
-        password_manager_driver()->GetPasswordManager()->OnPasswordFormsParsed(
-            password_manager_driver(), {form});
+        FormData form = CreateAndAttachPasswordForm();
+        password_manager::PasswordFormManager::
+            set_wait_for_server_predictions_for_filling(false);
+        password_manager::PasswordManager* password_manager =
+            static_cast<password_manager::PasswordManager*>(
+                password_manager_driver()->GetPasswordManager());
+        password_manager->OnPasswordFormsParsed(password_manager_driver(),
+                                                {form});
+        // Wait until `form` gets parsed.
+        EXPECT_TRUE(base::test::RunUntil([&]() {
+          return password_manager->GetPasswordFormCache()->GetPasswordForm(
+              password_manager_driver(), form.renderer_id());
+        }));
         return form;
       }
       default:
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
index f74e138d..2e20cb7 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
@@ -403,9 +403,11 @@
 }
 
 void AutofillKeyboardAccessoryControllerImpl::Show(
+    UiSessionId ui_session_id,
     std::vector<Suggestion> suggestions,
     AutofillSuggestionTriggerSource trigger_source,
     AutoselectFirstSuggestion autoselect_first_suggestion) {
+  ui_session_id_ = ui_session_id;
   suggestions_filling_product_ =
       !suggestions.empty() && IsStandaloneSuggestionType(suggestions[0].type)
           ? GetFillingProductFromSuggestionType(suggestions[0].type)
@@ -477,6 +479,11 @@
   delegate_->OnSuggestionsShown(suggestions_);
 }
 
+std::optional<AutofillSuggestionController::UiSessionId>
+AutofillKeyboardAccessoryControllerImpl::GetUiSessionId() const {
+  return view_ ? std::make_optional(ui_session_id_) : std::nullopt;
+}
+
 void AutofillKeyboardAccessoryControllerImpl::SetKeepPopupOpenForTesting(
     bool keep_popup_open_for_testing) {
   keep_popup_open_for_testing_ = keep_popup_open_for_testing;
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.h b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.h
index 0f94211..5d62fb78 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.h
@@ -78,9 +78,11 @@
   FillingProduct GetMainFillingProduct() const override;
   std::optional<AutofillClient::PopupScreenLocation> GetPopupScreenLocation()
       const override;
-  void Show(std::vector<Suggestion> suggestions,
+  void Show(UiSessionId ui_session_id,
+            std::vector<Suggestion> suggestions,
             AutofillSuggestionTriggerSource trigger_source,
             AutoselectFirstSuggestion autoselect_first_suggestion) override;
+  std::optional<UiSessionId> GetUiSessionId() const override;
   void SetKeepPopupOpenForTesting(bool keep_popup_open_for_testing) override;
   void UpdateDataListValues(base::span<const SelectOption> options) override;
   void PinView() override;
@@ -115,6 +117,9 @@
   // Hides the view and asynchronously deletes itself.
   void HideViewAndDie();
 
+  // Uniquely identifies the UI the controller is showing.
+  UiSessionId ui_session_id_;
+
   base::WeakPtr<AutofillSuggestionDelegate> delegate_;
   base::WeakPtr<content::WebContents> web_contents_;
   PopupControllerCommon controller_common_;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index 53beb17..87564f5 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -173,9 +173,11 @@
 AutofillPopupControllerImpl::~AutofillPopupControllerImpl() = default;
 
 void AutofillPopupControllerImpl::Show(
+    UiSessionId ui_session_id,
     std::vector<Suggestion> suggestions,
     AutofillSuggestionTriggerSource trigger_source,
     AutoselectFirstSuggestion autoselect_first_suggestion) {
+  ui_session_id_ = ui_session_id;
   suggestions_filling_product_ =
       !suggestions.empty() && IsStandaloneSuggestionType(suggestions[0].type)
           ? GetFillingProductFromSuggestionType(suggestions[0].type)
@@ -296,6 +298,11 @@
   }
 }
 
+std::optional<AutofillSuggestionController::UiSessionId>
+AutofillPopupControllerImpl::GetUiSessionId() const {
+  return view_ ? std::make_optional(ui_session_id_) : std::nullopt;
+}
+
 void AutofillPopupControllerImpl::SetKeepPopupOpenForTesting(
     bool keep_popup_open_for_testing) {
   keep_popup_open_for_testing_ = keep_popup_open_for_testing;
@@ -305,7 +312,7 @@
     base::span<const SelectOption> options) {
   non_filtered_suggestions_ = UpdateSuggestionsFromDataList(
       options, std::move(non_filtered_suggestions_));
-  UpdateFilteredSuggestions(/*notify_suggestions_changed=*/false);
+  UpdateFilteredSuggestions();
   if (HasSuggestions()) {
     OnSuggestionsChanged();
   } else {
@@ -456,8 +463,7 @@
   }
 }
 
-void AutofillPopupControllerImpl::UpdateFilteredSuggestions(
-    bool notify_suggestions_changed) {
+void AutofillPopupControllerImpl::UpdateFilteredSuggestions() {
   if (filter_) {
     SuggestionFiltrationResult filtration_result =
         FilterSuggestions(non_filtered_suggestions_, *filter_);
@@ -467,9 +473,6 @@
     filtered_suggestions_.clear();
     suggestion_filter_matches_.clear();
   }
-  if (notify_suggestions_changed) {
-    OnSuggestionsChanged(/*prefer_prev_arrow_side=*/true);
-  }
 }
 
 int AutofillPopupControllerImpl::GetLineCount() const {
@@ -548,7 +551,7 @@
                                      GetSuggestions()[list_index]);
     CHECK(suggestion_iter != non_filtered_suggestions_.end());
     non_filtered_suggestions_.erase(suggestion_iter);
-    UpdateFilteredSuggestions(/*notify_suggestions_changed=*/false);
+    UpdateFilteredSuggestions();
   } else {
     non_filtered_suggestions_.erase(non_filtered_suggestions_.begin() +
                                     list_index);
@@ -584,7 +587,7 @@
 void AutofillPopupControllerImpl::SetSuggestions(
     std::vector<Suggestion> suggestions) {
   non_filtered_suggestions_ = std::move(suggestions);
-  UpdateFilteredSuggestions(/*notify_suggestions_changed=*/false);
+  UpdateFilteredSuggestions();
 }
 
 base::WeakPtr<AutofillPopupController>
@@ -786,7 +789,7 @@
   // Show() can fail and cause controller deletion. Therefore store the weak
   // pointer before, so that this method returns null when that happens.
   sub_popup_controller_ = controller->weak_ptr_factory_.GetWeakPtr();
-  controller->Show(std::move(suggestions), trigger_source_,
+  controller->Show(ui_session_id_, std::move(suggestions), trigger_source_,
                    autoselect_first_suggestion);
   return sub_popup_controller_;
 }
@@ -823,7 +826,8 @@
 void AutofillPopupControllerImpl::SetFilter(
     std::optional<SuggestionFilter> filter) {
   filter_ = std::move(filter);
-  UpdateFilteredSuggestions(/*notify_suggestions_changed=*/true);
+  UpdateFilteredSuggestions();
+  OnSuggestionsChanged(/*prefer_prev_arrow_side=*/true);
 }
 
 bool AutofillPopupControllerImpl::HandleKeyPressEvent(
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
index 912c8b70..3f53801 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
@@ -87,9 +87,11 @@
       const override;
   void Hide(SuggestionHidingReason reason) override;
   void ViewDestroyed() override;
-  void Show(std::vector<Suggestion> suggestions,
+  void Show(UiSessionId ui_session_id,
+            std::vector<Suggestion> suggestions,
             AutofillSuggestionTriggerSource trigger_source,
             AutoselectFirstSuggestion autoselect_first_suggestion) override;
+  std::optional<UiSessionId> GetUiSessionId() const override;
   void SetKeepPopupOpenForTesting(bool keep_popup_open_for_testing) override;
   void UpdateDataListValues(base::span<const SelectOption> options) override;
   void PinView() override;
@@ -171,8 +173,9 @@
   // the first preferred when recalculating the popup position.
   void OnSuggestionsChanged(bool prefer_prev_arrow_side);
 
-  void UpdateFilteredSuggestions(bool notify_suggestions_changed);
+  void UpdateFilteredSuggestions();
 
+  UiSessionId ui_session_id_;
   base::WeakPtr<content::WebContents> web_contents_;
   PopupControllerCommon controller_common_;
   base::WeakPtr<AutofillPopupView> view_;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.h
index 26408074..64c1e4ec 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.h
@@ -28,8 +28,9 @@
 
   // Shows the popup, or updates the existing popup with the given values.
   // If the popup contains credit card items, find and set
-  // |touchBarController_| and show the credit card autofill touch bar.
-  void Show(std::vector<autofill::Suggestion> suggestions,
+  // `touchBarController_` and show the credit card autofill touch bar.
+  void Show(UiSessionId ui_session_id,
+            std::vector<autofill::Suggestion> suggestions,
             AutofillSuggestionTriggerSource trigger_source,
             AutoselectFirstSuggestion autoselect_first_suggestion) override;
 
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm
index 83f01fc0..8a79b64 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm
@@ -58,6 +58,7 @@
 AutofillPopupControllerImplMac::~AutofillPopupControllerImplMac() = default;
 
 void AutofillPopupControllerImplMac::Show(
+    UiSessionId ui_session_id,
     std::vector<autofill::Suggestion> suggestions,
     AutofillSuggestionTriggerSource trigger_source,
     AutoselectFirstSuggestion autoselect_first_suggestion) {
@@ -67,7 +68,8 @@
     [touch_bar_controller_ showCreditCardAutofillWithController:this];
   }
 
-  AutofillPopupControllerImpl::Show(std::move(suggestions), trigger_source,
+  AutofillPopupControllerImpl::Show(ui_session_id, std::move(suggestions),
+                                    trigger_source,
                                     autoselect_first_suggestion);
   // No code below this line!
   // |Show| may hide the popup and destroy |this|, so |Show| should be the last
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
index 5a29c1e6..d9ddc2ee 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
@@ -154,6 +154,18 @@
   EXPECT_TRUE(sub_controller);
 }
 
+// Tests that a sub-popup shares its UI session id with its parent controller.
+TEST_F(AutofillPopupControllerImplTest, SubPopupHasSameUiSessionIdAsParent) {
+  const std::optional<AutofillSuggestionController::UiSessionId> parent_id =
+      client().popup_controller(manager()).GetUiSessionId();
+  ASSERT_TRUE(parent_id.has_value());
+  base::WeakPtr<AutofillSuggestionController> sub_controller =
+      client().popup_controller(manager()).OpenSubPopup(
+          {0, 0, 10, 10}, {}, AutoselectFirstSuggestion(false));
+  EXPECT_TRUE(sub_controller);
+  EXPECT_EQ(sub_controller->GetUiSessionId(), parent_id);
+}
+
 TEST_F(AutofillPopupControllerImplTest,
        PopupInteraction_SubPopupMetricsAreLogged) {
   base::HistogramTester histogram_tester;
@@ -477,7 +489,8 @@
   // the visible duration metric start time.
   test_api(static_cast<AutofillPopupControllerImpl&>(*sub_controller))
       .SetView(client().sub_popup_view()->GetWeakPtr());
-  sub_controller->Show({Suggestion(SuggestionType::kPasswordEntry)},
+  sub_controller->Show(AutofillClient::SuggestionUiSessionId(),
+                       {Suggestion(SuggestionType::kPasswordEntry)},
                        AutofillSuggestionTriggerSource::kPasswordManager,
                        AutoselectFirstSuggestion(false));
 
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller.cc b/chrome/browser/ui/autofill/autofill_suggestion_controller.cc
new file mode 100644
index 0000000..1b5f72b
--- /dev/null
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller.cc
@@ -0,0 +1,16 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/autofill/autofill_suggestion_controller.h"
+
+namespace autofill {
+
+// static
+AutofillSuggestionController::UiSessionId
+AutofillSuggestionController::GenerateSuggestionUiSessionId() {
+  static UiSessionId::Generator generator;
+  return generator.GenerateNextId();
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller.h b/chrome/browser/ui/autofill/autofill_suggestion_controller.h
index 15ddbdf..1114322 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller.h
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller.h
@@ -40,6 +40,10 @@
       PopupControllerCommon controller_common,
       int32_t form_control_ax_id);
 
+  using UiSessionId = AutofillClient::SuggestionUiSessionId;
+  // Generates a new unique session id for suggestion UI.
+  static UiSessionId GenerateSuggestionUiSessionId();
+
   // Recalculates the height and width of the suggestion UI and triggers a
   // redraw when suggestions change.
   virtual void OnSuggestionsChanged() = 0;
@@ -75,10 +79,17 @@
 
   // Shows the suggestion UI, or updates the existing suggestion UI with the
   // given values.
-  virtual void Show(std::vector<Suggestion> suggestions,
+  virtual void Show(UiSessionId session_id,
+                    std::vector<Suggestion> suggestions,
                     AutofillSuggestionTriggerSource trigger_source,
                     AutoselectFirstSuggestion autoselect_first_suggestion) = 0;
 
+  // Returns the unique session id for the suggestions UI that is showing. If
+  // no UI is showing, it returns `std::nullopt`. If there are multiple,
+  // connected controllers (e.g. for sub-popups on Desktop), all controllers
+  // will have the same session id.
+  virtual std::optional<UiSessionId> GetUiSessionId() const = 0;
+
   // This method cannot be moved into a test api, because it is called by
   // production code in `ChromeAutofillClient`. This happens because, before the
   // popup is shown, tests can ask the client to keep the popup open for
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h b/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h
index efc4cea..2ce3290 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h
@@ -174,9 +174,10 @@
     FocusWebContentsOnFrame(
         static_cast<ContentAutofillDriver&>(manager.driver())
             .render_frame_host());
-    client().popup_controller(manager).Show(std::move(suggestions),
-                                            trigger_source,
-                                            AutoselectFirstSuggestion(false));
+    client().popup_controller(manager).Show(
+        AutofillSuggestionController::GenerateSuggestionUiSessionId(),
+        std::move(suggestions), trigger_source,
+        AutoselectFirstSuggestion(false));
   }
 
   input::NativeWebKeyboardEvent CreateKeyPressEvent(int windows_key_code) {
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
index 441a3055..9b0c1482 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
@@ -321,6 +321,21 @@
   client().popup_controller(manager()).DoHide();
 }
 
+// Tests that the controller does not have a UI session id if it has no view.
+TEST_F(AutofillSuggestionControllerTest, EmptyUiSessionIdAfterCreation) {
+  test_api(client().popup_controller(manager())).SetView(nullptr);
+  EXPECT_EQ(client().popup_controller(manager()).GetUiSessionId(),
+            std::nullopt);
+}
+
+// Tests that the controller has a UI session id after `Show` is called.
+TEST_F(AutofillSuggestionControllerTest, NonEmptyUiSessionIdAfterShow) {
+  ShowSuggestions(manager(), {SuggestionType::kAutocompleteEntry,
+                              SuggestionType::kAutocompleteEntry});
+  EXPECT_TRUE(
+      client().popup_controller(manager()).GetUiSessionId().has_value());
+}
+
 TEST_F(AutofillSuggestionControllerTest, ProperlyResetController) {
   ShowSuggestions(manager(), {SuggestionType::kAutocompleteEntry,
                               SuggestionType::kAutocompleteEntry});
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index bd373a3e..b7fe032c 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -43,6 +43,7 @@
 #include "chrome/browser/translate/chrome_translate_client.h"
 #include "chrome/browser/ui/autofill/address_bubbles_controller.h"
 #include "chrome/browser/ui/autofill/autofill_field_promo_controller_impl.h"
+#include "chrome/browser/ui/autofill/autofill_suggestion_controller.h"
 #include "chrome/browser/ui/autofill/delete_address_profile_dialog_controller_impl.h"
 #include "chrome/browser/ui/autofill/edit_address_profile_dialog_controller_impl.h"
 #include "chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h"
@@ -535,7 +536,8 @@
 #endif
 }
 
-void ChromeAutofillClient::ShowAutofillSuggestions(
+AutofillClient::SuggestionUiSessionId
+ChromeAutofillClient::ShowAutofillSuggestions(
     const PopupOpenArgs& open_args,
     base::WeakPtr<AutofillSuggestionDelegate> delegate) {
   // The Autofill Popup cannot open if it overlaps with another popup.
@@ -546,10 +548,13 @@
   // guarantees the IPH will be hidden by the time the Autofill Popup will
   // attempt to open. This works because the tasks of hiding the IPH and showing
   // the Autofill Popup are posted on the same thread (UI thread).
+  const SuggestionUiSessionId session_id =
+      AutofillSuggestionController::GenerateSuggestionUiSessionId();
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
       FROM_HERE,
       base::BindOnce(&ChromeAutofillClient::ShowAutofillSuggestionsImpl,
-                     GetWeakPtr(), open_args, delegate));
+                     GetWeakPtr(), session_id, open_args, delegate));
+  return session_id;
 }
 
 void ChromeAutofillClient::UpdateAutofillDataListValues(
@@ -578,12 +583,20 @@
              : std::make_optional<AutofillClient::PopupScreenLocation>();
 }
 
+std::optional<AutofillClient::SuggestionUiSessionId>
+ChromeAutofillClient::GetSessionIdForCurrentAutofillSuggestions() const {
+  return suggestion_controller_ ? suggestion_controller_->GetUiSessionId()
+                                : std::nullopt;
+}
+
 void ChromeAutofillClient::UpdateAutofillSuggestions(
     const std::vector<Suggestion>& suggestions,
     FillingProduct main_filling_product,
     AutofillSuggestionTriggerSource trigger_source) {
-  if (!suggestion_controller_.get()) {
-    return;  // Update only if there is a controller.
+  const std::optional<SuggestionUiSessionId> session_id =
+      GetSessionIdForCurrentAutofillSuggestions();
+  if (!session_id) {
+    return;  // Update only if there is UI showing.
   }
 
   // When a form changes dynamically, `suggestion_controller_` may hold a
@@ -597,7 +610,7 @@
 
   // Calling show will reuse the existing view automatically.
   suggestion_controller_->Show(
-      suggestions, trigger_source,
+      *session_id, suggestions, trigger_source,
       ShouldAutofillPopupAutoselectFirstSuggestion(trigger_source));
 }
 
@@ -779,6 +792,7 @@
 }
 
 void ChromeAutofillClient::ShowAutofillSuggestionsImpl(
+    SuggestionUiSessionId session_id,
     const PopupOpenArgs& open_args,
     base::WeakPtr<AutofillSuggestionDelegate> delegate) {
   // Convert element_bounds to be in screen space.
@@ -795,7 +809,7 @@
       open_args.form_control_ax_id);
 
   suggestion_controller_->Show(
-      open_args.suggestions, open_args.trigger_source,
+      session_id, open_args.suggestions, open_args.trigger_source,
       ShouldAutofillPopupAutoselectFirstSuggestion(open_args.trigger_source));
 
   // When testing, try to keep popup open when the reason to hide is one of:
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 99efc81..4e6f701a 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -128,7 +128,7 @@
       const AutofillProfile* original_profile,
       bool is_migration_to_account,
       AddressProfileSavePromptCallback callback) override;
-  void ShowAutofillSuggestions(
+  SuggestionUiSessionId ShowAutofillSuggestions(
       const PopupOpenArgs& open_args,
       base::WeakPtr<AutofillSuggestionDelegate> delegate) override;
   void UpdateAutofillDataListValues(
@@ -136,6 +136,8 @@
   base::span<const Suggestion> GetAutofillSuggestions() const override;
   void PinAutofillSuggestions() override;
   std::optional<PopupScreenLocation> GetPopupScreenLocation() const override;
+  std::optional<SuggestionUiSessionId>
+  GetSessionIdForCurrentAutofillSuggestions() const override;
   void UpdateAutofillSuggestions(
       const std::vector<Suggestion>& suggestions,
       FillingProduct main_filling_product,
@@ -199,6 +201,7 @@
   Profile* GetProfile() const;
   bool SupportsConsentlessExecution(const url::Origin& origin);
   void ShowAutofillSuggestionsImpl(
+      SuggestionUiSessionId session_id,
       const PopupOpenArgs& open_args,
       base::WeakPtr<AutofillSuggestionDelegate> delegate);
   base::WeakPtr<ChromeAutofillClient> GetWeakPtr();
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client_interactive_uitest.cc b/chrome/browser/ui/autofill/chrome_autofill_client_interactive_uitest.cc
index a4257f1..171c451 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client_interactive_uitest.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client_interactive_uitest.cc
@@ -18,6 +18,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
 #include "components/autofill/core/browser/browser_autofill_manager_test_api.h"
+#include "components/autofill/core/browser/filling_product.h"
 #include "components/autofill/core/browser/ui/popup_open_enums.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/aliases.h"
@@ -34,19 +35,32 @@
 #include "url/gurl.h"
 
 namespace autofill {
+
 namespace {
 
-class MockAutofillExternalDelegate : public AutofillExternalDelegate {
+using ::testing::Not;
+using ::testing::Optional;
+using ::testing::Return;
+
+class TestAutofillExternalDelegate : public AutofillExternalDelegate {
  public:
-  explicit MockAutofillExternalDelegate(
+  explicit TestAutofillExternalDelegate(
       BrowserAutofillManager* autofill_manager)
       : AutofillExternalDelegate(autofill_manager) {}
-  ~MockAutofillExternalDelegate() override = default;
+  ~TestAutofillExternalDelegate() override = default;
 
-  MOCK_METHOD(void,
-              OnSuggestionsShown,
-              (base::span<const Suggestion>),
-              (override));
+  void OnSuggestionsShown(base::span<const Suggestion>) override {
+    ++show_counter_;
+  }
+
+  FillingProduct GetMainFillingProduct() const override {
+    return FillingProduct::kAutocomplete;
+  }
+
+  int show_counter() const { return show_counter_; }
+
+ private:
+  int show_counter_ = 0;
 };
 
 // This test class is needed to make the constructor public.
@@ -69,6 +83,10 @@
   void SetUpOnMainThread() override {
     ASSERT_TRUE(
         ui_test_utils::NavigateToURL(browser(), GURL("http://test.com")));
+
+    test_api(browser_autofill_manager())
+        .SetExternalDelegate(std::make_unique<TestAutofillExternalDelegate>(
+            &browser_autofill_manager()));
   }
 
   TestChromeAutofillClient* client() {
@@ -87,6 +105,37 @@
     return browser()->tab_strip_model()->GetActiveWebContents();
   }
 
+  AutofillClient::SuggestionUiSessionId ShowSuggestions(
+      const gfx::RectF& bounds) {
+    return client()->ShowAutofillSuggestions(
+        ChromeAutofillClient::PopupOpenArgs(
+            bounds, base::i18n::TextDirection::LEFT_TO_RIGHT,
+            {Suggestion(u"test")},
+            AutofillSuggestionTriggerSource::kFormControlElementClicked,
+            /*form_control_ax_id=*/0, PopupAnchorType::kField),
+        test_api(browser_autofill_manager())
+            .external_delegate()
+            ->GetWeakPtrForTest());
+  }
+
+  // Waits until the suggestions have been at least once more since calling this
+  // function.
+  void WaitUntilSuggestionsHaveBeenShown(
+      const base::Location& location = FROM_HERE) {
+    EXPECT_TRUE(base::test::RunUntil([this, initial_count =
+                                                suggestion_show_counter()]() {
+      return suggestion_show_counter() > initial_count;
+    })) << location.ToString()
+        << ": Showing Autofill suggestions timed out.";
+  }
+
+  // Returns show many times the suggestions have been shown or updated.
+  int suggestion_show_counter() {
+    return static_cast<TestAutofillExternalDelegate*>(
+               test_api(browser_autofill_manager()).external_delegate())
+        ->show_counter();
+  }
+
  private:
   test::AutofillBrowserTestEnvironment autofill_test_environment_;
   feature_engagement::test::ScopedIphFeatureList iph_feature_list_;
@@ -104,30 +153,10 @@
   test_api(form).field(0).set_bounds(gfx::RectF(10, 10));
   client()->ShowAutofillFieldIphForManualFallbackFeature(form.fields()[0]);
 
-  auto delegate = std::make_unique<MockAutofillExternalDelegate>(
-      &browser_autofill_manager());
-
-  bool popup_shown = false;
-  EXPECT_CALL(*delegate, OnSuggestionsShown).WillOnce([&] {
-    popup_shown = true;
-  });
-
-  base::WeakPtr<AutofillExternalDelegate> weak_delegate =
-      delegate->GetWeakPtrForTest();
-  test_api(browser_autofill_manager()).SetExternalDelegate(std::move(delegate));
-
   // Set the bounds such that the Autofill Popup would overlap with the IPH (the
   // IPH is displayed right below `form.fields[0]`, whose bounds are set above).
-  ChromeAutofillClient::PopupOpenArgs open_args(
-      gfx::RectF(100, 100), base::i18n::TextDirection::LEFT_TO_RIGHT,
-      {Suggestion(u"test")},
-      AutofillSuggestionTriggerSource::kFormControlElementClicked,
-      /*form_control_ax_id=*/0, PopupAnchorType::kField);
-  client()->ShowAutofillSuggestions(open_args, weak_delegate);
-
-  // Showing the Autofill Popup and hiding the IPH are asynchronous tasks.
-  EXPECT_TRUE(base::test::RunUntil([&]() { return popup_shown; }))
-      << "Showing the Autofill Popup timed out.";
+  ShowSuggestions(/*bounds=*/gfx::RectF(100, 100));
+  WaitUntilSuggestionsHaveBeenShown();
 
   EXPECT_FALSE(chrome::FindBrowserWithTab(web_contents())
                    ->window()
@@ -135,5 +164,41 @@
                        feature_engagement::kIPHAutofillManualFallbackFeature));
 }
 
+IN_PROC_BROWSER_TEST_F(ChromeAutofillClientBrowserTest, SuggestionUiSessionId) {
+  // Before a popup is showing, no identifier is returned.
+  EXPECT_EQ(client()->GetSessionIdForCurrentAutofillSuggestions(),
+            std::nullopt);
+
+  // Showing suggestions leads (asynchronously) to showing a popup with the
+  // identifier returned by ShowAutofillSuggestions.
+  const AutofillClient::SuggestionUiSessionId first_id =
+      ShowSuggestions(gfx::RectF(50, 50));
+  WaitUntilSuggestionsHaveBeenShown();
+  EXPECT_THAT(client()->GetSessionIdForCurrentAutofillSuggestions(),
+              std::make_optional(first_id));
+
+  const AutofillClient::SuggestionUiSessionId second_id =
+      ShowSuggestions(gfx::RectF(60, 60));
+  EXPECT_NE(first_id, second_id);
+  // Since showing suggestions is asynchronous, the identifier returned by
+  // ShowAutofillSuggestions can be different from the one currently showing.
+  EXPECT_THAT(client()->GetSessionIdForCurrentAutofillSuggestions(),
+              Not(Optional(second_id)));
+  // But once the new popup has been shown, they will be the same.
+  WaitUntilSuggestionsHaveBeenShown();
+  EXPECT_THAT(client()->GetSessionIdForCurrentAutofillSuggestions(),
+              Optional(second_id));
+
+  // Updating the suggestions does not lead to a new identifier. Note that
+  // updating the suggestions is synchronous.
+  const int old_count = suggestion_show_counter();
+  client()->UpdateAutofillSuggestions(
+      {Suggestion(u"other text")}, FillingProduct::kAutocomplete,
+      AutofillSuggestionTriggerSource::kUnspecified);
+  EXPECT_GT(suggestion_show_counter(), old_count);
+  EXPECT_THAT(client()->GetSessionIdForCurrentAutofillSuggestions(),
+              Optional(second_id));
+}
+
 }  // namespace
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/mock_autofill_popup_controller.h b/chrome/browser/ui/autofill/mock_autofill_popup_controller.h
index 5267c5e..ee06cb89 100644
--- a/chrome/browser/ui/autofill/mock_autofill_popup_controller.h
+++ b/chrome/browser/ui/autofill/mock_autofill_popup_controller.h
@@ -100,10 +100,15 @@
   MOCK_METHOD(void, HideSubPopup, (), (override));
   MOCK_METHOD(void,
               Show,
-              (std::vector<Suggestion>,
+              (UiSessionId,
+               std::vector<Suggestion>,
                AutofillSuggestionTriggerSource,
                AutoselectFirstSuggestion),
               (override));
+  MOCK_METHOD(std::optional<AutofillSuggestionController::UiSessionId>,
+              GetUiSessionId,
+              (),
+              (const override));
   MOCK_METHOD(void, SetKeepPopupOpenForTesting, (bool), (override));
   MOCK_METHOD(void,
               UpdateDataListValues,
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.h b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.h
index 164d88c3..8bf074a 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.h
+++ b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.h
@@ -11,9 +11,8 @@
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
 #include "chrome/browser/ui/bookmarks/bookmark_stats.h"
-#include "chrome/browser/ui/simple_message_box.h"
-#include "chrome/browser/ui/tabs/tab_group.h"
 #include "components/page_load_metrics/browser/navigation_handle_user_data.h"
+#include "components/tab_groups/tab_group_id.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/native_widget_types.h"
 
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 6090190..7fb4ef5 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -316,6 +316,8 @@
         browser_params.trusted_source = params.trusted_source;
         browser_params.initial_bounds = params.window_features.bounds;
         browser_params.initial_origin_specified = GetOriginSpecified(params);
+        browser_params.can_maximize = !params.is_tab_modal_popup;
+        browser_params.can_fullscreen = !params.is_tab_modal_popup;
         return {Browser::Create(browser_params), -1};
       }
       Browser::CreateParams browser_params =
diff --git a/chrome/browser/ui/browser_window/BUILD.gn b/chrome/browser/ui/browser_window/BUILD.gn
index e63eff3..5f595cc2 100644
--- a/chrome/browser/ui/browser_window/BUILD.gn
+++ b/chrome/browser/ui/browser_window/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+assert(is_win || is_mac || is_linux || is_chromeos)
+
 # This is the public interface for a browser window. Most features in
 # //chrome/browser depend on this interface, and thus to prevent circular
 # dependencies this interface should not depend on anything else in //chrome.
@@ -16,3 +18,23 @@
     "//ui/base:types",
   ]
 }
+
+source_set("impl") {
+  sources = [ "browser_window_features.cc" ]
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+  deps = [
+    ":browser_window",
+    "//chrome/browser/extensions",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui:ui_features",
+    "//chrome/browser/ui/commerce",
+    "//chrome/browser/ui/lens",
+    "//chrome/browser/ui/toasts",
+    "//chrome/browser/ui/views/side_panel",
+    "//chrome/browser/ui/views/toolbar",
+    "//components/commerce/core:feature_list",
+    "//components/lens:features",
+    "//components/profile_metrics",
+    "//components/saved_tab_groups:model",
+  ]
+}
diff --git a/chrome/browser/ui/commerce/BUILD.gn b/chrome/browser/ui/commerce/BUILD.gn
new file mode 100644
index 0000000..2377e616
--- /dev/null
+++ b/chrome/browser/ui/commerce/BUILD.gn
@@ -0,0 +1,120 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_win || is_mac || is_linux || is_chromeos)
+
+source_set("commerce") {
+  sources = [
+    "commerce_page_action_controller.h",
+    "commerce_prompt.h",
+    "commerce_ui_tab_helper.h",
+    "discounts_page_action_controller.h",
+    "price_tracking_page_action_controller.h",
+    "product_specifications_entry_point_controller.h",
+    "product_specifications_page_action_controller.h",
+  ]
+  public_deps = [
+    "//base",
+    "//chrome/browser/cart:mojo_bindings",
+    "//chrome/browser/ui/page_action:icon_type",
+    "//components/commerce/core:commerce_types",
+    "//components/commerce/core:shopping_service",
+    "//components/commerce/core/compare",
+    "//components/commerce/core/product_specifications",
+    "//components/commerce/core/subscriptions",
+    "//components/feature_engagement/public",
+    "//components/image_fetcher/core",
+    "//components/prefs",
+    "//content/public/browser",
+    "//ui/gfx",
+  ]
+}
+
+source_set("impl") {
+  sources = [
+    "commerce_page_action_controller.cc",
+    "commerce_ui_tab_helper.cc",
+    "discounts_page_action_controller.cc",
+    "price_tracking_page_action_controller.cc",
+    "product_specifications_entry_point_controller.cc",
+    "product_specifications_page_action_controller.cc",
+  ]
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+
+  deps = [
+    ":commerce",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/browser_window",
+    "//chrome/browser/ui/views/side_panel",
+    "//chrome/common:constants",
+    "//components/bookmarks/browser",
+    "//components/commerce/core:commerce_constants",
+    "//components/commerce/core:feature_list",
+    "//components/commerce/core:feature_utils",
+    "//components/commerce/core:metrics",
+    "//components/commerce/core:pref_names",
+    "//components/commerce/core:utils",
+    "//components/vector_icons",
+    "//services/metrics/public/cpp:gen_ukm_builders",
+    "//services/metrics/public/cpp:metrics_cpp",
+    "//services/metrics/public/cpp:ukm_builders",
+    "//ui/base",
+    "//ui/base/metadata",
+    "//ui/views",
+    "//url",
+  ]
+}
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "mock_commerce_ui_tab_helper.cc",
+    "mock_commerce_ui_tab_helper.h",
+  ]
+
+  deps = [
+    ":commerce",
+    "//base",
+    "//chrome/browser/ui/tabs",
+    "//skia:skia_core_public_headers",
+    "//testing/gmock",
+    "//ui/views",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "commerce_ui_tab_helper_unittest.cc",
+    "discounts_page_action_controller_unittest.cc",
+    "price_tracking_page_action_controller_unittest.cc",
+    "product_specifications_page_action_controller_unittest.cc",
+  ]
+  deps = [
+    ":commerce",
+    "//base/test:test_support",
+    "//chrome/test:test_support",
+    "//components/commerce/core:account_checker_test_support",
+    "//components/commerce/core:feature_list",
+    "//components/commerce/core:shopping_service_test_support",
+    "//components/feature_engagement/test:test_support",
+    "//components/image_fetcher/core:test_support",
+    "//components/image_fetcher/core:test_support",
+  ]
+}
+
+source_set("browser_tests") {
+  testonly = true
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  sources = [ "product_specifications_entry_point_controller_browsertest.cc" ]
+  deps = [
+    ":commerce",
+    "//chrome/browser/ui/browser_window:browser_window",
+    "//chrome/test:test_support",
+    "//components/commerce/core:account_checker_test_support",
+    "//components/commerce/core:shopping_service_test_support",
+    "//components/commerce/core:utils",
+  ]
+}
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 8b76c3b1..217f3e60 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -97,7 +97,7 @@
 #if BUILDFLAG(IS_MAC)
 #include "base/apple/bundle_locations.h"
 #include "base/mac/mac_util.h"
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
+#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
 #include "chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
 #endif
@@ -1230,10 +1230,10 @@
       base::UserMetricsAction("Media.ShowSystemMediaPermissionBubble"));
   int title_id = 0;
   if (MicrophoneAccessed() && CameraAccessed() &&
-      (system_media_permissions::CheckSystemVideoCapturePermission() ==
-           system_media_permissions::SystemPermission::kDenied ||
-       system_media_permissions::CheckSystemAudioCapturePermission() ==
-           system_media_permissions::SystemPermission::kDenied)) {
+      (system_permission_settings::CheckSystemVideoCapturePermission() ==
+           system_permission_settings::SystemPermission::kDenied ||
+       system_permission_settings::CheckSystemAudioCapturePermission() ==
+           system_permission_settings::SystemPermission::kDenied)) {
     title_id = IDS_CAMERA_MIC_TURNED_OFF_IN_MACOS;
     AddListItem(ContentSettingBubbleModel::ListItem(
         &vector_icons::kVideocamIcon, l10n_util::GetStringUTF16(IDS_CAMERA),
@@ -1242,15 +1242,15 @@
         &vector_icons::kMicIcon, l10n_util::GetStringUTF16(IDS_MIC),
         l10n_util::GetStringUTF16(IDS_TURNED_OFF), false, true, 1));
   } else if (CameraAccessed() &&
-             system_media_permissions::CheckSystemVideoCapturePermission() ==
-                 system_media_permissions::SystemPermission::kDenied) {
+             system_permission_settings::CheckSystemVideoCapturePermission() ==
+                 system_permission_settings::SystemPermission::kDenied) {
     title_id = IDS_CAMERA_TURNED_OFF_IN_MACOS;
     AddListItem(ContentSettingBubbleModel::ListItem(
         &vector_icons::kVideocamIcon, l10n_util::GetStringUTF16(IDS_CAMERA),
         l10n_util::GetStringUTF16(IDS_TURNED_OFF), false, true, 0));
   } else if (MicrophoneAccessed() &&
-             system_media_permissions::CheckSystemAudioCapturePermission() ==
-                 system_media_permissions::SystemPermission::kDenied) {
+             system_permission_settings::CheckSystemAudioCapturePermission() ==
+                 system_permission_settings::SystemPermission::kDenied) {
     title_id = IDS_MIC_TURNED_OFF_IN_MACOS;
     AddListItem(ContentSettingBubbleModel::ListItem(
         &vector_icons::kMicIcon, l10n_util::GetStringUTF16(IDS_MIC),
@@ -1266,11 +1266,11 @@
 
 bool ContentSettingMediaStreamBubbleModel::ShouldShowSystemMediaPermissions() {
 #if BUILDFLAG(IS_MAC)
-  return (((system_media_permissions::CheckSystemVideoCapturePermission() ==
-                system_media_permissions::SystemPermission::kDenied &&
+  return (((system_permission_settings::CheckSystemVideoCapturePermission() ==
+                system_permission_settings::SystemPermission::kDenied &&
             CameraAccessed() && !CameraBlocked()) ||
-           (system_media_permissions::CheckSystemAudioCapturePermission() ==
-                system_media_permissions::SystemPermission::kDenied &&
+           (system_permission_settings::CheckSystemAudioCapturePermission() ==
+                system_permission_settings::SystemPermission::kDenied &&
             MicrophoneAccessed() && !MicrophoneBlocked())) &&
           !(CameraAccessed() && CameraBlocked()) &&
           !(MicrophoneAccessed() && MicrophoneBlocked()));
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.cc b/chrome/browser/ui/content_settings/content_setting_image_model.cc
index f872b320..dbd89e86 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model.cc
@@ -59,7 +59,6 @@
 
 #if BUILDFLAG(IS_MAC)
 #include "chrome/browser/browser_process_platform_part.h"
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
 #include "chrome/browser/web_applications/os_integration/mac/app_shim_registry.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
 #endif
@@ -955,26 +954,26 @@
 bool ContentSettingMediaImageModel::
     DidCameraAccessFailBecauseOfSystemLevelBlock() {
   return (IsCamAccessed() && !IsCameraBlockedOnSiteLevel() &&
-          system_media_permissions::CheckSystemVideoCapturePermission() ==
-              system_media_permissions::SystemPermission::kDenied);
+          system_permission_settings::IsDenied(
+              ContentSettingsType::MEDIASTREAM_CAMERA));
 }
 
 bool ContentSettingMediaImageModel::
     DidMicAccessFailBecauseOfSystemLevelBlock() {
   return (IsMicAccessed() && !IsMicBlockedOnSiteLevel() &&
-          system_media_permissions::CheckSystemAudioCapturePermission() ==
-              system_media_permissions::SystemPermission::kDenied);
+          system_permission_settings::IsDenied(
+              ContentSettingsType::MEDIASTREAM_MIC));
 }
 
 bool ContentSettingMediaImageModel::IsCameraAccessPendingOnSystemLevelPrompt() {
-  return (system_media_permissions::CheckSystemVideoCapturePermission() ==
-              system_media_permissions::SystemPermission::kNotDetermined &&
+  return (system_permission_settings::CanPrompt(
+              ContentSettingsType::MEDIASTREAM_CAMERA) &&
           IsCamAccessed() && !IsCameraBlockedOnSiteLevel());
 }
 
 bool ContentSettingMediaImageModel::IsMicAccessPendingOnSystemLevelPrompt() {
-  return (system_media_permissions::CheckSystemAudioCapturePermission() ==
-              system_media_permissions::SystemPermission::kNotDetermined &&
+  return (system_permission_settings::CanPrompt(
+              ContentSettingsType::MEDIASTREAM_MIC) &&
           IsMicAccessed() && !IsMicBlockedOnSiteLevel());
 }
 
diff --git a/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm b/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm
index a654b1d..0f6c315c 100644
--- a/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm
+++ b/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm
@@ -2,15 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/content_settings/content_setting_image_model.h"
-
 #import <AVFoundation/AVFoundation.h>
 
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/content_settings/page_specific_content_settings_delegate.h"
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
+#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/content_settings/content_setting_image_model.h"
 #include "chrome/browser/ui/content_settings/media_authorization_wrapper_test.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
@@ -86,7 +85,7 @@
       ContentSettingImageModel::CreateForContentType(
           ContentSettingImageModel::ImageType::MEDIASTREAM);
   MediaAuthorizationWrapperTest auth_wrapper;
-  system_media_permissions::SetMediaAuthorizationWrapperForTesting(
+  system_permission_settings::SetMediaAuthorizationWrapperForTesting(
       &auth_wrapper);
 
   // Camera allowed per site: Test for system level permissions.
@@ -174,7 +173,8 @@
           kTestOrigin, {PageSpecificContentSettings::kCameraAccessed,
                         PageSpecificContentSettings::kCameraBlocked});
       content_setting_image_model->Update(web_contents());
-      ExpectImageModelState(*content_setting_image_model, /*is_visible=*/true,
+      ExpectImageModelState(*content_setting_image_model,
+                            /*is_visible=*/true,
                             /*has_icon=*/true,
                             l10n_util::GetStringUTF16(IDS_CAMERA_BLOCKED), 0,
                             &gfx::kNoneIcon);
@@ -186,7 +186,8 @@
           kTestOrigin, {PageSpecificContentSettings::kMicrophoneAccessed,
                         PageSpecificContentSettings::kMicrophoneBlocked});
       content_setting_image_model->Update(web_contents());
-      ExpectImageModelState(*content_setting_image_model, /*is_visible=*/true,
+      ExpectImageModelState(*content_setting_image_model,
+                            /*is_visible=*/true,
                             /*has_icon=*/true,
                             l10n_util::GetStringUTF16(IDS_MICROPHONE_BLOCKED),
                             0, &gfx::kNoneIcon);
@@ -201,7 +202,8 @@
                         PageSpecificContentSettings::kMicrophoneBlocked});
       content_setting_image_model->Update(web_contents());
       ExpectImageModelState(
-          *content_setting_image_model, /*is_visible=*/true, /*has_icon=*/true,
+          *content_setting_image_model, /*is_visible=*/true,
+          /*has_icon=*/true,
           l10n_util::GetStringUTF16(IDS_MICROPHONE_CAMERA_BLOCKED), 0,
           &gfx::kNoneIcon);
     }
diff --git a/chrome/browser/ui/content_settings/media_authorization_wrapper_test.h b/chrome/browser/ui/content_settings/media_authorization_wrapper_test.h
index 8551dbc..35d09190 100644
--- a/chrome/browser/ui/content_settings/media_authorization_wrapper_test.h
+++ b/chrome/browser/ui/content_settings/media_authorization_wrapper_test.h
@@ -8,10 +8,10 @@
 #import <AVFoundation/AVFoundation.h>
 
 #include "base/functional/callback.h"
-#include "chrome/browser/media/webrtc/media_authorization_wrapper_mac.h"
+#include "chrome/browser/permissions/system/media_authorization_wrapper_mac.h"
 
 class MediaAuthorizationWrapperTest final
-    : public system_media_permissions::MediaAuthorizationWrapper {
+    : public system_permission_settings::MediaAuthorizationWrapper {
  public:
   MediaAuthorizationWrapperTest() = default;
 
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller.cc b/chrome/browser/ui/lens/lens_overlay_query_controller.cc
index 7e5281e..7a766c8 100644
--- a/chrome/browser/ui/lens/lens_overlay_query_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_query_controller.cc
@@ -784,7 +784,7 @@
     // TOOD(b/362816047): Send correct LensOverlayInteractionRequestMetadata,
     // once the server is ready for it.
     interaction_request_metadata.set_type(
-        lens::LensOverlayInteractionRequestMetadata::UNKNOWN);
+        lens::LensOverlayInteractionRequestMetadata::CONTEXTUAL_SEARCH_QUERY);
     interaction_request_metadata.mutable_query_metadata()
         ->mutable_text_query()
         ->set_query(*query_text);
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller_unittest.cc b/chrome/browser/ui/lens/lens_overlay_query_controller_unittest.cc
index b611ff0..855034e 100644
--- a/chrome/browser/ui/lens/lens_overlay_query_controller_unittest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_query_controller_unittest.cc
@@ -275,29 +275,24 @@
   task_environment_.RunUntilIdle();
   query_controller.EndQuery();
   ASSERT_TRUE(full_image_response_future.IsReady());
-  ASSERT_EQ(query_controller.sent_objects_request_.request_context()
-                .request_id()
-                .sequence_id(),
+
+  // Check initial fetch objects request is correct.
+  auto sent_object_request = query_controller.sent_objects_request_;
+  ASSERT_EQ(sent_object_request.request_context().request_id().sequence_id(),
             1);
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .width(),
-            100);
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .height(),
-            100);
-  ASSERT_EQ(query_controller.sent_objects_request_.request_context()
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().width(), 100);
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().height(), 100);
+  ASSERT_EQ(sent_object_request.request_context()
                 .client_context()
                 .locale_context()
                 .language(),
             kLocale);
-  ASSERT_EQ(query_controller.sent_objects_request_.request_context()
+  ASSERT_EQ(sent_object_request.request_context()
                 .client_context()
                 .locale_context()
                 .region(),
             kRegion);
-  ASSERT_EQ(query_controller.sent_objects_request_.request_context()
+  ASSERT_EQ(sent_object_request.request_context()
                 .client_context()
                 .locale_context()
                 .time_zone(),
@@ -350,64 +345,51 @@
   task_environment_.RunUntilIdle();
   query_controller.EndQuery();
 
-  std::string actual_start_time;
+  std::string unused_start_time;
   bool has_start_time =
       net::GetValueForKeyInQuery(GURL(url_response_future.Get().url()),
-                                 kStartTimeQueryParam, &actual_start_time);
+                                 kStartTimeQueryParam, &unused_start_time);
 
   ASSERT_TRUE(full_image_response_future.IsReady());
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .width(),
-            100);
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .height(),
-            100);
+
+  // Check the initial fetch objects request.
+  auto sent_object_request = query_controller.sent_objects_request_;
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().width(), 100);
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().height(), 100);
   ASSERT_TRUE(url_response_future.Get().has_url());
   ASSERT_EQ(GetSelectionTypeFromUrl(url_response_future.Get().url()),
             lens::REGION_SEARCH);
   ASSERT_EQ(interaction_data_response_future.Get().suggest_signals(),
             kTestSuggestSignals);
-  ASSERT_EQ(query_controller.sent_objects_request_.request_context()
-                .request_id()
-                .sequence_id(),
+  ASSERT_EQ(sent_object_request.request_context().request_id().sequence_id(),
             1);
-  ASSERT_EQ(query_controller.sent_interaction_request_.request_context()
-                .request_id()
-                .sequence_id(),
-            2);
+
+  // Verify the interaction request.
+  auto sent_interaction_request = query_controller.sent_interaction_request_;
   ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .type(),
-      lens::LensOverlayInteractionRequestMetadata::REGION_SEARCH);
-  ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .selection_metadata()
-          .region()
-          .region()
-          .center_x(),
-      30);
-  ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .selection_metadata()
-          .region()
-          .region()
-          .center_y(),
-      40);
-  ASSERT_EQ(query_controller.sent_interaction_request_.image_crop()
-                .zoomed_crop()
-                .crop()
+      sent_interaction_request.request_context().request_id().sequence_id(), 2);
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata().type(),
+            lens::LensOverlayInteractionRequestMetadata::REGION_SEARCH);
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata()
+                .selection_metadata()
+                .region()
+                .region()
                 .center_x(),
             30);
-  ASSERT_EQ(query_controller.sent_interaction_request_.image_crop()
-                .zoomed_crop()
-                .crop()
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata()
+                .selection_metadata()
+                .region()
+                .region()
                 .center_y(),
             40);
-  ASSERT_FALSE(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .has_query_metadata());
+  ASSERT_EQ(
+      sent_interaction_request.image_crop().zoomed_crop().crop().center_x(),
+      30);
+  ASSERT_EQ(
+      sent_interaction_request.image_crop().zoomed_crop().crop().center_y(),
+      40);
+  ASSERT_FALSE(sent_interaction_request.interaction_request_metadata()
+                   .has_query_metadata());
   ASSERT_TRUE(has_start_time);
   ASSERT_EQ(query_controller.num_gen204_pings_sent_, 1);
   CheckGen204IdsMatch(query_controller.sent_client_logs_,
@@ -458,78 +440,59 @@
   task_environment_.RunUntilIdle();
   query_controller.EndQuery();
 
-  std::string actual_start_time;
+  std::string unused_start_time;
   bool has_start_time =
       net::GetValueForKeyInQuery(GURL(url_response_future.Get().url()),
-                                 kStartTimeQueryParam, &actual_start_time);
+                                 kStartTimeQueryParam, &unused_start_time);
 
   ASSERT_TRUE(full_image_response_future.IsReady());
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .width(),
-            1000);
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .height(),
-            1000);
+
+  // Check initial fetch objects request is correct.
+  auto sent_object_request = query_controller.sent_objects_request_;
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().width(), 1000);
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().height(), 1000);
   ASSERT_TRUE(url_response_future.Get().has_url());
   ASSERT_EQ(GetSelectionTypeFromUrl(url_response_future.Get().url()),
             lens::REGION_SEARCH);
   ASSERT_EQ(interaction_data_response_future.Get().suggest_signals(),
             kTestSuggestSignals);
-  ASSERT_EQ(query_controller.sent_objects_request_.request_context()
-                .request_id()
-                .sequence_id(),
+  ASSERT_EQ(sent_object_request.request_context().request_id().sequence_id(),
             1);
-  ASSERT_EQ(query_controller.sent_interaction_request_.request_context()
-                .request_id()
-                .sequence_id(),
-            2);
+
+  // Verify the interaction request.
+  auto sent_interaction_request = query_controller.sent_interaction_request_;
   ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .type(),
-      lens::LensOverlayInteractionRequestMetadata::REGION_SEARCH);
-  ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .selection_metadata()
-          .region()
-          .region()
-          .center_x(),
-      50);
-  ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .selection_metadata()
-          .region()
-          .region()
-          .center_y(),
-      50);
-  ASSERT_EQ(query_controller.sent_interaction_request_.image_crop()
-                .zoomed_crop()
-                .crop()
+      sent_interaction_request.request_context().request_id().sequence_id(), 2);
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata().type(),
+            lens::LensOverlayInteractionRequestMetadata::REGION_SEARCH);
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata()
+                .selection_metadata()
+                .region()
+                .region()
                 .center_x(),
             50);
-  ASSERT_EQ(query_controller.sent_interaction_request_.image_crop()
-                .zoomed_crop()
-                .crop()
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata()
+                .selection_metadata()
+                .region()
+                .region()
                 .center_y(),
             50);
-  ASSERT_EQ(query_controller.sent_interaction_request_.image_crop()
-                .zoomed_crop()
-                .crop()
-                .center_y(),
-            50);
-  ASSERT_EQ(query_controller.sent_interaction_request_.image_crop()
-                .zoomed_crop()
-                .crop()
-                .center_y(),
-            50);
+  ASSERT_EQ(
+      sent_interaction_request.image_crop().zoomed_crop().crop().center_x(),
+      50);
+  ASSERT_EQ(
+      sent_interaction_request.image_crop().zoomed_crop().crop().center_y(),
+      50);
+  ASSERT_EQ(
+      sent_interaction_request.image_crop().zoomed_crop().crop().center_y(),
+      50);
+  ASSERT_EQ(
+      sent_interaction_request.image_crop().zoomed_crop().crop().center_y(),
+      50);
   ASSERT_EQ(GetExpectedJpegBytesForBitmap(region_bitmap),
-            query_controller.sent_interaction_request_.image_crop()
-                .image()
-                .image_content());
-  ASSERT_FALSE(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .has_query_metadata());
+            sent_interaction_request.image_crop().image().image_content());
+  ASSERT_FALSE(sent_interaction_request.interaction_request_metadata()
+                   .has_query_metadata());
   ASSERT_TRUE(has_start_time);
   ASSERT_EQ(query_controller.num_gen204_pings_sent_, 1);
   CheckGen204IdsMatch(query_controller.sent_client_logs_,
@@ -578,67 +541,54 @@
   task_environment_.RunUntilIdle();
   query_controller.EndQuery();
 
-  std::string actual_start_time;
+  std::string unused_start_time;
   bool has_start_time =
       net::GetValueForKeyInQuery(GURL(url_response_future.Get().url()),
-                                 kStartTimeQueryParam, &actual_start_time);
+                                 kStartTimeQueryParam, &unused_start_time);
 
   ASSERT_TRUE(full_image_response_future.IsReady());
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .width(),
-            100);
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .height(),
-            100);
+
+  // Check initial fetch objects request is correct.
+  auto sent_object_request = query_controller.sent_objects_request_;
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().width(), 100);
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().height(), 100);
   ASSERT_TRUE(url_response_future.Get().has_url());
   ASSERT_EQ(GetSelectionTypeFromUrl(url_response_future.Get().url()),
             lens::MULTIMODAL_SEARCH);
   ASSERT_EQ(interaction_data_response_future.Get().suggest_signals(),
             kTestSuggestSignals);
-  ASSERT_EQ(query_controller.sent_objects_request_.request_context()
-                .request_id()
-                .sequence_id(),
+  ASSERT_EQ(sent_object_request.request_context().request_id().sequence_id(),
             1);
-  ASSERT_EQ(query_controller.sent_interaction_request_.request_context()
-                .request_id()
-                .sequence_id(),
-            2);
+
+  // Verify the interaction request.
+  auto sent_interaction_request = query_controller.sent_interaction_request_;
   ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .type(),
-      lens::LensOverlayInteractionRequestMetadata::REGION_SEARCH);
-  ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .selection_metadata()
-          .region()
-          .region()
-          .center_x(),
-      30);
-  ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .selection_metadata()
-          .region()
-          .region()
-          .center_y(),
-      40);
-  ASSERT_EQ(query_controller.sent_interaction_request_.image_crop()
-                .zoomed_crop()
-                .crop()
+      sent_interaction_request.request_context().request_id().sequence_id(), 2);
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata().type(),
+            lens::LensOverlayInteractionRequestMetadata::REGION_SEARCH);
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata()
+                .selection_metadata()
+                .region()
+                .region()
                 .center_x(),
             30);
-  ASSERT_EQ(query_controller.sent_interaction_request_.image_crop()
-                .zoomed_crop()
-                .crop()
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata()
+                .selection_metadata()
+                .region()
+                .region()
                 .center_y(),
             40);
   ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .query_metadata()
-          .text_query()
-          .query(),
-      kTestQueryText);
+      sent_interaction_request.image_crop().zoomed_crop().crop().center_x(),
+      30);
+  ASSERT_EQ(
+      sent_interaction_request.image_crop().zoomed_crop().crop().center_y(),
+      40);
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata()
+                .query_metadata()
+                .text_query()
+                .query(),
+            kTestQueryText);
   ASSERT_TRUE(has_start_time);
   ASSERT_EQ(query_controller.num_gen204_pings_sent_, 1);
   CheckGen204IdsMatch(query_controller.sent_client_logs_,
@@ -686,10 +636,10 @@
                              kSearchContextParamKey,
                              &actual_encoded_search_context);
 
-  std::string actual_start_time;
+  std::string unused_start_time;
   bool has_start_time =
       net::GetValueForKeyInQuery(GURL(url_response_future.Get().url()),
-                                 kStartTimeQueryParam, &actual_start_time);
+                                 kStartTimeQueryParam, &unused_start_time);
 
   ASSERT_TRUE(full_image_response_future.IsReady());
   ASSERT_TRUE(url_response_future.IsReady());
@@ -738,40 +688,29 @@
   task_environment_.RunUntilIdle();
   query_controller.EndQuery();
 
-  // Check initial fetch objects request is correct.
   ASSERT_TRUE(full_image_response_future.IsReady());
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .width(),
-            100);
-  ASSERT_EQ(query_controller.sent_objects_request_.image_data()
-                .image_metadata()
-                .height(),
-            100);
-  ASSERT_FALSE(
-      query_controller.sent_objects_request_.payload().content_data().empty());
-  ASSERT_EQ(query_controller.sent_objects_request_.payload().content_type(),
-            "application/pdf");
+
+  // Check initial fetch objects request is correct.
+  auto sent_object_request = query_controller.sent_objects_request_;
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().width(), 100);
+  ASSERT_EQ(sent_object_request.image_data().image_metadata().height(), 100);
+  ASSERT_FALSE(sent_object_request.payload().content_data().empty());
+  ASSERT_EQ(sent_object_request.payload().content_type(), "application/pdf");
 
   // Check interaction request is correct.
+  auto sent_interaction_request = query_controller.sent_interaction_request_;
   ASSERT_TRUE(interaction_data_response_future.IsReady());
-  ASSERT_EQ(query_controller.sent_interaction_request_.request_context()
-                .request_id()
-                .sequence_id(),
-            2);
   ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .type(),
-      lens::LensOverlayInteractionRequestMetadata::UNKNOWN);
-  ASSERT_EQ(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .query_metadata()
-          .text_query()
-          .query(),
-      kTestQueryText);
-  ASSERT_FALSE(
-      query_controller.sent_interaction_request_.interaction_request_metadata()
-          .has_selection_metadata());
+      sent_interaction_request.request_context().request_id().sequence_id(), 2);
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata().type(),
+            lens::LensOverlayInteractionRequestMetadata::CONTEXTUAL_SEARCH_QUERY);
+  ASSERT_EQ(sent_interaction_request.interaction_request_metadata()
+                .query_metadata()
+                .text_query()
+                .query(),
+            kTestQueryText);
+  ASSERT_FALSE(sent_interaction_request.interaction_request_metadata()
+                   .has_selection_metadata());
 
   // Check search URL is correct.
   ASSERT_TRUE(url_response_future.IsReady());
diff --git a/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc b/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc
index 3c41925..3851216 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc
+++ b/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc
@@ -32,6 +32,7 @@
 #include "chromeos/components/quick_answers/quick_answers_model.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
+#include "chromeos/ui/vector_icons/vector_icons.h"
 #include "components/omnibox/browser/vector_icons.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
@@ -660,7 +661,7 @@
   RichAnswersView* rich_answers_view = static_cast<RichAnswersView*>(
       rich_answers_view_widget->GetContentsView());
   expected_image_model = ui::ImageModel::FromVectorIcon(
-      omnibox::kAnswerDictionaryIcon, ui::kColorSysBaseContainerElevated,
+      chromeos::kDictionaryIcon, ui::kColorSysBaseContainerElevated,
       /*icon_size=*/kRichAnswersResultTypeIconSizeDip);
   EXPECT_TRUE(rich_answers_view->GetIconImageModelForTesting() ==
               expected_image_model);
diff --git a/chrome/browser/ui/quick_answers/ui/quick_answers_util.cc b/chrome/browser/ui/quick_answers/ui/quick_answers_util.cc
index bebafde..145634b 100644
--- a/chrome/browser/ui/quick_answers/ui/quick_answers_util.cc
+++ b/chrome/browser/ui/quick_answers/ui/quick_answers_util.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/ui/quick_answers/ui/quick_answers_text_label.h"
 #include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h"
 #include "chromeos/components/quick_answers/quick_answers_model.h"
+#include "chromeos/ui/vector_icons/vector_icons.h"
 #include "components/omnibox/browser/vector_icons.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/browser/speech/tts_controller_impl.h"
@@ -108,7 +109,7 @@
 const gfx::VectorIcon& GetResultTypeIcon(ResultType result_type) {
   switch (result_type) {
     case ResultType::kDefinitionResult:
-      return omnibox::kAnswerDictionaryIcon;
+      return chromeos::kDictionaryIcon;
     case ResultType::kTranslationResult:
       return omnibox::kAnswerTranslationIcon;
     case ResultType::kUnitConversionResult:
diff --git a/chrome/browser/ui/quick_answers/ui/quick_answers_view.cc b/chrome/browser/ui/quick_answers/ui/quick_answers_view.cc
index 55261ca9..65091c18 100644
--- a/chrome/browser/ui/quick_answers/ui/quick_answers_view.cc
+++ b/chrome/browser/ui/quick_answers/ui/quick_answers_view.cc
@@ -160,7 +160,7 @@
 
   switch (intent.value()) {
     case Intent::kDefinition:
-      return omnibox::kAnswerDictionaryIcon;
+      return chromeos::kDictionaryIcon;
     case Intent::kTranslation:
       return omnibox::kAnswerTranslationIcon;
     case Intent::kUnitConversion:
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc b/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
index b4ac05c..fb86ba5 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
+++ b/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
@@ -479,7 +479,9 @@
  public:
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
-    feature_list_.InitWithFeatures({features::kSafetyHub}, {});
+    feature_list_.InitWithFeatures(
+        {features::kSafetyHub},
+        {safe_browsing::kSafetyHubAbusiveNotificationRevocation});
     prefs()->SetBoolean(
         safety_hub_prefs::kUnusedSitePermissionsRevocationEnabled, true);
   }
diff --git a/chrome/browser/ui/safety_hub/menu_notification_unittest.cc b/chrome/browser/ui/safety_hub/menu_notification_unittest.cc
index 86c3301b..174b3fc 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_unittest.cc
+++ b/chrome/browser/ui/safety_hub/menu_notification_unittest.cc
@@ -83,7 +83,8 @@
   std::unique_ptr<UnusedSitePermissionsService> service_;
 };
 
-TEST_F(SafetyHubMenuNotificationTest, ToFromDictValue) {
+// TODO(crbug.com/364523673): This test is flaking on android pie builder.
+TEST_F(SafetyHubMenuNotificationTest, DISABLED_ToFromDictValue) {
   // Creating a mock menu notification.
   base::Time last = kPastTime + base::Days(30);
   auto notification = std::make_unique<SafetyHubMenuNotification>(
@@ -134,7 +135,8 @@
                        .GetString());
 }
 
-TEST_F(SafetyHubMenuNotificationTest, ShouldBeShown) {
+// TODO(crbug.com/364523673): This test is flaking on android pie builder.
+TEST_F(SafetyHubMenuNotificationTest, DISABLED_ShouldBeShown) {
   base::TimeDelta interval = base::Days(30);
   auto notification = std::make_unique<SafetyHubMenuNotification>(
       safety_hub::SafetyHubModuleType::UNUSED_SITE_PERMISSIONS);
@@ -222,7 +224,8 @@
   ASSERT_FALSE(other_notification->ShouldBeShown(interval));
 }
 
-TEST_F(SafetyHubMenuNotificationTest, IsCurrentlyActive) {
+// TODO(crbug.com/364523673): This test is flaking on android pie builder.
+TEST_F(SafetyHubMenuNotificationTest, DISABLED_IsCurrentlyActive) {
   auto notification = std::make_unique<SafetyHubMenuNotification>(
       safety_hub::SafetyHubModuleType::UNUSED_SITE_PERMISSIONS);
 
diff --git a/chrome/browser/ui/safety_hub/unused_site_permissions_service_unittest.cc b/chrome/browser/ui/safety_hub/unused_site_permissions_service_unittest.cc
index 5c6a29b..f399f5f 100644
--- a/chrome/browser/ui/safety_hub/unused_site_permissions_service_unittest.cc
+++ b/chrome/browser/ui/safety_hub/unused_site_permissions_service_unittest.cc
@@ -1850,7 +1850,8 @@
        UnusedSitePermissionsRevocationDisabled) {
   base::test::ScopedFeatureList scoped_feature;
   scoped_feature.InitWithFeatureStates(
-      {{content_settings::features::kSafetyCheckUnusedSitePermissions, false}});
+      {{content_settings::features::kSafetyCheckUnusedSitePermissions, false},
+       {safe_browsing::kSafetyHubAbusiveNotificationRevocation, false}});
 
   // If both kSafetyHub and kSafetyCheckUnusedSitePermissions are disabled, then
   // no auto-revocation should happen (i.e. no repeated timers should start).
diff --git a/chrome/browser/ui/toasts/BUILD.gn b/chrome/browser/ui/toasts/BUILD.gn
index 4d6d9b3aa..85c6068 100644
--- a/chrome/browser/ui/toasts/BUILD.gn
+++ b/chrome/browser/ui/toasts/BUILD.gn
@@ -42,6 +42,29 @@
   ]
 }
 
+source_set("interactive_ui_tests") {
+  testonly = true
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  sources = [ "toast_controller_interactive_ui_test.cc" ]
+  deps = [
+    ":toasts",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser",
+    "//chrome/browser:browser_process",
+    "//chrome/browser/ui:ui",
+    "//chrome/test:test_support",
+    "//components/vector_icons",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//testing/gtest",
+    "//ui/base:base",
+    "//ui/gfx",
+    "//ui/strings:ui_strings_grit",
+    "//ui/views",
+  ]
+}
+
 source_set("browser_tests") {
   testonly = true
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
diff --git a/chrome/browser/ui/toasts/toast_controller.cc b/chrome/browser/ui/toasts/toast_controller.cc
index 785da36b..a7fd685 100644
--- a/chrome/browser/ui/toasts/toast_controller.cc
+++ b/chrome/browser/ui/toasts/toast_controller.cc
@@ -5,7 +5,9 @@
 #include "chrome/browser/ui/toasts/toast_controller.h"
 
 #include <optional>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/check.h"
 #include "base/check_is_test.h"
@@ -32,9 +34,9 @@
     : browser_window_interface_(browser_window_interface),
       toast_registry_(toast_registry) {}
 
-// TODO(crbug.com/358610787): ensure that no toast is showing when the
-// destructor is called.
-ToastController::~ToastController() = default;
+ToastController::~ToastController() {
+  CloseToast();
+}
 
 bool ToastController::IsShowingToast() const {
   return current_toast_params_.has_value();
@@ -65,6 +67,45 @@
   }
 
   CloseToast();
+
+  if (IsShowingToast()) {
+    // TODO(crbug.com/358610190): Record that a toast was preempted if
+    // `next_toast_params_` already have a value.
+
+    // Queue the params since we are waiting for the previous toast to
+    // fully close before showing the next toast.
+    next_toast_params_ = std::move(params);
+  } else {
+    ShowToast(std::move(params));
+  }
+
+  return true;
+}
+
+void ToastController::ClosePersistentToast(ToastId id) {
+  CHECK(current_toast_params_.has_value());
+  CHECK_EQ(current_toast_params_.value().toast_id_, id);
+  // TODO(crbug.com/358610787): close the persistent toast and have internal
+  // state reflect that.
+  CloseToast();
+}
+
+void ToastController::OnWidgetDestroyed(views::Widget* widget) {
+  current_toast_params_ = std::nullopt;
+  toast_observer_.Reset();
+  toast_widget_ = nullptr;
+  toast_close_timer_.Stop();
+
+  // TODO(crbug.com/358610190): Record toast closed reason.
+
+  // The previous toast is destroyed so we should show the next toast.
+  if (next_toast_params_.has_value()) {
+    ShowToast(std::move(next_toast_params_.value()));
+    next_toast_params_ = std::nullopt;
+  }
+}
+
+void ToastController::ShowToast(ToastParams params) {
   current_toast_params_ = std::move(params);
 
   const ToastSpecification* current_toast_spec =
@@ -79,36 +120,19 @@
   }
 
   CreateToast(current_toast_spec);
-  return true;
-}
-
-void ToastController::ClosePersistentToast(ToastId id) {
-  CHECK(current_toast_params_.has_value());
-  CHECK_EQ(current_toast_params_.value().toast_id_, id);
-  // TODO(crbug.com/358610787): close the persistent toast and have internal
-  // state reflect that.
-  CloseToast();
-}
-
-void ToastController::OnWidgetDestroying(views::Widget* widget) {
-  // Avoid having a dangling reference when the browser window gets closed.
-  CloseToast();
 }
 
 void ToastController::CloseToast() {
   if (!IsShowingToast()) {
     return;
   }
-  if (toast_widget_) {
-    // TODO(crbug.com/358615317): Make the toast animate out and then complete
-    // the rest of the logic synchronously afterwards.
-    toast_observer_.Reset();
-    // TODO(crbug.com/358610872): Log toast close reason metric and potentially
-    // integrate with Widget::CloseReason.
-    toast_widget_->Close();
-    toast_widget_ = nullptr;
-  }
-  current_toast_params_ = std::nullopt;
+
+  CHECK(toast_widget_);
+  // TODO(crbug.com/358615317): Make the toast animate out and then complete
+  // the rest of the logic synchronously afterwards.
+  // TODO(crbug.com/358610872): Log toast close reason metric and potentially
+  // integrate with Widget::CloseReason.
+  toast_widget_->Close();
 }
 
 void ToastController::CreateToast(const ToastSpecification* spec) {
@@ -117,15 +141,21 @@
     return;
   }
   CHECK(current_toast_params_.has_value());
-  const std::u16string& toast_text = l10n_util::GetStringFUTF16(
-      spec->body_string_id(),
-      current_toast_params_.value().body_string_replacement_params_, nullptr);
   std::unique_ptr<toasts::ToastView> toast =
       std::make_unique<toasts::ToastView>(
-          browser_window_interface_->TopContainer(), toast_text, spec->icon());
+          browser_window_interface_->TopContainer(),
+          FormatString(spec->body_string_id(),
+                       current_toast_params_->body_string_replacement_params_),
+          spec->icon());
   toast_widget_ =
       views::BubbleDialogDelegateView::CreateBubble(std::move(toast));
   toast_observer_.Observe(toast_widget_);
   toast_widget_->ShowInactive();
   // TODO(crbug.com/358615317): Make the toast animate in.
 }
+
+std::u16string ToastController::FormatString(
+    int string_id,
+    std::vector<std::u16string> replacements) {
+  return l10n_util::GetStringFUTF16(string_id, replacements, nullptr);
+}
diff --git a/chrome/browser/ui/toasts/toast_controller.h b/chrome/browser/ui/toasts/toast_controller.h
index ff16e91..8fa974bd 100644
--- a/chrome/browser/ui/toasts/toast_controller.h
+++ b/chrome/browser/ui/toasts/toast_controller.h
@@ -52,15 +52,19 @@
   void ClosePersistentToast(ToastId id);
 
   // views::WidgetObserver:
-  void OnWidgetDestroying(views::Widget* widget) override;
+  void OnWidgetDestroyed(views::Widget* widget) override;
 
  private:
-  void CloseToast();
+  void ShowToast(ToastParams params);
   void CreateToast(const ToastSpecification*);
+  virtual void CloseToast();
+  std::u16string FormatString(int string_id,
+                              std::vector<std::u16string> replacement);
 
   const raw_ptr<BrowserWindowInterface> browser_window_interface_;
   const raw_ptr<const ToastRegistry> toast_registry_;
   std::optional<ToastParams> current_toast_params_;
+  std::optional<ToastParams> next_toast_params_;
   base::OneShotTimer toast_close_timer_;
 
   // Observer to check when the toast is destroyed.
diff --git a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
new file mode 100644
index 0000000..55035797
--- /dev/null
+++ b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
@@ -0,0 +1,62 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
+#include "chrome/browser/ui/toasts/api/toast_id.h"
+#include "chrome/browser/ui/toasts/toast_controller.h"
+#include "chrome/browser/ui/toasts/toast_features.h"
+#include "chrome/browser/ui/toasts/toast_view.h"
+#include "chrome/test/interaction/interactive_browser_test.h"
+#include "content/public/test/browser_test.h"
+
+class ToastControllerInteractiveTest : public InteractiveBrowserTest {
+ public:
+  void SetUp() override {
+    feature_list_.InitWithFeatures(
+        {toast_features::kToastFramework, toast_features::kLinkCopiedToast,
+         toast_features::kImageCopiedToast},
+        {});
+    InteractiveBrowserTest::SetUp();
+  }
+
+  ToastController* GetToastController() {
+    return browser()->browser_window_features()->toast_controller();
+  }
+
+  auto ShowToast(ToastParams params) {
+    return Do(
+        [&]() { GetToastController()->MaybeShowToast(std::move(params)); });
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(ToastControllerInteractiveTest, ShowEphemeralToast) {
+  RunTestSequence(ShowToast(ToastParams(ToastId::kLinkCopied)),
+                  WaitForShow(toasts::ToastView::kToastViewId), Check([=]() {
+                    return GetToastController()->IsShowingToast();
+                  }));
+}
+
+IN_PROC_BROWSER_TEST_F(ToastControllerInteractiveTest,
+                       ShowSameEphemeralToastTwice) {
+  RunTestSequence(
+      ShowToast(ToastParams(ToastId::kLinkCopied)),
+      WaitForShow(toasts::ToastView::kToastViewId),
+      Check([=]() { return GetToastController()->IsShowingToast(); }),
+      ShowToast(ToastParams(ToastId::kLinkCopied)),
+      WaitForShow(toasts::ToastView::kToastViewId),
+      Check([=]() { return GetToastController()->IsShowingToast(); }));
+}
+
+IN_PROC_BROWSER_TEST_F(ToastControllerInteractiveTest, PreemptEphemeralToast) {
+  RunTestSequence(ShowToast(ToastParams(ToastId::kLinkCopied)),
+                  WaitForShow(toasts::ToastView::kToastViewId), Check([=]() {
+                    return GetToastController()->IsShowingToast();
+                  }),
+                  ShowToast(ToastParams(ToastId::kImageCopied)));
+}
diff --git a/chrome/browser/ui/toasts/toast_controller_unittest.cc b/chrome/browser/ui/toasts/toast_controller_unittest.cc
index 097ba9f..f71df763 100644
--- a/chrome/browser/ui/toasts/toast_controller_unittest.cc
+++ b/chrome/browser/ui/toasts/toast_controller_unittest.cc
@@ -15,6 +15,24 @@
 #include "components/vector_icons/vector_icons.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+class TestToastController : public ToastController {
+ public:
+  explicit TestToastController(ToastRegistry* toast_registry)
+      : ToastController(nullptr, toast_registry) {}
+
+  // Need to override the destructor to ensure that we are calling
+  // TestToastController's CloseToast() method instead of its parent.
+  ~TestToastController() override { CloseToast(); }
+
+  void CloseToast() override {
+    if (IsShowingToast()) {
+      OnWidgetDestroyed(nullptr);
+    }
+  }
+};
+}  // namespace
+
 class ToastControllerUnitTest : public testing::Test {
  public:
   void SetUp() override {
@@ -41,7 +59,7 @@
       ToastId::kLinkCopied,
       ToastSpecification::Builder(vector_icons::kEmailIcon, 0).Build());
 
-  auto controller = std::make_unique<ToastController>(nullptr, registry);
+  auto controller = std::make_unique<TestToastController>(registry);
 
   // We should be able to show the toast because there is no toast showing.
   EXPECT_FALSE(controller->IsShowingToast());
@@ -66,7 +84,7 @@
           .AddPersistance()
           .Build());
 
-  auto controller = std::make_unique<ToastController>(nullptr, registry);
+  auto controller = std::make_unique<TestToastController>(registry);
 
   // We should be able to show the toast because there is no toast showing.
   EXPECT_TRUE(controller->CanShowToast(ToastId::kLinkCopied));
@@ -90,7 +108,7 @@
           .AddPersistance()
           .Build());
 
-  auto controller = std::make_unique<ToastController>(nullptr, registry);
+  auto controller = std::make_unique<TestToastController>(registry);
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kImageCopied)));
   EXPECT_TRUE(controller->IsShowingToast());
 
@@ -105,8 +123,7 @@
   registry->RegisterToast(
       ToastId::kLinkCopied,
       ToastSpecification::Builder(vector_icons::kEmailIcon, 0).Build());
-
-  auto controller = std::make_unique<ToastController>(nullptr, registry);
+  auto controller = std::make_unique<TestToastController>(registry);
 
   // We can show the toast again because it is an ephemeral toast.
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
@@ -117,6 +134,36 @@
   EXPECT_FALSE(controller->IsShowingToast());
 }
 
+TEST_F(ToastControllerUnitTest, CloseTimerResetsWhenToastShown) {
+  ToastRegistry* const registry = toast_registry();
+  registry->RegisterToast(
+      ToastId::kLinkCopied,
+      ToastSpecification::Builder(vector_icons::kEmailIcon, 0).Build());
+  registry->RegisterToast(
+      ToastId::kImageCopied,
+      ToastSpecification::Builder(vector_icons::kEmailIcon, 0).Build());
+
+  auto controller = std::make_unique<TestToastController>(registry);
+
+  // We can show the toast again because it is an ephemeral toast.
+  EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
+  EXPECT_TRUE(controller->IsShowingToast());
+
+  // The toast should still be showing because we didn't reach the time out time
+  // yet.
+  task_environment().FastForwardBy(toast_features::kToastTimeout.Get() / 2);
+  EXPECT_TRUE(controller->IsShowingToast());
+
+  // Show a different toast before the link copied toast times out.
+  EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kImageCopied)));
+  EXPECT_TRUE(controller->IsShowingToast());
+
+  // The image copied toast should still be showing even though the link copied
+  // toast should have timed out by now.
+  task_environment().FastForwardBy(toast_features::kToastTimeout.Get() / 2);
+  EXPECT_TRUE(controller->IsShowingToast());
+}
+
 TEST_F(ToastControllerUnitTest, PersistentToastStaysOpen) {
   ToastRegistry* const registry = toast_registry();
   registry->RegisterToast(ToastId::kLinkCopied, ToastSpecification::Builder(
@@ -124,7 +171,7 @@
                                                     .AddPersistance()
                                                     .Build());
 
-  auto controller = std::make_unique<ToastController>(nullptr, registry);
+  auto controller = std::make_unique<TestToastController>(registry);
 
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
   EXPECT_TRUE(controller->IsShowingToast());
diff --git a/chrome/browser/ui/toasts/toast_view.cc b/chrome/browser/ui/toasts/toast_view.cc
index 90d081d..5efa47f 100644
--- a/chrome/browser/ui/toasts/toast_view.cc
+++ b/chrome/browser/ui/toasts/toast_view.cc
@@ -9,11 +9,14 @@
 
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/window/dialog_delegate.h"
 
 namespace toasts {
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ToastView, kToastViewId);
+
 ToastView::ToastView(views::View* anchor_view,
                      const std::u16string& toast_text,
                      const gfx::VectorIcon& icon)
@@ -26,6 +29,7 @@
       DISTANCE_TOAST_BUBBLE_HEIGHT));
   SetProperty(views::kElementIdentifierKey, kToastElementId);
   set_close_on_deactivate(false);
+  SetProperty(views::kElementIdentifierKey, kToastViewId);
 }
 
 ToastView::~ToastView() = default;
diff --git a/chrome/browser/ui/toasts/toast_view.h b/chrome/browser/ui/toasts/toast_view.h
index b1651f0a..aa38934 100644
--- a/chrome/browser/ui/toasts/toast_view.h
+++ b/chrome/browser/ui/toasts/toast_view.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/memory/raw_ptr.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 
 namespace toasts {
@@ -16,6 +17,7 @@
   METADATA_HEADER(ToastView, views::BubbleDialogDelegateView)
 
  public:
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kToastViewId);
   ToastView(views::View* anchor_view,
             const std::u16string& toast_text,
             const gfx::VectorIcon& icon);
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc
index b5a26d5..a19e4d7 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc
@@ -79,6 +79,10 @@
 // The size of a close or delete icon.
 constexpr int kCloseIconSize = 16;
 
+// The size of a refresh icon.
+constexpr int kRefreshIconSize = 16;
+constexpr int kRefreshInkDropRadius = 12;
+
 // Popup items that use a leading icon instead of a trailing one.
 constexpr auto kPopupItemTypesUsingLeadingIcons =
     base::MakeFixedFlatSet<SuggestionType>(
@@ -615,7 +619,7 @@
   std::unique_ptr<views::ImageButton> button =
       views::CreateVectorImageButtonWithNativeTheme(
           CreateExecuteSoonWrapper(std::move(deletion_action)),
-          vector_icons::kReloadIcon, kCloseIconSize);
+          vector_icons::kReloadIcon, kRefreshIconSize);
 
   button->SetTooltipText(l10n_util::GetStringUTF16(
       IDS_PLUS_ADDRESS_CREATE_INLINE_REFRESH_TOOLTIP));
@@ -623,6 +627,8 @@
   button->GetViewAccessibility().SetName(l10n_util::GetStringUTF16(
       IDS_PLUS_ADDRESS_CREATE_INLINE_REFRESH_A11Y_NAME));
   button->SetVisible(false);
+  views::InstallFixedSizeCircleHighlightPathGenerator(button.get(),
+                                                      kRefreshInkDropRadius);
 
   return std::make_unique<PopupRowWithButtonView>(
       a11y_selection_delegate, selection_delegate, controller, line_number,
diff --git a/chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux_browsertest.cc b/chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux_browsertest.cc
index bcfa6035..a43876b 100644
--- a/chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux_browsertest.cc
+++ b/chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux_browsertest.cc
@@ -18,8 +18,8 @@
 #include "ui/ozone/public/ozone_platform.h"
 
 using DesktopBrowserFrameAuraLinuxTest = InProcessBrowserTest;
-using SupportsSsdForTest =
-    ui::OzonePlatform::PlatformRuntimeProperties::SupportsSsdForTest;
+using SupportsForTest =
+    ui::OzonePlatform::PlatformRuntimeProperties::SupportsForTest;
 
 namespace {
 
@@ -78,7 +78,7 @@
   // finally.
   auto* const platform = ui::OzonePlatform::GetInstance();
   for (const auto ssd_support_override :
-       {SupportsSsdForTest::kYes, SupportsSsdForTest::kNo}) {
+       {SupportsForTest::kYes, SupportsForTest::kNo}) {
     ui::OzonePlatform::PlatformRuntimeProperties::
         override_supports_ssd_for_test = ssd_support_override;
 
@@ -107,7 +107,7 @@
 
   // Reset the override.
   ui::OzonePlatform::PlatformRuntimeProperties::override_supports_ssd_for_test =
-      SupportsSsdForTest::kNotSet;
+      SupportsForTest::kNotSet;
 }
 
 // Tests that the new browser window restores the bounds properly: its size must
diff --git a/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc b/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc
index eca76485..47855dc 100644
--- a/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc
@@ -534,8 +534,10 @@
     const views::SizeBounds& available_size) const {
   int height = GetLayoutManager()->GetPreferredHeightForWidth(
       this, kExpandButtonStripWidth);
+  int expand_button_strip_height =
+      expand_button_strip_->GetVisible() ? kExpandButtonStripHeight : 0;
   return gfx::Size(kExpandButtonStripWidth,
-                   std::max(kExpandButtonStripHeight, height));
+                   std::max(expand_button_strip_height, height));
 }
 
 void MediaItemUIDeviceSelectorView::AddObserver(
diff --git a/chrome/browser/ui/views/permissions/chip/dashboard_kombucha_interactive_uitest.cc b/chrome/browser/ui/views/permissions/chip/dashboard_kombucha_interactive_uitest.cc
index 4401dbd..b1e9309 100644
--- a/chrome/browser/ui/views/permissions/chip/dashboard_kombucha_interactive_uitest.cc
+++ b/chrome/browser/ui/views/permissions/chip/dashboard_kombucha_interactive_uitest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/permissions/system/system_permission_settings.h"
 #include "chrome/browser/profiles/profile.h"
@@ -177,49 +178,6 @@
           PermissionToggleRowView::kPermissionDisabledAtSystemLevelElementId));
 }
 
-// 1. Enable Camera permission
-// 2. Use `getUserMedia` to show camera activity indicator
-// 3. Click on the indicator to open PageInfo
-// 4. Verify that Camera permission is shown in PageInfo
-// 5. Verify that Camera permission has "Using now" subtitle.
-// 6. Verify that the system settings link is shown.
-IN_PROC_BROWSER_TEST_F(DashboardKombuchaInteractiveUITest,
-                       CameraUsingTestWithSystemBlock) {
-  SetPermission(ContentSettingsType::MEDIASTREAM_CAMERA, CONTENT_SETTING_ALLOW);
-
-  system_permission_settings::ScopedSettingsForTesting scoped_system_permission(
-      ContentSettingsType::MEDIASTREAM_CAMERA, true);
-
-  RunTestSequence(
-      InstrumentTab(kWebContentsElementId),
-      NavigateWebContents(kWebContentsElementId, GetURL()),
-      EnsureNotPresent(PermissionDashboardView::kDashboardElementId),
-      EnsureNotPresent(PermissionChipView::kElementIdForTesting),
-      ExecuteJs(kWebContentsElementId, "requestCamera"),
-      WaitForShow(PermissionChipView::kElementIdForTesting),
-      CheckChipIsRequest(false), CheckChipText(IDS_CAMERA_IN_USE),
-      PressButton(PermissionChipView::kElementIdForTesting),
-      WaitForShow(PageInfoMainView::kPermissionsElementId),
-      CheckViewProperty(PageInfoMainView::kMainLayoutElementId,
-                        &PageInfoMainView::GetVisiblePermissionsCountForTesting,
-                        1),
-      // Set id to the first children of `kPermissionsElementId` -
-      // permissions view in PageInfo.
-      NameChildView(PageInfoMainView::kPermissionsElementId,
-                    kFirstPermissionRow, 0u),
-      // Verify the row label is Camera
-      CheckViewProperty(
-          kFirstPermissionRow, &PermissionToggleRowView::GetRowTitleForTesting,
-          l10n_util::GetStringUTF16(IDS_SITE_SETTINGS_TYPE_CAMERA)),
-      WaitForShow(PermissionToggleRowView::kRowSubTitleCameraElementId),
-      CheckViewProperty(
-          kFirstPermissionRow,
-          &PermissionToggleRowView::GetRowSubTitleForTesting,
-          l10n_util::GetStringUTF16(IDS_PAGE_INFO_PERMISSION_USING_NOW)),
-      WaitForShow(
-          PermissionToggleRowView::kPermissionDisabledAtSystemLevelElementId));
-}
-
 IN_PROC_BROWSER_TEST_F(DashboardKombuchaInteractiveUITest,
                        MicrophoneUsingTest) {
   SetPermission(ContentSettingsType::MEDIASTREAM_MIC, CONTENT_SETTING_ALLOW);
@@ -256,8 +214,49 @@
   );
 }
 
+// 1. Enable Camera permission
+// 2. Use `getUserMedia` to show camera activity indicator
+// 3. Click on the indicator to open PageInfo
+// 4. Verify that Camera permission is shown in PageInfo
+// 5. Verify that the system settings link is shown.
 IN_PROC_BROWSER_TEST_F(DashboardKombuchaInteractiveUITest,
-                       MicrophoneUsingTestWithSystemBlock) {
+                       CameraUsageTestWithSystemBlock) {
+  SetPermission(ContentSettingsType::MEDIASTREAM_CAMERA, CONTENT_SETTING_ALLOW);
+
+  system_permission_settings::ScopedSettingsForTesting scoped_system_permission(
+      ContentSettingsType::MEDIASTREAM_CAMERA, true);
+
+  RunTestSequence(
+      InstrumentTab(kWebContentsElementId),
+      NavigateWebContents(kWebContentsElementId, GetURL()),
+      EnsureNotPresent(PermissionDashboardView::kDashboardElementId),
+      EnsureNotPresent(PermissionChipView::kElementIdForTesting),
+      ExecuteJs(kWebContentsElementId, "requestCamera"),
+      WaitForShow(PermissionChipView::kElementIdForTesting),
+      CheckChipIsRequest(false),
+// Supported only by macOS.
+#if BUILDFLAG(IS_MAC)
+      CheckChipText(IDS_CAMERA_CANNOT_ACCESS),
+#endif  // BUILDFLAG(IS_MAC)
+      PressButton(PermissionChipView::kElementIdForTesting),
+      WaitForShow(PageInfoMainView::kPermissionsElementId),
+      CheckViewProperty(PageInfoMainView::kMainLayoutElementId,
+                        &PageInfoMainView::GetVisiblePermissionsCountForTesting,
+                        1),
+      // Set id to the first children of `kPermissionsElementId` -
+      // permissions view in PageInfo.
+      NameChildView(PageInfoMainView::kPermissionsElementId,
+                    kFirstPermissionRow, 0u),
+      // Verify the row label is Camera
+      CheckViewProperty(
+          kFirstPermissionRow, &PermissionToggleRowView::GetRowTitleForTesting,
+          l10n_util::GetStringUTF16(IDS_SITE_SETTINGS_TYPE_CAMERA)),
+      WaitForShow(
+          PermissionToggleRowView::kPermissionDisabledAtSystemLevelElementId));
+}
+
+IN_PROC_BROWSER_TEST_F(DashboardKombuchaInteractiveUITest,
+                       MicrophoneUsageTestWithSystemBlock) {
   SetPermission(ContentSettingsType::MEDIASTREAM_MIC, CONTENT_SETTING_ALLOW);
 
   system_permission_settings::ScopedSettingsForTesting scoped_system_permission(
@@ -270,7 +269,11 @@
       EnsureNotPresent(PermissionChipView::kElementIdForTesting),
       ExecuteJs(kWebContentsElementId, "requestMicrophone"),
       WaitForShow(PermissionChipView::kElementIdForTesting),
-      CheckChipIsRequest(false), CheckChipText(IDS_MICROPHONE_IN_USE),
+      CheckChipIsRequest(false),
+// Supported only by macOS.
+#if BUILDFLAG(IS_MAC)
+      CheckChipText(IDS_MICROPHONE_CANNOT_ACCESS),
+#endif  // BUILDFLAG(IS_MAC)
       PressButton(PermissionChipView::kElementIdForTesting),
       WaitForShow(PageInfoMainView::kPermissionsElementId),
       CheckViewProperty(PageInfoMainView::kMainLayoutElementId,
@@ -284,11 +287,6 @@
       CheckViewProperty(kFirstPermissionRow,
                         &PermissionToggleRowView::GetRowTitleForTesting,
                         l10n_util::GetStringUTF16(IDS_SITE_SETTINGS_TYPE_MIC)),
-      WaitForShow(PermissionToggleRowView::kRowSubTitleMicrophoneElementId),
-      CheckViewProperty(
-          kFirstPermissionRow,
-          &PermissionToggleRowView::GetRowSubTitleForTesting,
-          l10n_util::GetStringUTF16(IDS_PAGE_INFO_PERMISSION_USING_NOW)),
       WaitForShow(
           PermissionToggleRowView::kPermissionDisabledAtSystemLevelElementId)
 
diff --git a/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc b/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc
index ae979ea6..64aca18b 100644
--- a/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc
+++ b/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc
@@ -27,7 +27,7 @@
 
 #if BUILDFLAG(IS_MAC)
 #include "base/mac/mac_util.h"
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
+#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
 #endif
 
 namespace {
@@ -548,27 +548,27 @@
 
   if (permission_determined) {
 #if BUILDFLAG(IS_MAC)
-    system_media_permissions::SystemPermission permission;
+    system_permission_settings::SystemPermission permission;
 
     if (request_type == ContentSettingsType::MEDIASTREAM_MIC) {
       permission =
-          system_media_permissions::CheckSystemAudioCapturePermission();
+          system_permission_settings::CheckSystemAudioCapturePermission();
     }
     if (request_type == ContentSettingsType::MEDIASTREAM_CAMERA) {
       permission =
-          system_media_permissions::CheckSystemVideoCapturePermission();
+          system_permission_settings::CheckSystemVideoCapturePermission();
     }
 
     switch (permission) {
-      case system_media_permissions::SystemPermission::kRestricted:
+      case system_permission_settings::SystemPermission::kRestricted:
         break;
-      case system_media_permissions::SystemPermission::kDenied:
+      case system_permission_settings::SystemPermission::kDenied:
         RecordOsMetrics(permissions::OsScreenAction::OS_PROMPT_DENIED);
         break;
-      case system_media_permissions::SystemPermission::kAllowed:
+      case system_permission_settings::SystemPermission::kAllowed:
         RecordOsMetrics(permissions::OsScreenAction::OS_PROMPT_ALLOWED);
         break;
-      case system_media_permissions::SystemPermission::kNotDetermined:
+      case system_permission_settings::SystemPermission::kNotDetermined:
         NOTREACHED_IN_MIGRATION();
     }
 #endif  // BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc
index 31c26da..1d9bd96d 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc
@@ -196,7 +196,7 @@
     // Unittests failed when the system is on battery. This class is using a
     // mock power monitor source `power_monitor_source_` to ensure no real
     // power state or power notifications are delivered to the unittests.
-    EXPECT_FALSE(base::PowerMonitor::IsOnBatteryPower());
+    EXPECT_FALSE(base::PowerMonitor::GetInstance()->IsOnBatteryPower());
   }
 
   UpgradeDetector* upgrade_detector() { return &upgrade_detector_; }
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc
index 99ad6b90..c02f5b7 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc
@@ -49,12 +49,6 @@
     : browser_(browser) {}
 
 ReadAnythingCoordinator::~ReadAnythingCoordinator() {
-  local_side_panel_switch_delay_timer_.Stop();
-
-  if (features::IsReadAnythingDocsIntegrationEnabled()) {
-    RemoveGDocsHelperExtension();
-  }
-
   // Deregister Read Anything from the global side panel registry. This removes
   // Read Anything as a side panel entry observer.
 
@@ -67,120 +61,6 @@
   if (features::IsDataCollectionModeForScreen2xEnabled()) {
     BrowserList::GetInstance()->AddObserver(this);
   }
-
-  if (features::IsReadAnythingDocsIntegrationEnabled()) {
-    EmbeddedA11yExtensionLoader::GetInstance()->Init();
-  }
-}
-
-void ReadAnythingCoordinator::AddObserver(
-    ReadAnythingCoordinator::Observer* observer) {
-  observers_.AddObserver(observer);
-}
-
-void ReadAnythingCoordinator::RemoveObserver(
-    ReadAnythingCoordinator::Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-void ReadAnythingCoordinator::OnEntryShown(SidePanelEntry* entry) {
-  DCHECK(entry->key().id() == SidePanelEntry::Id::kReadAnything);
-  OnReadAnythingSidePanelEntryShown();
-}
-
-void ReadAnythingCoordinator::OnEntryHidden(SidePanelEntry* entry) {
-  DCHECK(entry->key().id() == SidePanelEntry::Id::kReadAnything);
-  OnReadAnythingSidePanelEntryHidden();
-}
-
-void ReadAnythingCoordinator::OnReadAnythingSidePanelEntryShown() {
-  for (Observer& obs : observers_) {
-    obs.Activate(true);
-  }
-
-  if (!features::IsReadAnythingDocsIntegrationEnabled()) {
-    return;
-  }
-
-  active_local_side_panel_count_++;
-  InstallGDocsHelperExtension();
-}
-
-void ReadAnythingCoordinator::OnReadAnythingSidePanelEntryHidden() {
-  for (Observer& obs : observers_) {
-    obs.Activate(false);
-  }
-
-  if (!features::IsReadAnythingDocsIntegrationEnabled()) {
-    return;
-  }
-
-  active_local_side_panel_count_--;
-  local_side_panel_switch_delay_timer_.Stop();
-  local_side_panel_switch_delay_timer_.Start(
-      FROM_HERE, base::Seconds(30),
-      base::BindRepeating(
-          &ReadAnythingCoordinator::OnLocalSidePanelSwitchDelayTimeout,
-          weak_ptr_factory_.GetWeakPtr()));
-}
-
-std::unique_ptr<views::View> ReadAnythingCoordinator::CreateContainerView() {
-  auto web_view =
-      std::make_unique<ReadAnythingSidePanelWebView>(browser_->profile());
-
-  return std::move(web_view);
-}
-
-void ReadAnythingCoordinator::InstallGDocsHelperExtension() {
-#if BUILDFLAG(IS_CHROMEOS)
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  EmbeddedA11yManagerLacros::GetInstance()->SetReadingModeEnabled(true);
-#else
-  EmbeddedA11yExtensionLoader::GetInstance()->InstallExtensionWithId(
-      extension_misc::kReadingModeGDocsHelperExtensionId,
-      extension_misc::kReadingModeGDocsHelperExtensionPath,
-      extension_misc::kReadingModeGDocsHelperManifestFilename,
-      /*should_localize=*/false);
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-#else
-  extensions::ExtensionService* service =
-      extensions::ExtensionSystem::Get(browser_->profile())
-          ->extension_service();
-  if (!service) {
-    // In tests, the service might not be created.
-    CHECK_IS_TEST();
-    return;
-  }
-  extensions::ComponentLoader* component_loader = service->component_loader();
-  if (!component_loader->Exists(
-          extension_misc::kReadingModeGDocsHelperExtensionId)) {
-    component_loader->Add(
-        IDR_READING_MODE_GDOCS_HELPER_MANIFEST,
-        base::FilePath(FILE_PATH_LITERAL("reading_mode_gdocs_helper")));
-  }
-#endif  // BUILDFLAG(IS_CHROMEOS)
-}
-
-void ReadAnythingCoordinator::RemoveGDocsHelperExtension() {
-#if BUILDFLAG(IS_CHROMEOS)
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  EmbeddedA11yManagerLacros::GetInstance()->SetReadingModeEnabled(false);
-#else
-  EmbeddedA11yExtensionLoader::GetInstance()->RemoveExtensionWithId(
-      extension_misc::kReadingModeGDocsHelperExtensionId);
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-#else
-  extensions::ExtensionService* service =
-      extensions::ExtensionSystem::Get(browser_->profile())
-          ->extension_service();
-  if (!service) {
-    // In tests, the service might not be created.
-    CHECK_IS_TEST();
-    return;
-  }
-  service->component_loader()->Remove(
-      extension_misc::kReadingModeGDocsHelperExtensionId);
-#endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
 void ReadAnythingCoordinator::OnBrowserSetLastActive(Browser* browser) {
@@ -198,11 +78,3 @@
     side_panel_ui->Show(SidePanelEntryId::kReadAnything);
   }
 }
-
-void ReadAnythingCoordinator::OnLocalSidePanelSwitchDelayTimeout() {
-  if (active_local_side_panel_count_ > 0) {
-    return;
-  }
-
-  RemoveGDocsHelperExtension();
-}
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h
index bac03e5..de7c4b0 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h
@@ -5,20 +5,10 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_COORDINATOR_H_
 #define CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_COORDINATOR_H_
 
-#include <memory>
-#include <string>
-
-#include "base/observer_list.h"
-#include "base/observer_list_types.h"
-#include "base/timer/timer.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/browser_list_observer.h"
-#include "chrome/browser/ui/views/side_panel/side_panel_entry_observer.h"
-#include "content/public/browser/web_contents_observer.h"
 
 class Browser;
-namespace views {
-class View;
-}  // namespace views
 
 ///////////////////////////////////////////////////////////////////////////////
 // ReadAnythingCoordinator
@@ -29,59 +19,18 @@
 //  feature. Classes outside this feature should make calls to the coordinator.
 //  This class has the same lifetime as the browser.
 //
-class ReadAnythingCoordinator : public SidePanelEntryObserver,
-                                public BrowserListObserver {
+class ReadAnythingCoordinator : public BrowserListObserver {
  public:
-  class Observer : public base::CheckedObserver {
-   public:
-    virtual void Activate(bool active) {}
-    virtual void OnActivePageDistillable(bool distillable) {}
-  };
-
   explicit ReadAnythingCoordinator(Browser* browser);
   ~ReadAnythingCoordinator() override;
 
   // This class does not do anything until Initialize is called.
   void Initialize();
 
-  void AddObserver(ReadAnythingCoordinator::Observer* observer);
-  void RemoveObserver(ReadAnythingCoordinator::Observer* observer);
-
-  void OnReadAnythingSidePanelEntryShown();
-  void OnReadAnythingSidePanelEntryHidden();
-
  private:
   friend class ReadAnythingCoordinatorTest;
   friend class ReadAnythingCoordinatorScreen2xDataCollectionModeTest;
 
-  // Starts the delay for showing the IPH after the tab has changed.
-  void StartPageChangeDelay();
-
-  // SidePanelEntryObserver:
-  void OnEntryShown(SidePanelEntry* entry) override;
-  void OnEntryHidden(SidePanelEntry* entry) override;
-
-  // Callback passed to SidePanelCoordinator. This function creates the
-  // container view and all its child views and returns it.
-  std::unique_ptr<views::View> CreateContainerView();
-
-  void InstallGDocsHelperExtension();
-  void RemoveGDocsHelperExtension();
-  // The number of active local side panel that is currently shown in the
-  // browser. If there is no active local side panel (count is 0) after a
-  // timeout, we can safely remove the gdocs helper extension.
-  int active_local_side_panel_count_ = 0;
-  // Start a timer when the user leaves a local side panel. If they switch to
-  // another local side panel before it expires, keep the extension installed;
-  // otherwise, uninstall it. This prevents frequent
-  // installations/uninstallations.
-  base::RepeatingTimer local_side_panel_switch_delay_timer_;
-  void OnLocalSidePanelSwitchDelayTimeout();
-
-  std::string default_language_code_;
-
-  base::ObserverList<Observer> observers_;
-
   // Owns this.
   raw_ptr<Browser> browser_;
 
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator_unittest.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator_unittest.cc
index 167e9096..dcf3d8b 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator_unittest.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator_unittest.cc
@@ -35,13 +35,6 @@
 
 using testing::_;
 
-class MockReadAnythingCoordinatorObserver
-    : public ReadAnythingCoordinator::Observer {
- public:
-  MOCK_METHOD(void, Activate, (bool active), (override));
-  MOCK_METHOD(void, OnActivePageDistillable, (bool distillable), (override));
-};
-
 class ReadAnythingCoordinatorTest : public TestWithBrowserView {
  public:
   ReadAnythingCoordinatorTest()
@@ -109,14 +102,8 @@
   // Wrapper methods around the ReadAnythingCoordinator. These do nothing more
   // than keep the below tests less verbose (simple pass-throughs).
 
-  void AddObserver(ReadAnythingCoordinator::Observer* observer) {
-    read_anything_coordinator_->AddObserver(observer);
-  }
-  void RemoveObserver(ReadAnythingCoordinator::Observer* observer) {
-    read_anything_coordinator_->RemoveObserver(observer);
-  }
   std::unique_ptr<views::View> CreateContainerView() {
-    return read_anything_coordinator_->CreateContainerView();
+    return std::make_unique<ReadAnythingSidePanelWebView>(browser()->profile());
   }
 
   void OnBrowserSetLastActive(Browser* browser) {
@@ -159,7 +146,6 @@
   raw_ptr<ReadAnythingCoordinator, DanglingUntriaged>
       read_anything_coordinator_ = nullptr;
 
-  MockReadAnythingCoordinatorObserver coordinator_observer_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
@@ -167,26 +153,6 @@
 // these tests on asan mac.
 #if !BUILDFLAG(IS_MAC) || !defined(ADDRESS_SANITIZER)
 
-TEST_F(ReadAnythingCoordinatorTest, ContainerViewsAreUnique) {
-  auto view1 = CreateContainerView();
-  auto view2 = CreateContainerView();
-  EXPECT_NE(view1, view2);
-}
-
-TEST_F(ReadAnythingCoordinatorTest,
-       ActivateCalled_ShowAndHideReadAnythingEntry) {
-  AddObserver(&coordinator_observer_);
-  ASSERT_EQ(contextual_registries_.size(), 2u);
-  SidePanelEntry* entry = contextual_registries_[0]->GetEntryForKey(
-      SidePanelEntry::Key(SidePanelEntry::Id::kReadAnything));
-
-  EXPECT_CALL(coordinator_observer_, Activate(true)).Times(1);
-  entry->OnEntryShown();
-
-  EXPECT_CALL(coordinator_observer_, Activate(false)).Times(1);
-  entry->OnEntryHidden();
-}
-
 #if !BUILDFLAG(IS_CHROMEOS_LACROS)
 TEST_F(ReadAnythingCoordinatorTest,
        SidePanelShowAndHide_NonLacros_CallEmbeddedA11yExtensionLoader) {
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.cc
index 04d7d57..1fcc5cb 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.cc
@@ -4,10 +4,128 @@
 
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_service.h"
 
+#include "base/check_is_test.h"
+#include "chrome/browser/accessibility/embedded_a11y_extension_loader.h"
+#include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/views/side_panel/read_anything/read_anything_service_factory.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/grit/browser_resources.h"
 #include "extensions/browser/extension_system.h"
+#include "ui/accessibility/accessibility_features.h"
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chrome/browser/lacros/embedded_a11y_manager_lacros.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+namespace {
+
+// The number of seconds to wait before removing the extension. This avoids
+// removing an extension only to add it back immediately.
+constexpr int kRemoveExtensionDelaySeconds = 30;
+
+}  // namespace
 
 ReadAnythingService::ReadAnythingService(Profile* profile) : profile_(profile) {
-  // TODO(https://crbug.com/355485153): This class is currently a stub.
-  extensions::ExtensionSystem::Get(profile_)->extension_service();
+  if (features::IsReadAnythingDocsIntegrationEnabled()) {
+    EmbeddedA11yExtensionLoader::GetInstance()->Init();
+
+    // The extension may still be installed from a previous session. Queue the
+    // timer to uninstall it.
+    // TODO(https://crbug.com/362787711): This logic also needs to run if the
+    // feature is disabled.
+    local_side_panel_switch_delay_timer_.Start(
+        FROM_HERE, base::Seconds(kRemoveExtensionDelaySeconds),
+        base::BindRepeating(
+            &ReadAnythingService::OnLocalSidePanelSwitchDelayTimeout,
+            weak_ptr_factory_.GetWeakPtr()));
+  }
+}
+
+// The service is shutting down which means the profile is destroying, at which
+// point we should not be re-entrantly trying to modify the profile by removing
+// the extension. Instead remove the extension at startup.
+ReadAnythingService::~ReadAnythingService() = default;
+
+// static
+ReadAnythingService* ReadAnythingService::Get(Profile* profile) {
+  return ReadAnythingServiceFactory::GetInstance()->GetForBrowserContext(
+      profile);
+}
+
+void ReadAnythingService::OnReadAnythingSidePanelEntryShown() {
+  if (!features::IsReadAnythingDocsIntegrationEnabled()) {
+    return;
+  }
+
+  active_local_side_panel_count_++;
+  InstallGDocsHelperExtension();
+}
+
+void ReadAnythingService::OnReadAnythingSidePanelEntryHidden() {
+  if (!features::IsReadAnythingDocsIntegrationEnabled()) {
+    return;
+  }
+
+  active_local_side_panel_count_--;
+  local_side_panel_switch_delay_timer_.Reset();
+}
+
+void ReadAnythingService::InstallGDocsHelperExtension() {
+#if BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  EmbeddedA11yManagerLacros::GetInstance()->SetReadingModeEnabled(true);
+#else
+  EmbeddedA11yExtensionLoader::GetInstance()->InstallExtensionWithId(
+      extension_misc::kReadingModeGDocsHelperExtensionId,
+      extension_misc::kReadingModeGDocsHelperExtensionPath,
+      extension_misc::kReadingModeGDocsHelperManifestFilename,
+      /*should_localize=*/false);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+#else
+  extensions::ExtensionService* service =
+      extensions::ExtensionSystem::Get(profile_)->extension_service();
+  if (!service) {
+    // In tests, the service might not be created.
+    CHECK_IS_TEST();
+    return;
+  }
+  extensions::ComponentLoader* component_loader = service->component_loader();
+  if (!component_loader->Exists(
+          extension_misc::kReadingModeGDocsHelperExtensionId)) {
+    component_loader->Add(
+        IDR_READING_MODE_GDOCS_HELPER_MANIFEST,
+        base::FilePath(FILE_PATH_LITERAL("reading_mode_gdocs_helper")));
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS)
+}
+
+void ReadAnythingService::RemoveGDocsHelperExtension() {
+#if BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  EmbeddedA11yManagerLacros::GetInstance()->SetReadingModeEnabled(false);
+#else
+  EmbeddedA11yExtensionLoader::GetInstance()->RemoveExtensionWithId(
+      extension_misc::kReadingModeGDocsHelperExtensionId);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+#else
+  extensions::ExtensionService* service =
+      extensions::ExtensionSystem::Get(profile_)->extension_service();
+  if (!service) {
+    // In tests, the service might not be created.
+    CHECK_IS_TEST();
+    return;
+  }
+  service->component_loader()->Remove(
+      extension_misc::kReadingModeGDocsHelperExtensionId);
+#endif  // BUILDFLAG(IS_CHROMEOS)
+}
+
+void ReadAnythingService::OnLocalSidePanelSwitchDelayTimeout() {
+  if (active_local_side_panel_count_ > 0) {
+    return;
+  }
+
+  RemoveGDocsHelperExtension();
 }
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.h
index 2f26e37..996c32fd 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.h
@@ -6,18 +6,42 @@
 #define CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_SERVICE_H_
 
 #include "base/memory/raw_ptr.h"
+#include "base/timer/timer.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 class Profile;
 
-// This class holds profile-scoped state for the read anything feature.
-// TODO(https://crbug.com/355485153): This class is currently a stub.
+// This per-profile class holds profile-scoped state for the read anything
+// feature.
 class ReadAnythingService : public KeyedService {
  public:
   explicit ReadAnythingService(Profile* profile);
+  ~ReadAnythingService() override;
+
+  static ReadAnythingService* Get(Profile* profile);
+
+  // Called by the per-tab ReadAnythingSidePanelController.
+  void OnReadAnythingSidePanelEntryShown();
+  void OnReadAnythingSidePanelEntryHidden();
 
  private:
+  void InstallGDocsHelperExtension();
+  void RemoveGDocsHelperExtension();
+  void OnLocalSidePanelSwitchDelayTimeout();
+
+  // The number of active local side panels that are currently shown. If there
+  // is no active local side panel (count is 0) after a timeout, we can safely
+  // remove the gdocs helper extension.
+  int active_local_side_panel_count_ = 0;
+
+  // Start a timer when the user leaves a local side panel. If they switch to
+  // another local side panel before it expires, keep the extension installed;
+  // otherwise, uninstall it. This prevents frequent
+  // installations/uninstallations.
+  base::RetainingOneShotTimer local_side_panel_switch_delay_timer_;
+
   raw_ptr<Profile> profile_;
+  base::WeakPtrFactory<ReadAnythingService> weak_ptr_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_SERVICE_H_
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service_factory.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service_factory.cc
index 3cec877..58c7cb6 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service_factory.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service_factory.cc
@@ -10,6 +10,13 @@
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_service.h"
 
 // static
+ReadAnythingService* ReadAnythingServiceFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<ReadAnythingService*>(
+      GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+// static
 ReadAnythingServiceFactory* ReadAnythingServiceFactory::GetInstance() {
   static base::NoDestructor<ReadAnythingServiceFactory> instance;
   return instance.get();
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service_factory.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service_factory.h
index a59155a6..33c73453 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service_factory.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service_factory.h
@@ -8,11 +8,15 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 
+class ReadAnythingService;
+
 // See ReadAnythingService for details.
 // A service is built for regular and incognito profiles, but not guest, system
 // or other irregular profiles.
 class ReadAnythingServiceFactory : public ProfileKeyedServiceFactory {
  public:
+  static ReadAnythingService* GetForBrowserContext(
+      content::BrowserContext* context);
   static ReadAnythingServiceFactory* GetInstance();
 
  private:
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_controller.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_controller.cc
index 1c45647..c984de399 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_controller.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_controller.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h"
+#include "chrome/browser/ui/views/side_panel/read_anything/read_anything_service.h"
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_web_view.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_registry.h"
@@ -116,10 +117,15 @@
 
 void ReadAnythingSidePanelController::OnEntryShown(SidePanelEntry* entry) {
   CHECK_EQ(entry->key().id(), SidePanelEntry::Id::kReadAnything);
-  auto* coordinator = tab_->GetBrowserWindowInterface()
-                          ->GetFeatures()
-                          .read_anything_coordinator();
-  coordinator->OnReadAnythingSidePanelEntryShown();
+  auto* service =
+      ReadAnythingService::Get(tab_->GetBrowserWindowInterface()->GetProfile());
+  // At the moment, services are created for normal and incognito profiles but
+  // not unusual profile types. On the other hand,
+  // ReadAnythingSidePanelController is created for all tabs. Thus we need a
+  // nullptr check.
+  if (service) {
+    service->OnReadAnythingSidePanelEntryShown();
+  }
   for (ReadAnythingSidePanelController::Observer& obs : observers_) {
     obs.Activate(true);
   }
@@ -127,10 +133,15 @@
 
 void ReadAnythingSidePanelController::OnEntryHidden(SidePanelEntry* entry) {
   CHECK_EQ(entry->key().id(), SidePanelEntry::Id::kReadAnything);
-  auto* coordinator = tab_->GetBrowserWindowInterface()
-                          ->GetFeatures()
-                          .read_anything_coordinator();
-  coordinator->OnReadAnythingSidePanelEntryHidden();
+  auto* service =
+      ReadAnythingService::Get(tab_->GetBrowserWindowInterface()->GetProfile());
+  // At the moment, services are created for normal and incognito profiles but
+  // not unusual profile types. On the other hand,
+  // ReadAnythingSidePanelController is created for all tabs. Thus we need a
+  // nullptr check.
+  if (service) {
+    service->OnReadAnythingSidePanelEntryHidden();
+  }
   for (ReadAnythingSidePanelController::Observer& obs : observers_) {
     obs.Activate(false);
   }
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector.cc b/chrome/browser/ui/views/ssl_client_certificate_selector.cc
index 6e78f16..3ba4a71 100644
--- a/chrome/browser/ui/views/ssl_client_certificate_selector.cc
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector.cc
@@ -30,9 +30,9 @@
 namespace {
 
 // Returns the storage of a test hook for `ShowSSLClientCertificateSelector()`.
-chrome::ShowSSLClientCertificateSelectorTestingHook&
+ShowSSLClientCertificateSelectorTestingHook&
 GetShowSSLClientCertificateSelectorTestingHook() {
-  static base::NoDestructor<chrome::ShowSSLClientCertificateSelectorTestingHook>
+  static base::NoDestructor<ShowSSLClientCertificateSelectorTestingHook>
       instance;
   return *instance;
 }
@@ -158,8 +158,6 @@
                         weak_factory_.GetWeakPtr());
 }
 
-namespace chrome {
-
 base::OnceClosure ShowSSLClientCertificateSelector(
     content::WebContents* contents,
     net::SSLCertRequestInfo* cert_request_info,
@@ -200,5 +198,3 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   GetShowSSLClientCertificateSelectorTestingHook() = std::move(hook);
 }
-
-}  // namespace chrome
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 308d762..3ad8dd4f 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -98,10 +98,6 @@
 #include "components/remote_cocoa/browser/window.h"
 #endif
 
-#if BUILDFLAG(IS_LINUX)
-#include "ui/aura/client/drag_drop_client.h"
-#endif
-
 #if defined(USE_AURA)
 #include "ui/aura/env.h"                            // nogncheck
 #include "ui/aura/window.h"                         // nogncheck
@@ -695,12 +691,6 @@
     Attach(source_context_, gfx::Point(), nullptr);
     if (num_dragging_tabs() == source_context_->GetTabStripModel()->count()) {
       if (ShouldDragWindowUsingSystemDnD()) {
-        // When dragging all of a window's tabs, just hide that window instead
-        // of creating a new hidden one.
-        // We don't actually hide the window yet, because on some platforms
-        // (e.g. Wayland) the drag and drop session must be started before
-        // hiding the window. The next ContinueDragging() call will take care of
-        // hiding the window.
         StartSystemDnDSessionIfNecessary(attached_context_, point_in_screen);
         return;
       }
@@ -960,12 +950,6 @@
   const bool tab_strip_changed = (target_context != attached_context_);
   last_point_in_screen_ = point_in_screen;
 
-  if (current_state_ == DragState::kDraggingUsingSystemDragAndDrop &&
-      GetAttachedBrowserWidget()->IsVisible()) {
-    // See the comment in Drag() where we start the drag session.
-    GetAttachedBrowserWidget()->Hide();
-  }
-
   if (tab_strip_changed) {
     is_dragging_new_browser_ = false;
     did_restore_window_ = false;
@@ -1126,6 +1110,16 @@
 
   system_drag_and_drop_session_running_ = true;
 
+  if (attached_context_ == source_context_ &&
+      num_dragging_tabs() == source_context_->GetTabStripModel()->count()) {
+    // When dragging all of a window's tabs, we just hide that window instead of
+    // creating a new hidden one. On some platforms (e.g. Wayland) the drag and
+    // drop session must be started before hiding the window, so defer until the
+    // drag has started.
+    drag_started_callback_ = base::BindOnce(
+        &TabDragController::HideAttachedContext, weak_factory_.GetWeakPtr());
+  }
+
   auto data_provider = ui::OSExchangeDataProviderFactory::CreateProvider();
   // Set data in a format that is accepted by TabStrip so that a drop can
   // happen.
@@ -1144,6 +1138,16 @@
 
   VLOG(1) << __func__ << " Starting system DnD session";
 
+#if defined(USE_AURA)
+  aura::Window* root_window =
+      context->GetWidget()->GetNativeWindow()->GetRootWindow();
+  aura::client::DragDropClient* client =
+      aura::client::GetDragDropClient(root_window);
+  if (client) {
+    drag_drop_client_observation_.Observe(client);
+  }
+#endif  // defined(USE_AURA)
+
   base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
   context->GetWidget()->RunShellDrag(
       context, std::make_unique<ui::OSExchangeData>(std::move(data_provider)),
@@ -1171,6 +1175,20 @@
   return ref ? Liveness::ALIVE : Liveness::DELETED;
 }
 
+void TabDragController::HideAttachedContext() {
+  CHECK_EQ(current_state_, DragState::kDraggingUsingSystemDragAndDrop);
+  CHECK(GetAttachedBrowserWidget()->IsVisible());
+
+  // See the comment in StartSystemDnDSessionIfNecessary() where we start the
+  // drag session.
+  GetAttachedBrowserWidget()->Hide();
+
+  // Stop observing, as we're only interested in knowing when the drag started.
+#if defined(USE_AURA)
+  drag_drop_client_observation_.Reset();
+#endif  // defined(USE_AURA)
+}
+
 gfx::Rect TabDragController::GetEnclosingRectForDraggedTabs() {
   CHECK_GT(drag_data_.size(), 0UL);
 
@@ -2829,3 +2847,16 @@
 
   observation_pauser_.reset();
 }
+
+#if defined(USE_AURA)
+void TabDragController::OnDragStarted() {
+  VLOG(1) << __func__;
+  if (drag_started_callback_) {
+    std::move(drag_started_callback_).Run();
+  }
+}
+
+void TabDragController::OnDragDropClientDestroying() {
+  drag_drop_client_observation_.Reset();
+}
+#endif  // defined(USE_AURA)
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h
index 4ece25e..c2f38b7f 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.h
@@ -31,6 +31,11 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 
+#if defined(USE_AURA)
+#include "ui/aura/client/drag_drop_client.h"
+#include "ui/aura/client/drag_drop_client_observer.h"
+#endif  // defined(USE_AURA)
+
 namespace ui {
 class ListSelectionModel;
 class PresentationTimeRecorder;
@@ -87,7 +92,12 @@
 // and RunMoveLoop() is invoked on the Widget to drag the browser around. This
 // is the default on aura.
 class TabDragController : public views::WidgetObserver,
-                          public TabDragWithScrollManager {
+                          public TabDragWithScrollManager
+#if defined(USE_AURA)
+    ,
+                          public aura::client::DragDropClientObserver
+#endif  // defined(USE_AURA)
+{
  public:
   // Amount above or below the tabstrip the user has to drag before detaching.
   static const int kTouchVerticalDetachMagnetism;
@@ -423,6 +433,10 @@
   Liveness StartSystemDnDSessionIfNecessary(TabDragContext* context,
                                             const gfx::Point& point_in_screen);
 
+  // Stored as a callback in `drag_started_callback_`. See the comment in
+  // StartSystemDnDSessionIfNecessary() for more details.
+  void HideAttachedContext();
+
   // Returns the compatible TabDragContext to drag to at the
   // specified point (screen coordinates), or nullptr if there is none.
   Liveness GetTargetTabStripForPoint(const gfx::Point& point_in_screen,
@@ -618,8 +632,6 @@
   // from the old context or the tab dragging is ended.
   void ClearTabDraggingInfo();
 
-  DragState current_state_;
-
   // Tests whether a drag can be attached to a |window|.  Drags may be
   // disallowed for reasons such as the target: does not support tabs, is
   // showing a modal, has a different profile, is a different browser type
@@ -644,6 +656,14 @@
 
   void MaybeResumeTrackingSavedTabGroup();
 
+#if defined(USE_AURA)
+  // aura::client::DragDropClientObserver:
+  void OnDragStarted() override;
+  void OnDragDropClientDestroying() override;
+#endif  // defined(USE_AURA)
+
+  DragState current_state_;
+
   ui::mojom::DragEventSource event_source_ = ui::mojom::DragEventSource::kMouse;
 
   // The TabDragContext the drag originated from. This is set to null
@@ -766,6 +786,17 @@
   // Called when the loop in RunMoveLoop finishes. Only for tests.
   base::OnceClosure drag_loop_done_callback_;
 
+  // Used in a system-DnD-based drag session if we need to hide a window with
+  // all of its tabs being dragged, as that needs to happen after starting the
+  // DnD session.
+  base::OnceClosure drag_started_callback_;
+
+#if defined(USE_AURA)
+  base::ScopedObservation<aura::client::DragDropClient,
+                          aura::client::DragDropClientObserver>
+      drag_drop_client_observation_{this};
+#endif  // defined(USE_AURA)
+
   std::unique_ptr<EventTracker> event_tracker_;
 
   std::unique_ptr<SourceTabStripEmptinessTracker>
diff --git a/chrome/browser/ui/views/tabs/tab_search_container.cc b/chrome/browser/ui/views/tabs/tab_search_container.cc
index d54201f..1388935 100644
--- a/chrome/browser/ui/views/tabs/tab_search_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_search_container.cc
@@ -244,6 +244,10 @@
 }
 
 void TabSearchContainer::ExecuteHideTabOrganization() {
+  // Stop the timer since the chip might be getting hidden on user actions like
+  // dismissal or click and not timeout.
+  hide_tab_organization_timer_.Stop();
+
   expansion_animation_.SetSlideDuration(
       GetAnimationDuration(kExpansionOutDuration));
   expansion_animation_.Hide();
diff --git a/chrome/browser/ui/web_applications/web_app_metrics.cc b/chrome/browser/ui/web_applications/web_app_metrics.cc
index 7764fd0..ab02acb 100644
--- a/chrome/browser/ui/web_applications/web_app_metrics.cc
+++ b/chrome/browser/ui/web_applications/web_app_metrics.cc
@@ -109,7 +109,7 @@
       icon_health_checks_(profile),
       browser_tab_strip_tracker_(this, nullptr) {
   browser_tab_strip_tracker_.Init();
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
   BrowserList::AddObserver(this);
   // This isn't around on ChromeOS or tests.
   if (metrics::DesktopSessionDurationTracker::IsInitialized()) {
@@ -133,7 +133,7 @@
 
 WebAppMetrics::~WebAppMetrics() {
   BrowserList::RemoveObserver(this);
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
   if (metrics::DesktopSessionDurationTracker::IsInitialized()) {
     metrics::DesktopSessionDurationTracker::Get()->RemoveObserver(this);
   }
diff --git a/chrome/browser/ui/webui/ash/BUILD.gn b/chrome/browser/ui/webui/ash/BUILD.gn
index 9e99627..bcc015c4 100644
--- a/chrome/browser/ui/webui/ash/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/BUILD.gn
@@ -8,8 +8,6 @@
 
 static_library("ash") {
   sources = [
-    "connectivity_diagnostics_dialog.cc",
-    "connectivity_diagnostics_dialog.h",
     "edu_account_login_handler.cc",
     "edu_account_login_handler.h",
     "os_feedback_dialog.cc",
@@ -77,7 +75,6 @@
     "//ash/constants",
     "//ash/public/cpp",
     "//ash/webui/common:trusted_types_util",
-    "//ash/webui/connectivity_diagnostics",
     "//ash/webui/diagnostics_ui",
     "//ash/webui/diagnostics_ui:url_constants",
     "//ash/webui/os_feedback_ui:url_constants",
@@ -88,14 +85,22 @@
     "//chrome/browser:browser_process",
     "//chrome/browser:resources",
     "//chrome/browser:resources_grit",
+    "//chrome/browser/ash/arc",
+    "//chrome/browser/ash/arc/session",
     "//chrome/browser/ash/child_accounts/parent_access_code",
+    "//chrome/browser/ash/crostini",
     "//chrome/browser/ash/drive",
+    "//chrome/browser/ash/file_manager",
+    "//chrome/browser/ash/login/session",
+    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users/avatar",
     "//chrome/browser/ash/login/users/default_user_image",
     "//chrome/browser/ash/net/network_health",
     "//chrome/browser/ash/power",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/system",
+    "//chrome/browser/ash/system_web_apps/apps",
+    "//chrome/browser/ash/system_web_apps/apps/camera_app",
     "//chrome/browser/feedback",
     "//chrome/browser/image_fetcher",
     "//chrome/browser/profiles",
@@ -116,9 +121,14 @@
     "//chrome/browser/ui/webui/ash/add_supervision",
     "//chrome/browser/ui/webui/ash/bluetooth",
     "//chrome/browser/ui/webui/ash/cellular_setup",
+    "//chrome/browser/ui/webui/ash/crostini_installer",
+    "//chrome/browser/ui/webui/ash/diagnostics_dialog",
+    "//chrome/browser/ui/webui/ash/edu_coexistence",
     "//chrome/browser/ui/webui/ash/internet",
     "//chrome/browser/ui/webui/ash/set_time_dialog",
+    "//chrome/browser/ui/webui/ash/skyvault",
     "//chrome/browser/ui/webui/ash/sys_internals",
+    "//chrome/browser/ui/webui/signin/ash",
     "//chrome/common:constants",
     "//chrome/services/file_util/public/cpp",
     "//chromeos/ash/components/dbus",
@@ -175,13 +185,26 @@
   ]
 
   allow_circular_includes_from = [
+    "//chrome/browser/ash/arc",
+    "//chrome/browser/ash/arc/session",
+    "//chrome/browser/ash/crostini",
+    "//chrome/browser/ash/file_manager",
+    "//chrome/browser/ash/login/session",
+    "//chrome/browser/ash/login/ui",
+    "//chrome/browser/ash/system_web_apps/apps",
+    "//chrome/browser/ash/system_web_apps/apps/camera_app",
     "//chrome/browser/ui/webui/ash/account_manager",
     "//chrome/browser/ui/webui/ash/add_supervision",
     "//chrome/browser/ui/webui/ash/bluetooth",
     "//chrome/browser/ui/webui/ash/cellular_setup",
+    "//chrome/browser/ui/webui/ash/crostini_installer",
+    "//chrome/browser/ui/webui/ash/diagnostics_dialog",
+    "//chrome/browser/ui/webui/ash/edu_coexistence",
     "//chrome/browser/ui/webui/ash/internet",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/browser/ui/webui/ash/set_time_dialog",
+    "//chrome/browser/ui/webui/ash/skyvault",
+    "//chrome/browser/ui/webui/signin/ash",
   ]
 }
 
diff --git a/chrome/browser/ui/webui/ash/account_manager/BUILD.gn b/chrome/browser/ui/webui/ash/account_manager/BUILD.gn
index 528891be..bd0f693 100644
--- a/chrome/browser/ui/webui/ash/account_manager/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/account_manager/BUILD.gn
@@ -34,6 +34,7 @@
     "//chrome/browser:resources_grit",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/webui",
+    "//chrome/browser/ui/webui/signin/ash",
     "//chrome/common:constants",
     "//components/account_manager_core",
     "//components/prefs",
diff --git a/chrome/browser/ui/webui/ash/connectivity_diagnostics_dialog.cc b/chrome/browser/ui/webui/ash/connectivity_diagnostics_dialog.cc
deleted file mode 100644
index 0825098..0000000
--- a/chrome/browser/ui/webui/ash/connectivity_diagnostics_dialog.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/ash/connectivity_diagnostics_dialog.h"
-
-#include <string>
-
-#include "ash/webui/connectivity_diagnostics/url_constants.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace {
-
-// Scale factor for size of the connectivity diagnostics dialog, based on
-// display size.
-const float kConnectivityDiagnosticsDialogScale = .8;
-
-}  // namespace
-
-namespace ash {
-
-// static
-void ConnectivityDiagnosticsDialog::ShowDialog(gfx::NativeWindow parent) {
-  ConnectivityDiagnosticsDialog* dialog = new ConnectivityDiagnosticsDialog();
-  dialog->ShowSystemDialog(parent);
-}
-
-ConnectivityDiagnosticsDialog::ConnectivityDiagnosticsDialog()
-    : SystemWebDialogDelegate(GURL(kChromeUIConnectivityDiagnosticsUrl),
-                              /*title=*/std::u16string()) {}
-
-ConnectivityDiagnosticsDialog::~ConnectivityDiagnosticsDialog() = default;
-
-void ConnectivityDiagnosticsDialog::GetDialogSize(gfx::Size* size) const {
-  const display::Display display =
-      display::Screen::GetScreen()->GetPrimaryDisplay();
-
-  *size =
-      gfx::Size(display.size().width() * kConnectivityDiagnosticsDialogScale,
-                display.size().height() * kConnectivityDiagnosticsDialogScale);
-}
-
-}  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/connectivity_diagnostics_dialog.h b/chrome/browser/ui/webui/ash/connectivity_diagnostics_dialog.h
deleted file mode 100644
index 7200cbed..0000000
--- a/chrome/browser/ui/webui/ash/connectivity_diagnostics_dialog.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CONNECTIVITY_DIAGNOSTICS_DIALOG_H_
-#define CHROME_BROWSER_UI_WEBUI_ASH_CONNECTIVITY_DIAGNOSTICS_DIALOG_H_
-
-#include "chrome/browser/ui/webui/ash/system_web_dialog_delegate.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace ash {
-
-class ConnectivityDiagnosticsDialog : public SystemWebDialogDelegate {
- public:
-  static void ShowDialog(gfx::NativeWindow parent);
-
- protected:
-  ConnectivityDiagnosticsDialog();
-  ~ConnectivityDiagnosticsDialog() override;
-
-  ConnectivityDiagnosticsDialog(const ConnectivityDiagnosticsDialog&) = delete;
-  ConnectivityDiagnosticsDialog& operator=(
-      const ConnectivityDiagnosticsDialog&) = delete;
-
-  // ui::WebDialogDelegate
-  void GetDialogSize(gfx::Size* size) const override;
-};
-}  // namespace ash
-
-#endif  // CHROME_BROWSER_UI_WEBUI_ASH_CONNECTIVITY_DIAGNOSTICS_DIALOG_H_
diff --git a/chrome/browser/ui/webui/ash/crostini_installer/BUILD.gn b/chrome/browser/ui/webui/ash/crostini_installer/BUILD.gn
index ce890d9e..e84ebaf2 100644
--- a/chrome/browser/ui/webui/ash/crostini_installer/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/crostini_installer/BUILD.gn
@@ -19,6 +19,7 @@
 
   deps = [
     ":mojo_bindings",
+    "//ash/components/arc/mojom",
     "//ash/public/cpp",
     "//chrome/app:generated_resources",
     "//chrome/app/theme:chrome_unscaled_resources",
@@ -26,7 +27,6 @@
     "//chrome/browser/ash/crostini:crostini_installer_types_mojom",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/webui",
-    "//chrome/browser/ui/webui/ash",
     "//chrome/common",
     "//chromeos/ash/components/dbus/spaced",
     "//components/strings:components_strings",
diff --git a/chrome/browser/ui/webui/ash/diagnostics_dialog/BUILD.gn b/chrome/browser/ui/webui/ash/diagnostics_dialog/BUILD.gn
index 3838b4fb..f3f6563ac 100644
--- a/chrome/browser/ui/webui/ash/diagnostics_dialog/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/diagnostics_dialog/BUILD.gn
@@ -20,7 +20,6 @@
     "//ash/webui/diagnostics_ui:url_constants",
     "//base",
     "//chrome/browser/profiles:profile",
-    "//chrome/browser/ui/webui/ash",
     "//ui/display",
   ]
 }
diff --git a/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.cc b/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.cc
index a479268d..26bd7d4 100644
--- a/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.cc
+++ b/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "ash/constants/ash_features.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chromeos/ash/components/emoji/emoji_search.h"
 #include "chromeos/ash/components/emoji/emoji_search.mojom.h"
 
@@ -49,7 +50,8 @@
     const std::vector<std::string>& language_codes,
     SearchEmojiCallback callback) {
   CHECK(search_);
-  emoji::EmojiSearchResult result = search_->SearchEmoji(query, language_codes);
+  emoji::EmojiSearchResult result =
+      search_->SearchEmoji(base::UTF8ToUTF16(query), language_codes);
   std::move(callback).Run(
       SearchResultsFromEmojiSearchEntries(std::move(result.emojis)),
       SearchResultsFromEmojiSearchEntries(std::move(result.symbols)),
diff --git a/chrome/browser/ui/webui/ash/settings/pages/people/BUILD.gn b/chrome/browser/ui/webui/ash/settings/pages/people/BUILD.gn
index 5e312caf..de6eeac 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/people/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/settings/pages/people/BUILD.gn
@@ -61,6 +61,7 @@
     "//chrome/browser/ui/webui/ash/account_manager",
     "//chrome/browser/ui/webui/ash/add_supervision",
     "//chrome/browser/ui/webui/ash/settings/search",
+    "//chrome/browser/ui/webui/signin/ash",
     "//chrome/common",
     "//chromeos/ash/components/account_manager",
     "//chromeos/ash/components/dbus/userdataauth",
diff --git a/chrome/browser/ui/webui/ash/skyvault/BUILD.gn b/chrome/browser/ui/webui/ash/skyvault/BUILD.gn
index a1c9acb..02ccb6e 100644
--- a/chrome/browser/ui/webui/ash/skyvault/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/skyvault/BUILD.gn
@@ -24,10 +24,7 @@
     "local_files_migration_ui.h",
   ]
 
-  public_deps = [
-    "//chrome/browser:browser_public_dependencies",
-    "//chrome/browser/ui/webui/ash",
-  ]
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
 
   deps = [
     ":mojo_bindings",
diff --git a/chrome/browser/ui/webui/data_sharing/data_sharing_ui.cc b/chrome/browser/ui/webui/data_sharing/data_sharing_ui.cc
index 74e801c9..cbfc94a 100644
--- a/chrome/browser/ui/webui/data_sharing/data_sharing_ui.cc
+++ b/chrome/browser/ui/webui/data_sharing/data_sharing_ui.cc
@@ -55,6 +55,7 @@
       network::mojom::CSPDirectiveName::ScriptSrc,
       "script-src "
       "chrome-untrusted://resources "
+      "chrome-untrusted://webui-test "
       "'unsafe-inline' 'self';");
 
   // Allow images and avatars to be loaded.
@@ -78,14 +79,15 @@
       network::mojom::CSPDirectiveName::ConnectSrc,
       "connect-src "
       "https://play.google.com "
-      "https://peoplestack-pa.clients6.google.com ");
+      "https://peoplestack-pa.clients6.google.com;");
 
   // Allow trsuted types to be created.
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::TrustedTypes,
       "trusted-types "
       "goog#html "
-      "lit-html;");
+      "lit-html "
+      "webui-test-script;");
 }
 
 DataSharingUI::~DataSharingUI() = default;
diff --git a/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
index 899cf97c..3090ec1 100644
--- a/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
@@ -96,6 +96,23 @@
   return result;
 }
 
+std::string FilteringBehaviorDetailsToString(
+    supervised_user::FilteringBehaviorDetails details) {
+  switch (details.reason) {
+    case supervised_user::FilteringBehaviorReason::DEFAULT:
+      return "Default";
+    case supervised_user::FilteringBehaviorReason::ASYNC_CHECKER:
+      if (details.classification_details.reason ==
+          safe_search_api::ClassificationDetails::Reason::kCachedResponse) {
+        return "AsyncChecker (Cached)";
+      }
+      return "AsyncChecker";
+    case supervised_user::FilteringBehaviorReason::MANUAL:
+      return "Manual";
+  }
+  NOTREACHED();
+}
+
 }  // namespace
 
 FamilyLinkUserInternalsMessageHandler::FamilyLinkUserInternalsMessageHandler() =
@@ -262,12 +279,15 @@
 void FamilyLinkUserInternalsMessageHandler::OnURLChecked(
     const GURL& url,
     supervised_user::FilteringBehavior behavior,
-    supervised_user::FilteringBehaviorReason reason,
-    bool uncertain) {
+    supervised_user::FilteringBehaviorDetails details) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::Value::Dict result;
   result.Set("url", url.possibly_invalid_spec());
-  result.Set("result", FilteringBehaviorToString(behavior, uncertain));
-  result.Set("reason", FilteringBehaviorReasonToString(reason));
+  result.Set("result",
+             FilteringBehaviorToString(
+                 behavior, details.classification_details.reason ==
+                               safe_search_api::ClassificationDetails::Reason::
+                                   kFailedUseDefault));
+  result.Set("reason", FilteringBehaviorDetailsToString(details));
   FireWebUIListener("filtering-result-received", result);
 }
diff --git a/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.h b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.h
index f9b41f0..99ed6a0 100644
--- a/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.h
+++ b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.h
@@ -54,8 +54,7 @@
 
   void OnURLChecked(const GURL& url,
                     supervised_user::FilteringBehavior behavior,
-                    supervised_user::FilteringBehaviorReason reason,
-                    bool uncertain) override;
+                    supervised_user::FilteringBehaviorDetails details) override;
 
   base::CallbackListSubscription user_settings_subscription_;
 
diff --git a/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc b/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc
index ae38909..7e5b83f 100644
--- a/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc
+++ b/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc
@@ -115,8 +115,8 @@
   source->AddResourcePath("images/right_illustration_dark.svg",
                           IDR_SIGNIN_IMAGES_SHARED_RIGHT_BANNER_DARK_SVG);
   source->AddResourcePath("images/product-logo.svg", IDR_PRODUCT_LOGO_SVG);
-  source->AddResourcePath("tangible_sync_style_shared.css.js",
-                          IDR_SIGNIN_TANGIBLE_SYNC_STYLE_SHARED_CSS_JS);
+  source->AddResourcePath("tangible_sync_style_shared_lit.css.js",
+                          IDR_SIGNIN_TANGIBLE_SYNC_STYLE_SHARED_LIT_CSS_JS);
   source->AddResourcePath("signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS);
 
   source->AddString("choiceList", GetChoiceListJSON(profile_.get()));
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index e791fb4..c5ec8ea 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -450,7 +450,7 @@
   }
 
   void ValidateDefault(const ContentSetting expected_setting,
-                       const site_settings::SiteSettingSource expected_source,
+                       const std::string expected_source,
                        size_t expected_total_calls) {
     EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
 
@@ -466,12 +466,13 @@
     const base::Value::Dict& default_value = data.arg3()->GetDict();
     const std::string* setting = default_value.FindString(kSetting);
     ASSERT_TRUE(setting);
-    EXPECT_EQ(content_settings::ContentSettingToString(expected_setting),
-              *setting);
+    EXPECT_EQ(*setting,
+              content_settings::ContentSettingToString(expected_setting));
     const std::string* source = default_value.FindString(kSource);
     if (source) {
-      EXPECT_EQ(site_settings::SiteSettingSourceToString(expected_source),
-                *source);
+      EXPECT_EQ(*source, expected_source);
+    } else {
+      EXPECT_TRUE(expected_source.empty());
     }
   }
 
@@ -1291,8 +1292,7 @@
   get_args.Append(kCallbackId);
   get_args.Append(kNotifications);
   handler()->HandleGetDefaultValueForContentType(get_args);
-  ValidateDefault(CONTENT_SETTING_ASK,
-                  site_settings::SiteSettingSource::kDefault, 1U);
+  ValidateDefault(CONTENT_SETTING_ASK, "", 1U);
 
   // Set the default to 'Blocked'.
   base::Value::List set_args;
@@ -1305,8 +1305,19 @@
 
   // Verify that the default has been set to 'Blocked'.
   handler()->HandleGetDefaultValueForContentType(get_args);
-  ValidateDefault(CONTENT_SETTING_BLOCK,
-                  site_settings::SiteSettingSource::kDefault, 3U);
+  ValidateDefault(CONTENT_SETTING_BLOCK, "", 3U);
+}
+
+TEST_F(SiteSettingsHandlerTest, GetEnforcedDefault) {
+  ContentSettingSourceSetter source_setter(profile(),
+                                           ContentSettingsType::NOTIFICATIONS);
+  source_setter.SetPolicyDefault(CONTENT_SETTING_ALLOW);
+
+  base::Value::List get_args;
+  get_args.Append(kCallbackId);
+  get_args.Append(kNotifications);
+  handler()->HandleGetDefaultValueForContentType(get_args);
+  ValidateDefault(CONTENT_SETTING_ALLOW, "policy", 1U);
 }
 
 // Flaky on CrOS and Linux. https://crbug.com/930481
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chrome/browser/ui/webui/settings/site_settings_helper.cc
index 561f74fe..6ac41c9 100644
--- a/chrome/browser/ui/webui/settings/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_helper.cc
@@ -97,7 +97,8 @@
 namespace {
 
 using PermissionStatus = blink::mojom::PermissionStatus;
-using content_settings::SettingSource;
+using ::content_settings::ProviderType;
+using ::content_settings::SettingSource;
 
 // Chooser data group names.
 const char kUsbChooserDataGroupType[] = "usb-devices-data";
@@ -336,8 +337,7 @@
 }
 
 bool IsFromWebUIAllowlistSource(const ContentSettingPatternSource& pattern) {
-  return pattern.source ==
-         content_settings::ProviderType::kWebuiAllowlistProvider;
+  return pattern.source == ProviderType::kWebuiAllowlistProvider;
 }
 
 // If the given |pattern| represents an individual origin, Isolated Web App, or
@@ -403,10 +403,9 @@
 // Retrieves the source of a chooser exception as a string. This method uses the
 // CalculateSiteSettingSource method above to calculate the correct string to
 // use.
-SiteSettingSource GetSourceForChooserException(
-    Profile* profile,
-    ContentSettingsType content_type,
-    content_settings::SettingSource source) {
+SiteSettingSource GetSourceForChooserException(Profile* profile,
+                                               ContentSettingsType content_type,
+                                               SettingSource source) {
   // Prepare the parameters needed by CalculateSiteSettingSource
   content_settings::SettingInfo info;
   info.source = source;
@@ -675,32 +674,57 @@
 }
 
 SiteSettingSource ProviderTypeToSiteSettingsSource(
-    const content_settings::ProviderType provider_type) {
+    const ProviderType provider_type) {
   switch (provider_type) {
-    case content_settings::ProviderType::kWebuiAllowlistProvider:
+    case ProviderType::kWebuiAllowlistProvider:
       return SiteSettingSource::kAllowlist;
-    case content_settings::ProviderType::kPolicyProvider:
-    case content_settings::ProviderType::kSupervisedProvider:
+    case ProviderType::kPolicyProvider:
+    case ProviderType::kSupervisedProvider:
       return SiteSettingSource::kPolicy;
-    case content_settings::ProviderType::kCustomExtensionProvider:
+    case ProviderType::kCustomExtensionProvider:
       return SiteSettingSource::kExtension;
-    case content_settings::ProviderType::kInstalledWebappProvider:
+    case ProviderType::kInstalledWebappProvider:
       return SiteSettingSource::kHostedApp;
-    case content_settings::ProviderType::kOneTimePermissionProvider:
-    case content_settings::ProviderType::kPrefProvider:
+    case ProviderType::kOneTimePermissionProvider:
+    case ProviderType::kPrefProvider:
       return SiteSettingSource::kPreference;
-    case content_settings::ProviderType::kDefaultProvider:
+    case ProviderType::kDefaultProvider:
       return SiteSettingSource::kDefault;
 
-    case content_settings::ProviderType::kNone:
-    case content_settings::ProviderType::kNotificationAndroidProvider:
-    case content_settings::ProviderType::kProviderForTests:
-    case content_settings::ProviderType::kOtherProviderForTests:
+    case ProviderType::kNone:
+    case ProviderType::kNotificationAndroidProvider:
+    case ProviderType::kProviderForTests:
+    case ProviderType::kOtherProviderForTests:
       NOTREACHED_IN_MIGRATION();
       return SiteSettingSource::kPreference;
   }
 }
 
+std::string ProviderToDefaultSettingSourceString(const ProviderType provider) {
+  switch (provider) {
+    case ProviderType::kPolicyProvider:
+      return "policy";
+    case ProviderType::kSupervisedProvider:
+      return "supervised_user";
+    case ProviderType::kCustomExtensionProvider:
+      return "extension";
+    case ProviderType::kOneTimePermissionProvider:
+    case ProviderType::kPrefProvider:
+      return "preference";
+    case ProviderType::kInstalledWebappProvider:
+    case ProviderType::kWebuiAllowlistProvider:
+    case ProviderType::kDefaultProvider:
+      return "default";
+
+    case ProviderType::kNone:
+    case ProviderType::kNotificationAndroidProvider:
+    case ProviderType::kProviderForTests:
+    case ProviderType::kOtherProviderForTests:
+      NOTREACHED_IN_MIGRATION();
+      return "preference";
+  }
+}
+
 // Add an "Allow"-entry to the list of |exceptions| for a |url_pattern| from
 // the web extent of a hosted |app|.
 void AddExceptionForHostedApp(const std::string& url_pattern,
@@ -938,7 +962,7 @@
 }
 
 using RawPatternSettings =
-    std::map<std::pair<ContentSettingsPattern, content_settings::ProviderType>,
+    std::map<std::pair<ContentSettingsPattern, ProviderType>,
              OnePatternSettings,
              std::greater<>>;
 
@@ -955,7 +979,7 @@
     // Don't add default settings.
     if (setting.primary_pattern == ContentSettingsPattern::Wildcard() &&
         setting.secondary_pattern == ContentSettingsPattern::Wildcard() &&
-        setting.source != content_settings::ProviderType::kPrefProvider) {
+        setting.source != ProviderType::kPrefProvider) {
       continue;
     }
 
@@ -965,7 +989,7 @@
     // incognito-only exceptions, meaning these are necesssarily duplicates.
     if (map->IsOffTheRecord() &&
         (!setting.incognito ||
-         setting.source == content_settings::ProviderType::kPolicyProvider)) {
+         setting.source == ProviderType::kPolicyProvider)) {
       continue;
     }
 
@@ -1056,7 +1080,7 @@
 
   // Keep the exceptions sorted by provider so they will be displayed in
   // precedence order.
-  std::map<content_settings::ProviderType, std::vector<base::Value::Dict>>
+  std::map<ProviderType, std::vector<base::Value::Dict>>
       all_provider_exceptions;
 
   for (const auto& [primary_pattern_and_source, one_settings] :
@@ -1083,8 +1107,8 @@
   // the policy-set allowed URLs, which should be displayed in the same manner.
   if (type == ContentSettingsType::MEDIASTREAM_MIC ||
       type == ContentSettingsType::MEDIASTREAM_CAMERA) {
-    auto& policy_exceptions = all_provider_exceptions
-        [content_settings::ProviderType::kPolicyProvider];
+    auto& policy_exceptions =
+        all_provider_exceptions[ProviderType::kPolicyProvider];
     DCHECK(policy_exceptions.empty());
     GetPolicyAllowedUrls(type, &policy_exceptions, web_ui, incognito);
   }
@@ -1095,8 +1119,8 @@
           features::kFileSystemAccessPersistentPermissions) &&
       (type == ContentSettingsType::FILE_SYSTEM_READ_GUARD ||
        type == ContentSettingsType::FILE_SYSTEM_WRITE_GUARD)) {
-    auto& urls_with_granted_entries = all_provider_exceptions
-        [content_settings::ProviderType::kDefaultProvider];
+    auto& urls_with_granted_entries =
+        all_provider_exceptions[ProviderType::kDefaultProvider];
     GetFileSystemGrantedEntries(&urls_with_granted_entries, profile, incognito);
   }
 
@@ -1166,16 +1190,14 @@
 void GetContentCategorySetting(const HostContentSettingsMap* map,
                                ContentSettingsType content_type,
                                base::Value::Dict* object) {
-  auto provider = content_settings::ProviderType::kDefaultProvider;
+  auto provider = ProviderType::kDefaultProvider;
   std::string setting = content_settings::ContentSettingToString(
       map->GetDefaultContentSetting(content_type, &provider));
   DCHECK(!setting.empty());
 
   object->Set(kSetting, setting);
-
-  SiteSettingSource source = ProviderTypeToSiteSettingsSource(provider);
-  if (source != SiteSettingSource::kDefault) {
-    object->Set(kSource, SiteSettingSourceToString(source));
+  if (provider != ProviderType::kDefaultProvider) {
+    object->Set(kSource, ProviderToDefaultSettingSourceString(provider));
   }
 }
 
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.h b/chrome/browser/ui/webui/settings/site_settings_helper.h
index 7b659ad..a699eee3 100644
--- a/chrome/browser/ui/webui/settings/site_settings_helper.h
+++ b/chrome/browser/ui/webui/settings/site_settings_helper.h
@@ -51,6 +51,10 @@
   base::Time expiration;
 };
 
+// An enum representing the source of a per-site content setting (corresponds to
+// UI enum of the same name in constants.ts).
+// Note: this should not be used for default content setting sources, which
+// instead use `ProviderToDefaultSettingSourceString`.
 enum class SiteSettingSource {
   kAdsFilterBlocklist,
   kEmbargo,
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
index c9b9cc8..fc7e308 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
@@ -638,7 +638,7 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// ReadAnythingCoordinator::Observer:
+// ReadAnythingSidePanelController::Observer:
 ///////////////////////////////////////////////////////////////////////////////
 
 void ReadAnythingUntrustedPageHandler::Activate(bool active) {
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
index e08302c..2926784 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
@@ -13,7 +13,6 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.h"
 #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_controller.h"
 #include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_screenshotter.h"
 #include "chrome/common/accessibility/read_anything.mojom.h"
@@ -85,7 +84,6 @@
 #endif
     public ui::AXActionHandlerObserver,
     public read_anything::mojom::UntrustedPageHandler,
-    public ReadAnythingCoordinator::Observer,
     public ReadAnythingSidePanelController::Observer,
     public translate::TranslateDriver::LanguageDetectionObserver {
  public:
@@ -162,7 +160,7 @@
   void OnCollapseSelection() override;
   void OnScreenshotRequested() override;
 
-  // ReadAnythingCoordinator::Observer:
+  // ReadAnythingSidePanelController::Observer:
   void Activate(bool active) override;
 
   void SetDefaultLanguageCode(const std::string& code);
diff --git a/chrome/browser/ui/webui/signin/ash/BUILD.gn b/chrome/browser/ui/webui/signin/ash/BUILD.gn
new file mode 100644
index 0000000..5dd21c2
--- /dev/null
+++ b/chrome/browser/ui/webui/signin/ash/BUILD.gn
@@ -0,0 +1,110 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash)
+
+static_library("ash") {
+  sources = [
+    "inline_login_dialog.cc",
+    "inline_login_dialog.h",
+    "inline_login_dialog_onboarding.cc",
+    "inline_login_dialog_onboarding.h",
+    "inline_login_handler_impl.cc",
+    "inline_login_handler_impl.h",
+    "inline_login_handler_modal_delegate.cc",
+    "inline_login_handler_modal_delegate.h",
+    "signin_helper.cc",
+    "signin_helper.h",
+    "user_cloud_signin_restriction_policy_fetcher.cc",
+    "user_cloud_signin_restriction_policy_fetcher.h",
+  ]
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+
+  deps = [
+    "//ash",
+    "//ash/constants",
+    "//base",
+    "//chrome/browser:browser_process",
+    "//chrome/browser:primitives",
+    "//chrome/browser/ash/account_manager",
+    "//chrome/browser/ash/arc/auth",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/webui/ash/edu_coexistence",
+    "//chrome/common",
+    "//chromeos/ash/components/account_manager",
+    "//chromeos/version",
+    "//components/account_manager_core",
+    "//components/prefs",
+    "//components/session_manager/core",
+    "//components/user_manager",
+    "//components/web_modal",
+    "//crypto",
+    "//ui/aura",
+    "//ui/base",
+    "//ui/chromeos",
+    "//ui/gfx",
+    "//ui/views",
+    "//url",
+  ]
+
+  allow_circular_includes_from = [
+    "//chrome/browser/ash/account_manager",
+    "//chrome/browser/ash/arc/auth",
+    "//chrome/browser/ui/webui/ash/edu_coexistence",
+  ]
+}
+
+source_set("browser_tests") {
+  testonly = true
+
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+  sources = [
+    "inline_login_dialog_browsertest.cc",
+    "inline_login_handler_impl_browsertest.cc",
+    "signin_helper_browsertest.cc",
+  ]
+
+  deps = [
+    ":ash",
+    "//ash/constants",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser",
+    "//chrome/browser:browser_process",
+    "//chrome/browser/ash/account_manager",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui",
+    "//chrome/browser/ui/webui/ash/edu_coexistence",
+    "//chrome/test:test_support",
+    "//chromeos/ash/components/account_manager",
+    "//chromeos/ash/components/standalone_browser",
+    "//components/account_manager_core",
+    "//components/account_manager_core:test_support",
+    "//components/constrained_window",
+    "//components/user_manager",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [ "user_cloud_signin_restriction_policy_fetcher_unittest.cc" ]
+
+  deps = [
+    ":ash",
+    "//base",
+    "//base/test:test_support",
+    "//services/network:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/browser/ui/webui/signin/ash/DEPS b/chrome/browser/ui/webui/signin/ash/DEPS
index 032f964..32aa8bf 100644
--- a/chrome/browser/ui/webui/signin/ash/DEPS
+++ b/chrome/browser/ui/webui/signin/ash/DEPS
@@ -4,4 +4,32 @@
   # https://docs.google.com/document/d/1g-98HpzA8XcoGBWUv1gQNr4rbnD5yfvbtYZyPDDbkaE
   "+ash/system/session",
   "+ash/style",
+
+  # ChromeOS should not depend on //chrome. See //docs/chromeos/code.md for
+  # details.
+  "-chrome",
+
+  # This directory is in //chrome, which violates the rule above. Allow this
+  # directory to #include its own files.
+  "+chrome/browser/ui/webui/signin/ash",
+
+  # Existing dependencies within //chrome. There is an active effort to
+  # refactor ash codes to break these dependencies; see b/332804822.
+  # Whenever possible, avoid adding new //chrome dependencies to this list.
+  "+chrome/browser/ash/account_manager",
+  "+chrome/browser/ash/crosapi",
+  "+chrome/browser/ash/login/users",
+  "+chrome/browser/ash/profiles",
+  "+chrome/browser/browser_process.h",
+  "+chrome/browser/browser_process_platform_part.h",
+  "+chrome/browser/profiles",
+  "+chrome/browser/signin",
+  "+chrome/browser/ui/browser_commands.h",
+  "+chrome/browser/ui/browser.h",
+  "+chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h",
+  "+chrome/browser/ui/webui/ash/edu_coexistence",
+  "+chrome/browser/ui/webui/ash/system_web_dialog_delegate.h",
+  "+chrome/browser/ui/webui/signin/inline_login_handler.h",
+  "+chrome/common",
+  "+chrome/test",
 ]
diff --git a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.cc b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.cc
index ab65ff77..1ab074d 100644
--- a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.cc
+++ b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.cc
@@ -75,7 +75,7 @@
        IDR_SIGNIN_DICE_WEB_SIGNIN_INTERCEPT_DICE_WEB_SIGNIN_INTERCEPT_APP_HTML_JS},
       {"dice_web_signin_intercept_browser_proxy.js",
        IDR_SIGNIN_DICE_WEB_SIGNIN_INTERCEPT_DICE_WEB_SIGNIN_INTERCEPT_BROWSER_PROXY_JS},
-      {"signin_shared_lit.css.js", IDR_SIGNIN_SIGNIN_SHARED_LIT_CSS_JS},
+      {"signin_shared.css.js", IDR_SIGNIN_SIGNIN_SHARED_CSS_JS},
       {"signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS},
       {"images/split_header.svg",
        IDR_SIGNIN_DICE_WEB_SIGNIN_INTERCEPT_IMAGES_SPLIT_HEADER_SVG},
diff --git a/chrome/browser/ui/webui/signin/managed_user_profile_notice_ui.cc b/chrome/browser/ui/webui/signin/managed_user_profile_notice_ui.cc
index fb8b07b..212166b 100644
--- a/chrome/browser/ui/webui/signin/managed_user_profile_notice_ui.cc
+++ b/chrome/browser/ui/webui/signin/managed_user_profile_notice_ui.cc
@@ -79,7 +79,6 @@
       {"images/enrollment_timeout_dark.svg",
        IDR_SIGNIN_MANAGED_USER_PROFILE_NOTICE_IMAGES_ENROLLMENT_TIMEOUT_DARK_SVG},
       {"signin_shared.css.js", IDR_SIGNIN_SIGNIN_SHARED_CSS_JS},
-      {"signin_shared_lit.css.js", IDR_SIGNIN_SIGNIN_SHARED_LIT_CSS_JS},
       {"signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS},
       {"tangible_sync_style_shared_lit.css.js",
        IDR_SIGNIN_TANGIBLE_SYNC_STYLE_SHARED_LIT_CSS_JS},
diff --git a/chrome/browser/ui/webui/signin/profile_customization_ui.cc b/chrome/browser/ui/webui/signin/profile_customization_ui.cc
index 58441ae..f971e27f 100644
--- a/chrome/browser/ui/webui/signin/profile_customization_ui.cc
+++ b/chrome/browser/ui/webui/signin/profile_customization_ui.cc
@@ -52,7 +52,7 @@
        IDR_SIGNIN_PROFILE_CUSTOMIZATION_IMAGES_PROFILE_CUSTOMIZATION_ILLUSTRATION_SVG},
       {"images/profile_customization_illustration_dark.svg",
        IDR_SIGNIN_PROFILE_CUSTOMIZATION_IMAGES_PROFILE_CUSTOMIZATION_ILLUSTRATION_DARK_SVG},
-      {"signin_shared_lit.css.js", IDR_SIGNIN_SIGNIN_SHARED_LIT_CSS_JS},
+      {"signin_shared.css.js", IDR_SIGNIN_SIGNIN_SHARED_CSS_JS},
       {"signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS},
   };
 
diff --git a/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.cc b/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.cc
index e5db988e..932fe49 100644
--- a/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.cc
+++ b/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.cc
@@ -41,7 +41,7 @@
        IDR_SIGNIN_SIGNIN_EMAIL_CONFIRMATION_SIGNIN_EMAIL_CONFIRMATION_APP_CSS_JS},
       {"signin_email_confirmation_app.html.js",
        IDR_SIGNIN_SIGNIN_EMAIL_CONFIRMATION_SIGNIN_EMAIL_CONFIRMATION_APP_HTML_JS},
-      {"signin_shared_lit.css.js", IDR_SIGNIN_SIGNIN_SHARED_LIT_CSS_JS},
+      {"signin_shared.css.js", IDR_SIGNIN_SIGNIN_SHARED_CSS_JS},
       {"signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS},
   };
   source->AddResourcePaths(kResources);
diff --git a/chrome/browser/ui/webui/signin/signin_error_ui.cc b/chrome/browser/ui/webui/signin/signin_error_ui.cc
index 8038883..ee46bc8 100644
--- a/chrome/browser/ui/webui/signin/signin_error_ui.cc
+++ b/chrome/browser/ui/webui/signin/signin_error_ui.cc
@@ -74,7 +74,7 @@
       {"signin_error_app.css.js",
        IDR_SIGNIN_SIGNIN_ERROR_SIGNIN_ERROR_APP_CSS_JS},
       {"signin_error.js", IDR_SIGNIN_SIGNIN_ERROR_SIGNIN_ERROR_JS},
-      {"signin_shared_lit.css.js", IDR_SIGNIN_SIGNIN_SHARED_LIT_CSS_JS},
+      {"signin_shared.css.js", IDR_SIGNIN_SIGNIN_SHARED_CSS_JS},
       {"signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS},
   };
   source->AddResourcePaths(kResources);
diff --git a/chrome/browser/ui/webui/signin/signin_reauth_ui.cc b/chrome/browser/ui/webui/signin/signin_reauth_ui.cc
index c1b7dc30..fd1b9f3 100644
--- a/chrome/browser/ui/webui/signin/signin_reauth_ui.cc
+++ b/chrome/browser/ui/webui/signin/signin_reauth_ui.cc
@@ -111,7 +111,7 @@
        IDR_SIGNIN_SIGNIN_REAUTH_SIGNIN_REAUTH_APP_CSS_JS},
       {"signin_reauth_browser_proxy.js",
        IDR_SIGNIN_SIGNIN_REAUTH_SIGNIN_REAUTH_BROWSER_PROXY_JS},
-      {"signin_shared_lit.css.js", IDR_SIGNIN_SIGNIN_SHARED_LIT_CSS_JS},
+      {"signin_shared.css.js", IDR_SIGNIN_SIGNIN_SHARED_CSS_JS},
       {"signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS},
       // Resources for the account passwords reauth.
       {"images/signin_reauth_illustration.svg",
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
index 6a23ec2..2008829 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
@@ -157,7 +157,7 @@
 
   static constexpr webui::ResourcePath kResources[] = {
       {"icons.html.js", IDR_SIGNIN_ICONS_HTML_JS},
-      {"signin_shared_lit.css.js", IDR_SIGNIN_SIGNIN_SHARED_LIT_CSS_JS},
+      {"signin_shared.css.js", IDR_SIGNIN_SIGNIN_SHARED_CSS_JS},
       {"signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS},
       {"tangible_sync_style_shared_lit.css.js",
        IDR_SIGNIN_TANGIBLE_SYNC_STYLE_SHARED_LIT_CSS_JS},
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_ui.cc b/chrome/browser/ui/webui/tab_search/tab_search_ui.cc
index 8ce93c17..9d9f687 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_ui.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_ui.cc
@@ -92,7 +92,7 @@
       {"searchTabs", IDS_TAB_SEARCH_SEARCH_TABS},
       {"tabCount", IDS_TAB_SEARCH_TAB_COUNT},
       {"tabSearchTabName", IDS_TAB_SEARCH_TAB_NAME},
-      // Tab organization UI strings
+      // Auto tab groups UI strings
       {"clearAriaLabel", IDS_TAB_ORGANIZATION_CLEAR_ARIA_LABEL},
       {"clearSuggestions", IDS_TAB_ORGANIZATION_CLEAR_SUGGESTIONS},
       {"createGroup", IDS_TAB_ORGANIZATION_CREATE_GROUP},
@@ -150,6 +150,8 @@
       {"tipTitle", IDS_TAB_ORGANIZATION_TIP_TITLE},
       {"thumbsDown", IDS_TAB_ORGANIZATION_THUMBS_DOWN},
       {"thumbsUp", IDS_TAB_ORGANIZATION_THUMBS_UP},
+      // Declutter UI strings
+      {"declutterTitle", IDS_DECLUTTER_TITLE},
   };
   source->AddLocalizedStrings(kStrings);
   source->AddBoolean("useRipples", views::PlatformStyle::kUseRipples);
diff --git a/chrome/browser/vr/cmd_buffer_surface_provider.cc b/chrome/browser/vr/cmd_buffer_surface_provider.cc
index a011b10..64db40c 100644
--- a/chrome/browser/vr/cmd_buffer_surface_provider.cc
+++ b/chrome/browser/vr/cmd_buffer_surface_provider.cc
@@ -8,9 +8,9 @@
 #include "gpu/command_buffer/client/gles2_implementation.h"
 #include "gpu/command_buffer/client/gles2_lib.h"
 #include "gpu/skia_bindings/gl_bindings_skia_cmd_buffer.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace vr {
diff --git a/chrome/browser/vr/native_gl_surface_provider.cc b/chrome/browser/vr/native_gl_surface_provider.cc
index 47a847b..815d92c 100644
--- a/chrome/browser/vr/native_gl_surface_provider.cc
+++ b/chrome/browser/vr/native_gl_surface_provider.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/vr/native_gl_surface_provider.h"
 
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gl/gl_implementation.h"
diff --git a/chrome/browser/vr/skia_surface_provider.cc b/chrome/browser/vr/skia_surface_provider.cc
index b7170286..c9e8dbf 100644
--- a/chrome/browser/vr/skia_surface_provider.cc
+++ b/chrome/browser/vr/skia_surface_provider.cc
@@ -7,12 +7,12 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrRecordingContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrRecordingContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace vr {
diff --git a/chrome/browser/vr/skia_surface_provider.h b/chrome/browser/vr/skia_surface_provider.h
index 9910a80..2038d355 100644
--- a/chrome/browser/vr/skia_surface_provider.h
+++ b/chrome/browser/vr/skia_surface_provider.h
@@ -8,7 +8,7 @@
 #include "base/functional/function_ref.h"
 #include "device/vr/gl_bindings.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 class SkSurface;
 class SkCanvas;
diff --git a/chrome/browser/webauthn/unexportable_key_utils_chromeos_unittest.cc b/chrome/browser/webauthn/unexportable_key_utils_chromeos_unittest.cc
index e1dbd14..08269f17 100644
--- a/chrome/browser/webauthn/unexportable_key_utils_chromeos_unittest.cc
+++ b/chrome/browser/webauthn/unexportable_key_utils_chromeos_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/auth/active_session_auth_controller.h"
+#include "ash/public/cpp/auth/active_session_fingerprint_client.h"
 #include "ash/public/cpp/webauthn_dialog_controller.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
@@ -73,6 +74,10 @@
               (std::unique_ptr<ash::AuthRequest> auth_request),
               (override));
   MOCK_METHOD(bool, IsShown, (), (const override));
+  MOCK_METHOD(void,
+              SetFingerprintClient,
+              (ash::ActiveSessionFingerprintClient * fp_client),
+              (override));
 };
 
 class UserVerifyingKeyUtilsCrosTest
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 238c88e..2c53ab7 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1724889563-dfbc171bb629eef62e6ac4b17cfd0d2512e0e486-f97fed8662eedf5f552aad5b3c96acbe96b3348a.profdata
+chrome-android32-main-1725429024-e636ddb77f588349b35124b8bd84d42f2d444393-b0e70c56f087e06bfa9561b4a62f92f9ecc18aad.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index a974c68..dbcd282b 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1724889563-96e595bfee268e00fe6beefbcf12f963c14a9e27-f97fed8662eedf5f552aad5b3c96acbe96b3348a.profdata
+chrome-android64-main-1725436681-5f2cf6e2801cacf7706a93e37378957e99e86f10-d60cb106d55d3e63e4680b2ca015f429438a4195.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 59e8fdd..03cb3dcce 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1725386325-7ccd7a6d3677514ebe19caea03974004ade6f179-b5129dee21dbb804cb1c83fb36a5d53a40618516.profdata
+chrome-mac-arm-main-1725443873-d8c5400be6c692c8768788318e424e7981e41dd5-91b41bec3117d99bbb1e27deb7c9b8b8a0bf2a5e.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 21b1479..bcab6718 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1725364637-c55edcae7bbc0e0f410c2b14fe628d226f7a8a30-26695bbc5f100cc7e4c982593ef050dc431e5efa.profdata
+chrome-mac-main-1725429024-626b0959eb22c556169beaf6fab1967317575bdf-b0e70c56f087e06bfa9561b4a62f92f9ecc18aad.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index d4e6e94..26f4f1bb 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1725375522-b504c58f193ff3bcc810e12dc37411824b69b197-6b2f4b11472254866735f549155a86162cc61b4b.profdata
+chrome-win32-main-1725440264-fa4aad4aeb725b222e52f8aef038cb1dc5ffed89-d3994b7788037958a188262ab4f8e7874891fa02.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index d8da5ae..e430c41b 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1725375522-b6cc32742732a68ec9e855c58db3ce51e782f951-6b2f4b11472254866735f549155a86162cc61b4b.profdata
+chrome-win64-main-1725440264-55f11062a557780b04dbaa8e014f8508fb9ee97d-d3994b7788037958a188262ab4f8e7874891fa02.profdata
diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl
index 57b034c..f759a23 100644
--- a/chrome/common/extensions/api/autofill_private.idl
+++ b/chrome/common/extensions/api/autofill_private.idl
@@ -121,6 +121,7 @@
     ADDRESS_HOME_APT_TYPE,
     ADDRESS_HOME_HOUSE_NUMBER_AND_APT,
     SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES,
+    IMPROVED_PREDICTION,
     MAX_VALID_FIELD_TYPE
   };
 
diff --git a/chrome/enterprise_companion/app/app_server.cc b/chrome/enterprise_companion/app/app_server.cc
index 4fdb80a6..641205c 100644
--- a/chrome/enterprise_companion/app/app_server.cc
+++ b/chrome/enterprise_companion/app/app_server.cc
@@ -31,6 +31,7 @@
 
 #include <atlsecurity.h>
 
+#include "base/win/scoped_com_initializer.h"
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/scoped_handle.h"
 #endif
@@ -95,6 +96,15 @@
   void FirstTaskRun() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+#if BUILDFLAG(IS_WIN)
+    if (!com_initializer_.Succeeded()) {
+      VLOG(1) << "Failed to initialize COM";
+      Shutdown(EnterpriseCompanionStatus(
+          ApplicationError::kCOMInitializationFailed));
+      return;
+    }
+#endif
+
     lock_ = CreateScopedLock();
     if (!lock_) {
       Shutdown(EnterpriseCompanionStatus(ApplicationError::kCannotAcquireLock));
@@ -134,6 +144,10 @@
   base::Thread net_thread_{"Network"};
 #endif
 
+#if BUILDFLAG(IS_WIN)
+  base::win::ScopedCOMInitializer com_initializer_{
+      base::win::ScopedCOMInitializer::kMTA};
+#endif
   base::SequenceBound<URLLoaderFactoryProvider> url_loader_factory_provider_;
   std::unique_ptr<ScopedLock> lock_;
   std::unique_ptr<mojom::EnterpriseCompanion> stub_;
diff --git a/chrome/enterprise_companion/enterprise_companion.cc b/chrome/enterprise_companion/enterprise_companion.cc
index dc33501..04530981 100644
--- a/chrome/enterprise_companion/enterprise_companion.cc
+++ b/chrome/enterprise_companion/enterprise_companion.cc
@@ -29,6 +29,7 @@
 
 // Command line arguments.
 const char kLoggingModuleSwitch[] = "vmodule";
+const char kLoggingModuleSwitchValue[] = "*/chrome/enterprise_companion/*=2";
 const char kCrashHandlerSwitch[] = "crash-handler";
 const char kCrashMeSwitch[] = "crash-me";
 const char kShutdownSwitch[] = "shutdown";
@@ -41,8 +42,6 @@
 
 namespace {
 
-constexpr char kLoggingModuleSwitchValue[] =
-    "*/chrome/enterprise_companion/*=2";
 constexpr int64_t kLogRotateAtSize = 1024 * 1024;  // 1 MiB.
 
 void InitLogging() {
diff --git a/chrome/enterprise_companion/enterprise_companion.h b/chrome/enterprise_companion/enterprise_companion.h
index 47e05086..366318ec 100644
--- a/chrome/enterprise_companion/enterprise_companion.h
+++ b/chrome/enterprise_companion/enterprise_companion.h
@@ -14,6 +14,8 @@
 
 // Specifies the logging module filter.
 extern const char kLoggingModuleSwitch[];
+// The default logging module switch value.
+extern const char kLoggingModuleSwitchValue[];
 // Runs as the embedded Crashpad handler.
 extern const char kCrashHandlerSwitch[];
 // Crash the program for testing purposes.
diff --git a/chrome/enterprise_companion/enterprise_companion_status.cc b/chrome/enterprise_companion/enterprise_companion_status.cc
index e7eb60ba..b97599b 100644
--- a/chrome/enterprise_companion/enterprise_companion_status.cc
+++ b/chrome/enterprise_companion/enterprise_companion_status.cc
@@ -97,6 +97,8 @@
       return "The application could not be installed.";
     case ApplicationError::kIpcCallerNotAllowed:
       return "The IPC caller is not allowed.";
+    case ApplicationError::kCOMInitializationFailed:
+      return "COM initialization failed.";
   }
 }
 
diff --git a/chrome/enterprise_companion/enterprise_companion_status.h b/chrome/enterprise_companion/enterprise_companion_status.h
index bd9867ec..e9c2188 100644
--- a/chrome/enterprise_companion/enterprise_companion_status.h
+++ b/chrome/enterprise_companion/enterprise_companion_status.h
@@ -36,6 +36,8 @@
   kInstallationFailed,
   // The IPC caller is not allowed to perform the requested action.
   kIpcCallerNotAllowed,
+  // Failed to initialize COM on Windows.
+  kCOMInitializationFailed,
 };
 
 // Represents an error which was deserialized from an external source (e.g.
diff --git a/chrome/enterprise_companion/installer_mac.mm b/chrome/enterprise_companion/installer_mac.mm
index 45cd05ee..5e432d4 100644
--- a/chrome/enterprise_companion/installer_mac.mm
+++ b/chrome/enterprise_companion/installer_mac.mm
@@ -12,16 +12,26 @@
 #include "base/logging.h"
 #include "base/process/launch.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/enterprise_companion/enterprise_companion_branding.h"
 #include "chrome/enterprise_companion/enterprise_companion_version.h"
 #include "chrome/enterprise_companion/installer_paths.h"
 #include "chrome/enterprise_companion/installer_posix.h"
 #include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
 
+#if defined(ADDRESS_SANITIZER)
+#include "base/base_paths.h"
+#include "base/path_service.h"
+#endif
+
 namespace enterprise_companion {
 
 namespace {
 
+#if defined(ADDRESS_SANITIZER)
+constexpr char kAsanDylibFilename[] = "libclang_rt.asan_osx_dynamic.dylib";
+#endif
+
 constexpr base::TimeDelta kKSAdminTimeout = base::Minutes(5);
 
 // Register the installation with ksadmin.
@@ -80,6 +90,23 @@
     return false;
   }
 
+#if defined(ADDRESS_SANITIZER)
+  base::FilePath dir_exe;
+  if (!base::PathService::Get(base::DIR_EXE, &dir_exe)) {
+    LOG(ERROR) << "Failed to get the current executable's directory.";
+    return false;
+  }
+
+  base::FilePath asan_dylib_path = dir_exe.AppendASCII(kAsanDylibFilename);
+  if (base::PathExists(asan_dylib_path) &&
+      !base::CopyFile(asan_dylib_path,
+                      install_directory->Append(asan_dylib_path.BaseName()))) {
+    LOG(ERROR) << "Failed to copy " << asan_dylib_path << " to "
+               << *install_directory;
+    return false;
+  }
+#endif
+
   if (!RegisterInstallation(*install_directory)) {
     if (base::PathExists(backup_exe)) {
       if (!base::Move(backup_exe, exe_path)) {
diff --git a/chrome/enterprise_companion/test/integration_tests.cc b/chrome/enterprise_companion/test/integration_tests.cc
index 72f40f9..c6b39c5 100644
--- a/chrome/enterprise_companion/test/integration_tests.cc
+++ b/chrome/enterprise_companion/test/integration_tests.cc
@@ -6,23 +6,22 @@
 #include <string>
 
 #include "base/base64.h"
-#include "base/base_paths.h"
 #include "base/command_line.h"
 #include "base/containers/flat_map.h"
+#include "base/environment.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/json/json_file_value_serializer.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/path_service.h"
 #include "base/process/process.h"
+#include "base/strings/strcat.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/enterprise_companion/app/app.h"
 #include "chrome/enterprise_companion/device_management_storage/dm_storage.h"
-#include "chrome/enterprise_companion/enterprise_companion.h"
 #include "chrome/enterprise_companion/enterprise_companion_client.h"
 #include "chrome/enterprise_companion/enterprise_companion_status.h"
 #include "chrome/enterprise_companion/global_constants.h"
@@ -57,22 +56,15 @@
 constexpr char kFakeMachineLevelExtensionPolicyValue[] =
     "machine-level-extension payload";
 
-#if BUILDFLAG(IS_POSIX)
-constexpr char kTestExe[] = "enterprise_companion_test";
-#elif BUILDFLAG(IS_WIN)
-constexpr char kTestExe[] = "enterprise_companion_test.exe";
-#endif
-
 }  // namespace
 
 class IntegrationTests : public ::testing::Test {
  public:
   void SetUp() override {
     ASSERT_TRUE(dm_test_server_.Start());
-    ASSERT_TRUE(base::PathExists(test_exe_path_));
-    GetTestMethods().Clean();
-    GetTestMethods().ExpectClean();
-    InstallConstantsOverrides();
+    ASSERT_NO_FATAL_FAILURE(GetTestMethods().Clean());
+    ASSERT_NO_FATAL_FAILURE(GetTestMethods().ExpectClean());
+    ASSERT_NO_FATAL_FAILURE(InstallConstantsOverrides());
 
     scoped_refptr<device_management_storage::DMStorage> dm_storage =
         device_management_storage::GetDefaultDMStorage();
@@ -86,22 +78,12 @@
     if (server_process_.IsValid()) {
       ShutdownServerAndWaitForExit();
     }
-    GetTestMethods().Clean();
-    GetTestMethods().ExpectClean();
+    ASSERT_NO_FATAL_FAILURE(CopyApplicationArtifacts());
+    ASSERT_NO_FATAL_FAILURE(GetTestMethods().Clean());
+    ASSERT_NO_FATAL_FAILURE(GetTestMethods().ExpectClean());
   }
 
  protected:
-  // Install the app under test.
-  bool Install() {
-#if BUILDFLAG(IS_MAC)
-    InstallFakeKSAdmin(/*should_succeed=*/true);
-#endif
-    base::CommandLine command_line(test_exe_path_);
-    command_line.AppendSwitch(kInstallSwitch);
-    base::Process installer_process = base::LaunchProcess(command_line, {});
-    return WaitForProcess(installer_process) == 0;
-  }
-
   // Launches the installed app.
   void LaunchApp() {
     std::optional<base::FilePath> install_dir = GetInstallDirectory();
@@ -179,10 +161,6 @@
   }
 
   void StoreDMToken(const std::string& dm_token) {
-    ASSERT_FALSE(server_process_.IsValid())
-        << "The DM token cannot be written to reliably once the server "
-           "process "
-           "has launched.";
     scoped_refptr<device_management_storage::DMStorage> dm_storage =
         device_management_storage::GetDefaultDMStorage();
     ASSERT_TRUE(dm_storage);
@@ -254,14 +232,14 @@
   // Expects that the policy values configured via
   // `SetDefaultPolicyFetchResponses` have been persisted to disk.
   void ExpectDefaultPolicyValuesPersisted() {
-    ExpectPersistedPolicyValues({
+    ASSERT_NO_FATAL_FAILURE(ExpectPersistedPolicyValues({
         {policy::dm_protocol::kGoogleUpdateMachineLevelOmahaPolicyType,
          kFakeMachineLevelOmahaPolicyValue},
         {policy::dm_protocol::kChromeMachineLevelUserCloudPolicyType,
          kFakeMachineLevelUserPolicyValue},
         {policy::dm_protocol::kChromeMachineLevelExtensionCloudPolicyType,
          kFakeMachineLevelExtensionPolicyValue},
-    });
+    }));
   }
 
   void RegisterClientWithDMServer() {
@@ -284,16 +262,64 @@
   base::FilePath policy_cache_root_;
 
  private:
-  const base::FilePath test_exe_path_ =
-      base::PathService::CheckedGet(base::DIR_EXE).AppendASCII(kTestExe);
+  // Copies artifacts from the installed application (e.g. logs, crash dumps,
+  // etc.) to ISOLATED_OUTDIR, if present.
+  void CopyApplicationArtifacts() {
+    std::string isolated_outdir_str;
+    if (!base::Environment::Create()->GetVar("ISOLATED_OUTDIR",
+                                             &isolated_outdir_str)) {
+      return;
+    }
+
+    std::optional<base::FilePath> install_dir = GetInstallDirectory();
+    ASSERT_TRUE(install_dir);
+    base::FilePath artifacts_dir =
+        base::FilePath::FromASCII(isolated_outdir_str)
+            .AppendASCII(base::StrCat(
+                {testing::UnitTest::GetInstance()->current_test_suite()->name(),
+                 ".",
+                 testing::UnitTest::GetInstance()
+                     ->current_test_info()
+                     ->name()}));
+
+    ASSERT_NO_FATAL_FAILURE(
+        CopyApplicationArtifacts(*install_dir, artifacts_dir));
+
+#if BUILDFLAG(IS_WIN)
+    std::optional<base::FilePath> alt_install_dir =
+        GetInstallDirectoryForAlternateArch();
+    if (alt_install_dir) {
+      ASSERT_NO_FATAL_FAILURE(CopyApplicationArtifacts(
+          *alt_install_dir, artifacts_dir.AppendASCII("alt_arch")));
+    }
+#endif
+  }
+
+  void CopyApplicationArtifacts(const base::FilePath& install_dir,
+                                const base::FilePath& artifacts_dir) {
+    ASSERT_TRUE(base::CreateDirectory(artifacts_dir));
+    base::FilePath log_path =
+        install_dir.AppendASCII("enterprise_companion.log");
+    if (base::PathExists(log_path)) {
+      ASSERT_TRUE(
+          base::CopyFile(log_path, artifacts_dir.Append(log_path.BaseName())));
+    }
+
+    base::FilePath crash_db_path = install_dir.AppendASCII("Crashpad");
+    if (base::PathExists(crash_db_path)) {
+      ASSERT_TRUE(base::CopyDirectory(
+          crash_db_path, artifacts_dir.AppendASCII("Crashpad"), true));
+    }
+  }
+
   ScopedIPCSupportWrapper ipc_support_;
 };
 
 // Running the application installer should configure a valid installation.
 TEST_F(IntegrationTests, Install) {
-  ASSERT_TRUE(Install());
+  ASSERT_NO_FATAL_FAILURE(GetTestMethods().Install());
 
-  GetTestMethods().ExpectInstalled();
+  ASSERT_NO_FATAL_FAILURE(GetTestMethods().ExpectInstalled());
 }
 
 // Attempting to shut down the server when it's not running should fail.
@@ -304,9 +330,9 @@
 
 // The server should shut down upon request.
 TEST_F(IntegrationTests, Shutdown) {
-  ASSERT_TRUE(Install());
-  LaunchApp();
-  WaitForServerStart();
+  ASSERT_NO_FATAL_FAILURE(GetTestMethods().Install());
+  ASSERT_NO_FATAL_FAILURE(LaunchApp());
+  ASSERT_NO_FATAL_FAILURE(WaitForServerStart());
 
   ShutdownServerAndWaitForExit();
 }
@@ -314,9 +340,9 @@
 // The server should fail to fetch policies if no enrollment token is present
 // and the device is not registered.
 TEST_F(IntegrationTests, FetchPoliciesWithoutRegistrationFails) {
-  ASSERT_TRUE(Install());
-  LaunchApp();
-  WaitForServerStart();
+  ASSERT_NO_FATAL_FAILURE(GetTestMethods().Install());
+  ASSERT_NO_FATAL_FAILURE(LaunchApp());
+  ASSERT_NO_FATAL_FAILURE(WaitForServerStart());
 
   test_server_.ExpectOnce(
       {CreateEventLogMatcher(
@@ -333,10 +359,10 @@
 // The application should register the device and fetch policies upon request.
 TEST_F(IntegrationTests, FetchPoliciesAndRegister) {
   SetDefaultPolicyFetchResponses();
-  StoreEnrollmentToken(kFakeEnrollmentToken);
-  ASSERT_TRUE(Install());
-  LaunchApp();
-  WaitForServerStart();
+  ASSERT_NO_FATAL_FAILURE(StoreEnrollmentToken(kFakeEnrollmentToken));
+  ASSERT_NO_FATAL_FAILURE(GetTestMethods().Install());
+  ASSERT_NO_FATAL_FAILURE(LaunchApp());
+  ASSERT_NO_FATAL_FAILURE(WaitForServerStart());
 
   test_server_.ExpectOnce(
       {CreateEventLogMatcher(
@@ -349,19 +375,19 @@
 
   EXPECT_TRUE(CreateAppFetchPolicies()->Run().ok());
 
-  ExpectDefaultPolicyValuesPersisted();
+  ASSERT_NO_FATAL_FAILURE(ExpectDefaultPolicyValuesPersisted());
 }
 
 // The application should fetch policies upon request without re-registering
 // if the device is already managed.
 TEST_F(IntegrationTests, FetchPoliciesAlreadyRegistered) {
   SetDefaultPolicyFetchResponses();
-  StoreEnrollmentToken(kFakeEnrollmentToken);
-  StoreDMToken(policy::kFakeDeviceToken);
+  ASSERT_NO_FATAL_FAILURE(StoreEnrollmentToken(kFakeEnrollmentToken));
+  ASSERT_NO_FATAL_FAILURE(StoreDMToken(policy::kFakeDeviceToken));
   RegisterClientWithDMServer();
-  ASSERT_TRUE(Install());
-  LaunchApp();
-  WaitForServerStart();
+  ASSERT_NO_FATAL_FAILURE(GetTestMethods().Install());
+  ASSERT_NO_FATAL_FAILURE(LaunchApp());
+  ASSERT_NO_FATAL_FAILURE(WaitForServerStart());
 
   test_server_.ExpectOnce(
       {CreateEventLogMatcher(
@@ -371,18 +397,18 @@
 
   EXPECT_TRUE(CreateAppFetchPolicies()->Run().ok());
 
-  ExpectDefaultPolicyValuesPersisted();
+  ASSERT_NO_FATAL_FAILURE(ExpectDefaultPolicyValuesPersisted());
 }
 
 // The application should invalidate the stored DM token if the server
 // indicates that the device is unknown.
 TEST_F(IntegrationTests, UnknownDMTokenInvalidated) {
   SetDefaultPolicyFetchResponses();
-  StoreEnrollmentToken(kFakeEnrollmentToken);
-  StoreDMToken(policy::kFakeDeviceToken);
-  ASSERT_TRUE(Install());
-  LaunchApp();
-  WaitForServerStart();
+  ASSERT_NO_FATAL_FAILURE(StoreEnrollmentToken(kFakeEnrollmentToken));
+  ASSERT_NO_FATAL_FAILURE(StoreDMToken(policy::kFakeDeviceToken));
+  ASSERT_NO_FATAL_FAILURE(GetTestMethods().Install());
+  ASSERT_NO_FATAL_FAILURE(LaunchApp());
+  ASSERT_NO_FATAL_FAILURE(WaitForServerStart());
 
   test_server_.ExpectOnce(
       {CreateEventLogMatcher(
@@ -409,12 +435,13 @@
 // registration attempt.
 TEST_F(IntegrationTests, ReloadsTokens) {
   SetDefaultPolicyFetchResponses();
-  ASSERT_TRUE(Install());
-  LaunchApp();
-  WaitForServerStart();
+  ASSERT_NO_FATAL_FAILURE(GetTestMethods().Install());
+  ASSERT_NO_FATAL_FAILURE(LaunchApp());
+  ASSERT_NO_FATAL_FAILURE(WaitForServerStart());
 
   // Attempt a registration with the invalid enrollment token, it should fail.
-  StoreEnrollmentToken(policy::kInvalidEnrollmentToken);
+  ASSERT_NO_FATAL_FAILURE(
+      StoreEnrollmentToken(policy::kInvalidEnrollmentToken));
   test_server_.ExpectOnce(
       {CreateEventLogMatcher(
           test_server_,
@@ -427,7 +454,7 @@
 
   // Change the enrollment token externally and attempt enrollment again, it
   // should succeed.
-  StoreEnrollmentToken(kFakeEnrollmentToken);
+  ASSERT_NO_FATAL_FAILURE(StoreEnrollmentToken(kFakeEnrollmentToken));
   test_server_.ExpectOnce(
       {CreateEventLogMatcher(
           test_server_,
@@ -438,7 +465,7 @@
       CreateLogResponse());
   EXPECT_TRUE(CreateAppFetchPolicies()->Run().ok());
 
-  ExpectDefaultPolicyValuesPersisted();
+  ASSERT_NO_FATAL_FAILURE(ExpectDefaultPolicyValuesPersisted());
 }
 
 }  // namespace enterprise_companion
diff --git a/chrome/enterprise_companion/test/run_all_integration_tests.cc b/chrome/enterprise_companion/test/run_all_integration_tests.cc
index 433efcf..309cbb0d 100644
--- a/chrome/enterprise_companion/test/run_all_integration_tests.cc
+++ b/chrome/enterprise_companion/test/run_all_integration_tests.cc
@@ -9,6 +9,7 @@
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
 #include "build/build_config.h"
+#include "chrome/enterprise_companion/enterprise_companion.h"
 #include "chrome/enterprise_companion/enterprise_companion_branding.h"
 
 #if BUILDFLAG(IS_POSIX)
@@ -38,6 +39,14 @@
 }  // namespace
 
 int main(int argc, char* argv[]) {
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(enterprise_companion::kLoggingModuleSwitch)) {
+    command_line->AppendSwitchASCII(
+        enterprise_companion::kLoggingModuleSwitch,
+        enterprise_companion::kLoggingModuleSwitchValue);
+  }
+
   logging::LoggingSettings settings{.logging_dest = logging::LOG_TO_STDERR};
   std::optional<base::FilePath> log_file_path = GetLogFilePath();
   if (log_file_path) {
diff --git a/chrome/enterprise_companion/test/test_utils.cc b/chrome/enterprise_companion/test/test_utils.cc
index 692b0ed..d55e201 100644
--- a/chrome/enterprise_companion/test/test_utils.cc
+++ b/chrome/enterprise_companion/test/test_utils.cc
@@ -6,11 +6,14 @@
 
 #include <optional>
 
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/function_ref.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
 #include "base/process/process.h"
 #include "base/run_loop.h"
 #include "base/task/task_traits.h"
@@ -22,10 +25,21 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "chrome/enterprise_companion/device_management_storage/dm_storage.h"
+#include "chrome/enterprise_companion/enterprise_companion.h"
 #include "chrome/enterprise_companion/installer_paths.h"
 
 namespace enterprise_companion {
 
+namespace {
+
+#if BUILDFLAG(IS_POSIX)
+constexpr char kTestExe[] = "enterprise_companion_test";
+#elif BUILDFLAG(IS_WIN)
+constexpr char kTestExe[] = "enterprise_companion_test.exe";
+#endif
+
+}  // namespace
+
 int WaitForProcess(base::Process& process) {
   int exit_code = 0;
   bool process_exited = false;
@@ -97,4 +111,15 @@
   ASSERT_TRUE(base::PathExists(install_dir->AppendASCII(kExecutableName)));
 }
 
+void TestMethods::Install() {
+  const base::FilePath test_exe_path =
+      base::PathService::CheckedGet(base::DIR_EXE).AppendASCII(kTestExe);
+  ASSERT_TRUE(base::PathExists(test_exe_path));
+
+  base::CommandLine command_line(test_exe_path);
+  command_line.AppendSwitch(kInstallSwitch);
+  base::Process installer_process = base::LaunchProcess(command_line, {});
+  ASSERT_EQ(WaitForProcess(installer_process), 0);
+}
+
 }  // namespace enterprise_companion
diff --git a/chrome/enterprise_companion/test/test_utils.h b/chrome/enterprise_companion/test/test_utils.h
index 44ecb21..a0967a7 100644
--- a/chrome/enterprise_companion/test/test_utils.h
+++ b/chrome/enterprise_companion/test/test_utils.h
@@ -48,6 +48,9 @@
 
   // Asserts that the application has been installed.
   virtual void ExpectInstalled();
+
+  // Installs the application under test via the bundled installer.
+  virtual void Install();
 };
 
 TestMethods& GetTestMethods();
diff --git a/chrome/enterprise_companion/test/test_utils_mac.cc b/chrome/enterprise_companion/test/test_utils_mac.cc
index 5ba2652a..2cdaf4c 100644
--- a/chrome/enterprise_companion/test/test_utils_mac.cc
+++ b/chrome/enterprise_companion/test/test_utils_mac.cc
@@ -45,6 +45,11 @@
                             base::FILE_PERMISSION_READ_BY_OTHERS |
                             base::FILE_PERMISSION_EXECUTE_BY_OTHERS);
   }
+
+  void Install() override {
+    InstallFakeKSAdmin(/*should_succeed=*/true);
+    TestMethods::Install();
+  }
 };
 
 }  // namespace
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index 420eb58c..7bd6a77 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -1938,6 +1938,7 @@
   CheckTextFieldsDOMState("user1", false, std::string(), false);
 
   // Only password field should be autocompleted.
+  ASSERT_TRUE(SimulateElementClick("password"));
   password_autofill_agent_->PreviewSuggestion(
       password_element_, kAliceUsername16, kAlicePassword16);
   CheckTextFieldsSuggestedState(std::string(), false, kAlicePassword, true);
@@ -3253,6 +3254,161 @@
   CheckFirstFillingResult(FillingResult::kFoundNoPasswordForUsername);
 }
 
+// Tests the standard behavior of the filling a suggestions by passing the IDs
+// of the elements to be filled along with the values to fill.
+TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById) {
+  ASSERT_TRUE(SimulateElementClick(kUsernameName));
+
+  password_autofill_agent_->PreviewPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_),
+      form_util::GetFieldRendererId(password_element_), kAliceUsername16,
+      kAlicePassword16);
+
+  EXPECT_EQ(username_element_.SuggestedValue().Utf16(), kAliceUsername16);
+  EXPECT_TRUE(username_element_.IsPreviewed());
+  EXPECT_EQ(password_element_.SuggestedValue().Utf16(), kAlicePassword16);
+  EXPECT_TRUE(password_element_.IsPreviewed());
+
+  password_autofill_agent_->FillPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_),
+      form_util::GetFieldRendererId(password_element_), kAliceUsername16,
+      kAlicePassword16);
+
+  EXPECT_EQ(username_element_.SelectionStart(), 5u);
+  EXPECT_EQ(username_element_.SelectionEnd(), 5u);
+  EXPECT_EQ(username_element_.Value().Utf16(), kAliceUsername16);
+  EXPECT_TRUE(username_element_.IsAutofilled());
+  EXPECT_EQ(password_element_.Value().Utf16(), kAlicePassword16);
+  EXPECT_TRUE(password_element_.IsAutofilled());
+}
+
+// Tests the behavior of FillPasswordSuggestionById when the elements to be
+// filled are read-only.
+TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_ReadOnlyElements) {
+  ASSERT_TRUE(SimulateElementClick(kUsernameName));
+  SetElementReadOnly(username_element_, true);
+  SetElementReadOnly(password_element_, true);
+
+  password_autofill_agent_->PreviewPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_),
+      form_util::GetFieldRendererId(password_element_), kAliceUsername16,
+      kAlicePassword16);
+
+  EXPECT_EQ(username_element_.SuggestedValue().Utf16(), u"");
+  EXPECT_FALSE(username_element_.IsPreviewed());
+  EXPECT_EQ(password_element_.SuggestedValue().Utf16(), u"");
+  EXPECT_FALSE(password_element_.IsPreviewed());
+
+  password_autofill_agent_->FillPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_),
+      form_util::GetFieldRendererId(password_element_), kAliceUsername16,
+      kAlicePassword16);
+
+  EXPECT_EQ(username_element_.Value().Utf16(), u"");
+  EXPECT_FALSE(username_element_.IsAutofilled());
+  EXPECT_EQ(password_element_.Value().Utf16(), u"");
+  EXPECT_FALSE(password_element_.IsAutofilled());
+}
+
+// Tests the behavior of FillPasswordSuggestionById when no username value is
+// present in the passed credentials.
+TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_NoUsernameValue) {
+  username_element_.SetValue(WebString(kBobUsername16));
+  ASSERT_TRUE(SimulateElementClick(kPasswordName));
+
+  password_autofill_agent_->PreviewPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_),
+      form_util::GetFieldRendererId(password_element_), u"", kAlicePassword16);
+
+  EXPECT_EQ(username_element_.SuggestedValue().Utf16(), u"");
+  EXPECT_FALSE(username_element_.IsPreviewed());
+  EXPECT_EQ(password_element_.SuggestedValue().Utf16(), kAlicePassword16);
+  EXPECT_TRUE(password_element_.IsPreviewed());
+
+  password_autofill_agent_->FillPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_),
+      form_util::GetFieldRendererId(password_element_), u"", kAlicePassword16);
+
+  EXPECT_EQ(username_element_.Value().Utf16(), kBobUsername16);
+  EXPECT_FALSE(username_element_.IsAutofilled());
+  EXPECT_EQ(password_element_.Value().Utf16(), kAlicePassword16);
+  EXPECT_TRUE(password_element_.IsAutofilled());
+}
+
+// Tests the behavior of FillPasswordSuggestionById when the form contains no
+// username element.
+TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_NoUsername) {
+  LoadHTML(kVisibleFormWithNoUsernameHTML);
+  password_element_ = GetInputElementByID(kPasswordName);
+  ASSERT_TRUE(SimulateElementClick(kPasswordName));
+
+  password_autofill_agent_->PreviewPasswordSuggestionById(
+      FieldRendererId(), form_util::GetFieldRendererId(password_element_),
+      kAliceUsername16, kAlicePassword16);
+
+  EXPECT_EQ(password_element_.SuggestedValue().Utf16(), kAlicePassword16);
+  EXPECT_TRUE(password_element_.Value().IsEmpty());
+  EXPECT_TRUE(password_element_.IsPreviewed());
+
+  password_autofill_agent_->FillPasswordSuggestionById(
+      FieldRendererId(), form_util::GetFieldRendererId(password_element_),
+      kAliceUsername16, kAlicePassword16);
+
+  EXPECT_TRUE(password_element_.SuggestedValue().IsEmpty());
+  EXPECT_EQ(password_element_.Value().Utf16(), kAlicePassword16);
+  EXPECT_TRUE(password_element_.IsAutofilled());
+}
+
+// Tests the behavior of FillPasswordSuggestionById when the form contains no
+// password element.
+TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_NoPassword) {
+  LoadHTML(kSingleUsernameFormHTML);
+  username_element_ = GetInputElementByID(kUsernameName);
+  ASSERT_TRUE(SimulateElementClick(kUsernameName));
+
+  password_autofill_agent_->PreviewPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_), FieldRendererId(),
+      kAliceUsername16, kAlicePassword16);
+
+  EXPECT_EQ(username_element_.SuggestedValue().Utf16(), kAliceUsername16);
+  EXPECT_TRUE(username_element_.Value().IsEmpty());
+  EXPECT_TRUE(username_element_.IsPreviewed());
+
+  password_autofill_agent_->FillPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_), FieldRendererId(),
+      kAliceUsername16, kAlicePassword16);
+
+  EXPECT_TRUE(username_element_.SuggestedValue().IsEmpty());
+  EXPECT_EQ(username_element_.Value().Utf16(), kAliceUsername16);
+  EXPECT_TRUE(username_element_.IsAutofilled());
+}
+
+// Tests the behavior of FillPasswordSuggestionById when neither of the passed
+// elements are focused.
+TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_NoFocusedElement) {
+  ASSERT_TRUE(SimulateElementClick("random_field"));
+
+  password_autofill_agent_->PreviewPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_),
+      form_util::GetFieldRendererId(password_element_), kAliceUsername16,
+      kAlicePassword16);
+
+  EXPECT_EQ(username_element_.SuggestedValue().Utf16(), u"");
+  EXPECT_FALSE(username_element_.IsPreviewed());
+  EXPECT_EQ(password_element_.SuggestedValue().Utf16(), u"");
+  EXPECT_FALSE(password_element_.IsPreviewed());
+
+  password_autofill_agent_->FillPasswordSuggestionById(
+      form_util::GetFieldRendererId(username_element_),
+      form_util::GetFieldRendererId(password_element_), kAliceUsername16,
+      kAlicePassword16);
+
+  EXPECT_EQ(username_element_.Value().Utf16(), u"");
+  EXPECT_FALSE(username_element_.IsAutofilled());
+  EXPECT_EQ(password_element_.Value().Utf16(), u"");
+  EXPECT_FALSE(password_element_.IsAutofilled());
+}
+
 TEST_F(PasswordAutofillAgentTest, ShowPopupOnEmptyPasswordField) {
   // Load a form with no username and update test data.
   LoadHTML(kVisibleFormWithNoUsernameHTML);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1984b74..c864dc0 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1300,13 +1300,23 @@
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
   sources = [
+    "../browser/banners/app_banner_manager_browsertest_base.cc",
+    "../browser/banners/app_banner_manager_browsertest_base.h",
+    "../browser/browsing_data/browsing_data_remover_browsertest_base.cc",
+    "../browser/browsing_data/browsing_data_remover_browsertest_base.h",
     "../browser/metrics/metrics_log_browsertest.cc",
     "../browser/metrics/sampled_out_client_id_saved_browsertest.cc",
     "../browser/metrics/startup_metrics_browsertest.cc",
     "../browser/net/cert_verifier_service_browsertest.cc",
     "../browser/net/cert_verify_proc_browsertest.cc",
     "../browser/policy/policy_prefs_browsertest.cc",
+    "../browser/preloading/prefetch/search_prefetch/search_preload_test_response_utils.cc",
+    "../browser/preloading/prefetch/search_prefetch/search_preload_test_response_utils.h",
+    "../browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc",
+    "../browser/safe_browsing/client_side_detection_service_browsertest.cc",
+    "../browser/segmentation_platform/service_browsertest.cc",
     "../browser/share/qr_code_generator_pixeltest.cc",
+    "../browser/ssl/crlset_browsertest.cc",
     "../browser/storage/shared_storage_browsertest.cc",
     "../browser/tpcd/support/origin_trial_service_browsertest.cc",
     "../browser/tpcd/support/tpcd_support_browsertest.cc",
@@ -1323,29 +1333,36 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/policy:test_support",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/search_engines",
+    "//chrome/browser/segmentation_platform:test_utils",
     "//chrome/browser/ui",
     "//chrome/common:buildflags",
     "//chrome/common:constants",
     "//chrome/common:non_code_constants",
     "//chrome/test:test_support",
+    "//components/browsing_data/content:test_support",
     "//components/content_settings/core/browser",
     "//components/content_settings/core/browser:cookie_settings",
     "//components/content_settings/core/common",
     "//components/content_settings/core/common:features",
     "//components/enterprise:test_support",
     "//components/flags_ui:switches",
+    "//components/invalidation:test_support",
+    "//components/invalidation/impl:test_support",
     "//components/metrics",
     "//components/metrics:content",
     "//components/metrics:metrics_pref_names",
     "//components/metrics:test_support",
     "//components/metrics_services_manager",
     "//components/network_session_configurator/common",
+    "//components/optimization_guide/core:test_support",
     "//components/policy:generated",
     "//components/policy:policy_code_generate",
     "//components/policy/core/browser",
     "//components/policy/core/browser:pref_mapping_test_support",
     "//components/policy/core/common",
     "//components/policy/core/common:test_support",
+    "//components/policy/test_support",
     "//components/prefs",
     "//components/privacy_sandbox:features",
     "//components/privacy_sandbox:privacy_sandbox_prefs",
@@ -1354,6 +1371,13 @@
     "//components/privacy_sandbox/privacy_sandbox_attestations",
     "//components/privacy_sandbox/privacy_sandbox_attestations:test_support",
     "//components/qr_code_generator:bitmap_generator",
+    "//components/safe_browsing/content/browser:client_side_detection_service",
+    "//components/safe_browsing/content/common:interfaces",
+    "//components/safe_browsing/core/common:features",
+    "//components/safe_browsing/core/common/proto:client_model_proto",
+    "//components/segmentation_platform/embedder/default_model",
+    "//components/segmentation_platform/internal:internal",
+    "//components/segmentation_platform/internal:test_support",
     "//components/services/storage",
     "//components/startup_metric_utils",
     "//components/user_prefs",
@@ -1375,10 +1399,27 @@
     "//url",
   ]
 
+  public_deps = [ "//components/browsing_data/content" ]
+
+  if (enable_allocation_trace_recorder_integration_tests) {
+    sources += [ "../browser/allocation_recorder_browsertest.cc" ]
+    deps += [ "//components/allocation_recorder/testing" ]
+
+    if (is_mac) {
+      deps += [ "//chrome/app_shim" ]
+    }
+  }
+
   if (is_android) {
     deps += [ "//chrome/test:test_support_ui_android" ]
   } else {
     deps += [ "//chrome/test:test_support_ui" ]
+    public_deps +=
+        [ "//chrome/browser/web_applications:web_applications_test_support" ]
+  }
+
+  if (!is_chromeos_ash) {
+    sources += [ "../browser/enterprise/remote_commands/user_remote_commands_service_browsertest.cc" ]
   }
 
   if (enable_extensions) {
@@ -1524,13 +1565,8 @@
       "../browser/android/webview_renderer_extension_browsertest.cc",
       "../browser/banners/android/ambient_badge_manager_browsertest.cc",
       "../browser/banners/app_banner_manager_browsertest.cc",
-      "../browser/banners/app_banner_manager_browsertest_base.cc",
-      "../browser/banners/app_banner_manager_browsertest_base.h",
-      "../browser/browsing_data/browsing_data_remover_browsertest_base.cc",
-      "../browser/browsing_data/browsing_data_remover_browsertest_base.h",
       "../browser/browsing_data/chrome_browsing_data_lifetime_manager_browsertest.cc",
       "../browser/engagement/important_sites_util_browsertest.cc",
-      "../browser/enterprise/remote_commands/user_remote_commands_service_browsertest.cc",
       "../browser/enterprise/reporting/cloud_profile_reporting_browsertest.cc",
       "../browser/fast_checkout/fast_checkout_tab_helper_browsertest.cc",
       "../browser/feed/rss_links_fetcher_browsertest.cc",
@@ -1541,14 +1577,8 @@
       "../browser/password_manager/android/password_manager_android_browsertest.cc",
       "../browser/password_manager/passwords_navigation_observer.cc",
       "../browser/password_manager/passwords_navigation_observer.h",
-      "../browser/preloading/prefetch/search_prefetch/search_preload_test_response_utils.cc",
-      "../browser/preloading/prefetch/search_prefetch/search_preload_test_response_utils.h",
-      "../browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc",
       "../browser/profiles/profile_browsertest_android.cc",
-      "../browser/safe_browsing/client_side_detection_service_browsertest.cc",
-      "../browser/segmentation_platform/service_browsertest.cc",
       "../browser/ssl/chrome_security_state_client_browsertest.cc",
-      "../browser/ssl/crlset_browsertest.cc",
       "../browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc",
       "../browser/ui/android/autofill/save_update_address_profile_flow_manager_browsertest.cc",
       "../browser/ui/android/hats/survey_client_android_browsertest.cc",
@@ -1681,11 +1711,6 @@
       ]
     }
 
-    if (enable_allocation_trace_recorder_integration_tests) {
-      sources += [ "../browser/allocation_recorder_browsertest.cc" ]
-      deps += [ "//components/allocation_recorder/testing" ]
-    }
-
     if (enable_desktop_android_extensions) {
       sources += [ "../browser/extensions/desktop_android/desktop_android_extensions_browsertest.cc" ]
     }
@@ -2570,16 +2595,12 @@
       "../browser/background_sync/background_sync_content_setting_browsertest.cc",
       "../browser/background_sync/background_sync_metrics_browsertest.cc",
       "../browser/banners/app_banner_manager_browsertest.cc",
-      "../browser/banners/app_banner_manager_browsertest_base.cc",
-      "../browser/banners/app_banner_manager_browsertest_base.h",
       "../browser/banners/app_banner_manager_desktop_browsertest.cc",
       "../browser/battery/battery_metrics_browsertest.cc",
       "../browser/bluetooth/web_bluetooth_browsertest.cc",
       "../browser/browser_encoding_browsertest.cc",
       "../browser/browsing_data/browsing_data_model_browsertest.cc",
       "../browser/browsing_data/browsing_data_remover_browsertest.cc",
-      "../browser/browsing_data/browsing_data_remover_browsertest_base.cc",
-      "../browser/browsing_data/browsing_data_remover_browsertest_base.h",
       "../browser/browsing_data/chrome_browsing_data_lifetime_manager_browsertest.cc",
       "../browser/browsing_data/counters/autofill_counter_browsertest.cc",
       "../browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc",
@@ -2876,9 +2897,6 @@
       "../browser/preloading/prefetch/search_prefetch/search_prefetch_browser_test_base.cc",
       "../browser/preloading/prefetch/search_prefetch/search_prefetch_browser_test_base.h",
       "../browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc",
-      "../browser/preloading/prefetch/search_prefetch/search_preload_test_response_utils.cc",
-      "../browser/preloading/prefetch/search_prefetch/search_preload_test_response_utils.h",
-      "../browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc",
       "../browser/preloading/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper_browsertest.cc",
       "../browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_browsertest.cc",
       "../browser/privacy_sandbox/privacy_sandbox_countries_browsertest.cc",
@@ -2910,7 +2928,6 @@
       "../browser/resource_coordinator/discard_before_unload_helper_browsertest.cc",
       "../browser/resource_coordinator/tab_helper_browsertest.cc",
       "../browser/resource_coordinator/tab_manager_browsertest.cc",
-      "../browser/safe_browsing/client_side_detection_service_browsertest.cc",
       "../browser/safe_browsing/download_protection/download_protection_service_browsertest.cc",
       "../browser/safe_xml_parser_browsertest.cc",
       "../browser/screen_ai/optical_character_recognizer_browsertest.cc",
@@ -2920,7 +2937,6 @@
       "../browser/search_engines/template_url_scraper_browsertest.cc",
       "../browser/search_engines/template_url_service_browsertest.cc",
       "../browser/secure_origin_allowlist_browsertest.cc",
-      "../browser/segmentation_platform/service_browsertest.cc",
       "../browser/serial/chrome_serial_browsertest.cc",
       "../browser/sessions/better_session_restore_browsertest.cc",
       "../browser/sessions/exit_type_service_browsertest.cc",
@@ -2950,7 +2966,6 @@
       "../browser/spellchecker/spellcheck_service_browsertest.cc",
       "../browser/ssl/certificate_transparency_browsertest.cc",
       "../browser/ssl/connection_help_tab_helper_browsertest.cc",
-      "../browser/ssl/crlset_browsertest.cc",
       "../browser/ssl/https_upgrades_browsertest.cc",
       "../browser/ssl/known_interception_disclosure_infobar_browsertest.cc",
       "../browser/ssl/known_interception_disclosure_ui_browsertest.cc",
@@ -3326,11 +3341,6 @@
       data_deps += [ "//third_party/screen-ai:screen_ai" ]
     }
 
-    if (enable_allocation_trace_recorder_integration_tests) {
-      sources += [ "../browser/allocation_recorder_browsertest.cc" ]
-      deps += [ "//components/allocation_recorder/testing" ]
-    }
-
     if (!is_chromeos_lacros) {
       sources += [
         # Lacros does not seem to have any actual WebView-based UI to test.
@@ -3695,7 +3705,6 @@
     if (!is_chromeos_ash) {
       sources += [
         "../browser/chrome_multiprofile_startup_browsertest.cc",
-        "../browser/enterprise/remote_commands/user_remote_commands_service_browsertest.cc",
         "../browser/enterprise/reporting/cloud_profile_reporting_browsertest.cc",
         "../browser/profiles/avatar_menu_browsertest.cc",
         "../browser/profiles/delete_profile_helper_browsertest.cc",
@@ -4438,9 +4447,6 @@
     }
     if (toolkit_views) {
       sources += [
-        "../browser/ui/commerce/mock_commerce_ui_tab_helper.cc",
-        "../browser/ui/commerce/mock_commerce_ui_tab_helper.h",
-        "../browser/ui/commerce/product_specifications_entry_point_controller_browsertest.cc",
         "../browser/ui/file_system_access/file_system_access_dangerous_file_dialog_browsertest.cc",
         "../browser/ui/file_system_access/file_system_access_permission_dialog_browsertest.cc",
         "../browser/ui/file_system_access/file_system_access_restricted_directory_dialog_browsertest.cc",
@@ -4640,6 +4646,9 @@
         "//chrome/browser/lens/core/mojom:mojo_bindings",
         "//chrome/browser/ui/actions:actions",
         "//chrome/browser/ui/actions:actions_headers",
+        "//chrome/browser/ui/commerce",
+        "//chrome/browser/ui/commerce:browser_tests",
+        "//chrome/browser/ui/commerce:test_support",
         "//chrome/browser/ui/find_bar",
         "//chrome/browser/ui/lens",
         "//chrome/browser/ui/lens:browser_tests",
@@ -4748,9 +4757,6 @@
         "../browser/ui/views/tabs/tab_scrubber_chromeos_browsertest.cc",
         "../browser/ui/views/web_apps/web_app_ash_interactive_ui_test.cc",
         "../browser/ui/webui/nearby_share/nearby_share_dialog_ui_browsertest.cc",
-        "../browser/ui/webui/signin/ash/inline_login_dialog_browsertest.cc",
-        "../browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc",
-        "../browser/ui/webui/signin/ash/signin_helper_browsertest.cc",
         "../browser/ui/window_sizer/window_sizer_chromeos_uitest.cc",
         "../browser/web_applications/app_service/web_app_publisher_helper_browsertest.cc",
         "../browser/web_applications/app_service/web_apps_browsertest.cc",
@@ -4988,6 +4994,7 @@
         "//chrome/browser/ui/webui/ash/settings/pages/files/mojom",
         "//chrome/browser/ui/webui/ash/settings/test_support",
         "//chrome/browser/ui/webui/ash/skyvault",
+        "//chrome/browser/ui/webui/signin/ash:browser_tests",
         "//chrome/services/file_util/public/cpp:browser_tests",
         "//chrome/services/keymaster/public/mojom",
         "//chrome/services/keymint/public/mojom",
@@ -6676,7 +6683,6 @@
     "//chrome/browser/ui/autofill/payments",
     "//chrome/browser/ui/autofill/payments:unit_tests",
     "//chrome/browser/ui/blocked_content:unit_tests",
-    "//chrome/browser/ui/browser_window",
     "//chrome/browser/ui/color:color_headers",
     "//chrome/browser/ui/find_bar:unit_tests",
     "//chrome/browser/updates/announcement_notification:unit_tests",
@@ -7821,10 +7827,6 @@
       "../browser/ui/color/chrome_color_provider_utils_unittest.cc",
       "../browser/ui/color/material_new_tab_page_color_mixer_unittest.cc",
       "../browser/ui/color/new_tab_page_color_mixer_unittest.cc",
-      "../browser/ui/commerce/commerce_ui_tab_helper_unittest.cc",
-      "../browser/ui/commerce/discounts_page_action_controller_unittest.cc",
-      "../browser/ui/commerce/price_tracking_page_action_controller_unittest.cc",
-      "../browser/ui/commerce/product_specifications_page_action_controller_unittest.cc",
       "../browser/ui/content_settings/content_setting_bubble_model_unittest.cc",
       "../browser/ui/content_settings/content_setting_image_model_unittest.cc",
       "../browser/ui/exclusive_access/exclusive_access_bubble_unittest.cc",
@@ -8271,8 +8273,10 @@
       "//chrome/browser/ui/actions:unit_tests",
       "//chrome/browser/ui/apps",
       "//chrome/browser/ui/blocked_content",
+      "//chrome/browser/ui/browser_window",
       "//chrome/browser/ui/color:color_headers",
       "//chrome/browser/ui/color:mixers",
+      "//chrome/browser/ui/commerce:unit_tests",
       "//chrome/browser/ui/cookie_controls:unit_tests",
       "//chrome/browser/ui/exclusive_access",
       "//chrome/browser/ui/lens:unit_tests",
@@ -8546,7 +8550,6 @@
       "../browser/ui/views/mahi/mahi_menu_controller_unittest.cc",
       "../browser/ui/views/mahi/mahi_menu_view_unittest.cc",
       "../browser/ui/webui/help/version_updater_chromeos_unittest.cc",
-      "../browser/ui/webui/signin/ash/user_cloud_signin_restriction_policy_fetcher_unittest.cc",
       "../browser/ui/window_sizer/window_sizer_chromeos_unittest.cc",
       "../browser/upgrade_detector/installed_version_updater_chromeos_unittest.cc",
       "../browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc",
@@ -8711,6 +8714,7 @@
       "//chrome/browser/ui/webui/ash/settings",
       "//chrome/browser/ui/webui/ash/smb_shares",
       "//chrome/browser/ui/webui/nearby_share:mojom",
+      "//chrome/browser/ui/webui/signin/ash:unit_tests",
       "//chrome/services/pdf:pdf_progressive_searchifier_test",
       "//chrome/services/pdf:pdf_searchifier_test",
       "//chrome/services/pdf:pdf_thumbnailer_test",
@@ -10027,6 +10031,8 @@
       "//chrome/browser/ui:test_support",
       "//chrome/browser/ui/actions:actions",
       "//chrome/browser/ui/actions:actions_headers",
+      "//chrome/browser/ui/commerce",
+      "//chrome/browser/ui/commerce:test_support",
       "//chrome/browser/ui/tabs:tab_enums",
       "//chrome/browser/ui/views",
       "//components/app_constants",
@@ -10063,8 +10069,6 @@
       "../browser/enterprise/data_protection/data_protection_navigation_observer_unittest.cc",
       "../browser/enterprise/data_protection/data_protection_page_user_data_unittest.cc",
       "../browser/enterprise/watermark/watermark_view_unittest.cc",
-      "../browser/ui/commerce/mock_commerce_ui_tab_helper.cc",
-      "../browser/ui/commerce/mock_commerce_ui_tab_helper.h",
       "../browser/ui/media_router/media_route_starter_unittest.cc",
       "../browser/ui/media_router/media_router_ui_unittest.cc",
       "../browser/ui/views/accelerator_table_unittest.cc",
@@ -11052,6 +11056,7 @@
       "//chrome/browser/ui/omnibox",
       "//chrome/browser/ui/page_action:icon_type",
       "//chrome/browser/ui/toasts",
+      "//chrome/browser/ui/toasts:interactive_ui_tests",
       "//chrome/browser/ui/views/bubble",
       "//chrome/browser/ui/views/side_panel",
       "//chrome/browser/ui/views/toolbar",
@@ -11269,8 +11274,6 @@
         "../browser/ui/autofill/address_bubbles_controller_interactive_uitest.cc",
         "../browser/ui/autofill/delete_address_profile_dialog_controller_impl_interactive_uitest.cc",
         "../browser/ui/autofill/edit_address_profile_dialog_controller_impl_interactive_uitest.cc",
-        "../browser/ui/commerce/mock_commerce_ui_tab_helper.cc",
-        "../browser/ui/commerce/mock_commerce_ui_tab_helper.h",
         "../browser/ui/toolbar/app_menu_fullscreen_interactive_uitest.cc",
         "../browser/ui/toolbar/app_menu_model_interactive_uitest.cc",
         "../browser/ui/user_education/show_promo_in_page_interactive_uitest.cc",
@@ -11377,6 +11380,8 @@
         "//chrome/app/vector_icons:vector_icons",
         "//chrome/browser/image_fetcher",
         "//chrome/browser/ui/actions:actions",
+        "//chrome/browser/ui/commerce",
+        "//chrome/browser/ui/commerce:test_support",
         "//chrome/browser/ui/find_bar",
         "//chrome/browser/ui/lens",
         "//components/commerce/core:feature_list",
diff --git a/chrome/test/DEPS b/chrome/test/DEPS
index 7f8983b..caa322b 100644
--- a/chrome/test/DEPS
+++ b/chrome/test/DEPS
@@ -23,6 +23,7 @@
   "+components/contextual_search/core/browser",
   "+components/custom_handlers",
   "+components/crash/core/app",
+  "+components/data_sharing",
   "+components/device_reauth",
   "+components/domain_reliability",
   "+components/download/public/common",
diff --git a/chrome/test/base/chrome_unit_test_suite.cc b/chrome/test/base/chrome_unit_test_suite.cc
index a44e885..0ea9af3a 100644
--- a/chrome/test/base/chrome_unit_test_suite.cc
+++ b/chrome/test/base/chrome_unit_test_suite.cc
@@ -87,7 +87,7 @@
   void OnTestEnd(const testing::TestInfo& test_info) override {
     TestingBrowserProcess::TearDownAndDeleteInstance();
     // Some tests cause ChildThreadImpl to initialize a PowerMonitor.
-    base::PowerMonitor::ShutdownForTesting();
+    base::PowerMonitor::GetInstance()->ShutdownForTesting();
 #if BUILDFLAG(IS_WIN)
     // Running tests locally on Windows machines with some degree of
     // accessibility enabled can cause this flag to become implicitly set.
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 2b55336..4d09499cc 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -32,6 +32,7 @@
     "cr_components/cr_components_browsertest.cc",
     "cr_elements/cr_elements_browsertest.cc",
     "css/css_browsertest.cc",
+    "data_sharing/data_sharing_browsertest.cc",
     "downloads/downloads_browsertest.cc",
     "engagement/site_engagement_browsertest.cc",
     "feedback/feedback_browsertest.cc",
@@ -194,6 +195,7 @@
     "//components/commerce/core:feature_list",
     "//components/commerce/core:shopping_service_test_support",
     "//components/compose/core/browser:features",
+    "//components/data_sharing:test_support",
     "//components/history/core/common",
     "//components/history_clusters/core",
     "//components/history_clusters/history_clusters_internals/webui:constants",
@@ -420,6 +422,7 @@
     "cr_components/theme_color_picker:build_grdp",
     "cr_elements:build_grdp",
     "css:build_grdp",
+    "data_sharing:build_grdp",
     "discards:build_grdp",
     "downloads:build_grdp",
     "engagement:build_grdp",
@@ -473,6 +476,7 @@
     "$target_gen_dir/cr_components/theme_color_picker/resources.grdp",
     "$target_gen_dir/cr_elements/resources.grdp",
     "$target_gen_dir/css/resources.grdp",
+    "$target_gen_dir/data_sharing/resources.grdp",
     "$target_gen_dir/discards/resources.grdp",
     "$target_gen_dir/downloads/resources.grdp",
     "$target_gen_dir/engagement/resources.grdp",
diff --git a/chrome/test/data/webui/commerce/product_specifications/header_test.ts b/chrome/test/data/webui/commerce/product_specifications/header_test.ts
index bad2f99d..d6e3c1d 100644
--- a/chrome/test/data/webui/commerce/product_specifications/header_test.ts
+++ b/chrome/test/data/webui/commerce/product_specifications/header_test.ts
@@ -8,9 +8,7 @@
 import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
-
-import {$$, assertNotStyle, assertStyle} from './test_support.js';
+import {$$, eventToPromise, hasStyle, isVisible} from 'chrome://webui-test/test_util.js';
 
 suite('HeaderTest', () => {
   let header: HeaderElement;
@@ -90,16 +88,29 @@
   });
 
   test('setting `subtitle` gives the header a subtitle', async () => {
-    assertStyle($$(header, '#subtitle')!, 'display', 'none');
-    assertStyle($$(header, '#divider')!, 'display', 'none');
-    assertStyle($$(header, '#menuButton')!, 'display', 'none');
+    const subtitle = $$(header, '#subtitle');
+    assertTrue(!!subtitle);
+    assertTrue(hasStyle(subtitle, 'display', 'none'));
+    assertTrue(hasStyle(header.$.divider, 'display', 'none'));
+    assertTrue(hasStyle(header.$.menuButton, 'display', 'none'));
 
     header.subtitle = 'foo';
     await waitAfterNextRender(header);
 
-    assertEquals('foo', $$(header, '#subtitle')!.textContent!.trim());
-    assertNotStyle($$(header, '#subtitle')!, 'display', 'none');
-    assertNotStyle($$(header, '#divider')!, 'display', 'none');
-    assertNotStyle($$(header, '#menuButton')!, 'display', 'none');
+    assertEquals('foo', subtitle!.textContent!.trim());
+    assertFalse(hasStyle(subtitle, 'display', 'none'));
+    assertFalse(hasStyle(header.$.divider, 'display', 'none'));
+    assertFalse(hasStyle(header.$.menuButton, 'display', 'none'));
+  });
+
+  test('subtitle is editable on enter', async () => {
+    const subtitle = $$(header, '#subtitle');
+    assertTrue(!!subtitle);
+    subtitle.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter'}));
+    await waitAfterNextRender(header);
+
+    const input = header.shadowRoot!.querySelector<CrInputElement>('#input');
+    assertTrue(!!input);
+    assertTrue(isVisible(input));
   });
 });
diff --git a/chrome/test/data/webui/data_sharing/BUILD.gn b/chrome/test/data/webui/data_sharing/BUILD.gn
new file mode 100644
index 0000000..4959878
--- /dev/null
+++ b/chrome/test/data/webui/data_sharing/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2021 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../build_webui_tests.gni")
+
+build_webui_tests("build") {
+  is_chrome_untrusted = true
+  files = [ "mojom_conversion_utils_test.ts" ]
+
+  ts_path_mappings =
+      [ "chrome-untrusted://data-sharing/*|" +
+        rebase_path("$root_gen_dir/chrome/browser/resources/data_sharing/tsc/*",
+                    target_gen_dir) ]
+  ts_deps = [ "//chrome/browser/resources/data_sharing:build_ts" ]
+}
diff --git a/chrome/test/data/webui/data_sharing/OWNERS b/chrome/test/data/webui/data_sharing/OWNERS
new file mode 100644
index 0000000..522a7018
--- /dev/null
+++ b/chrome/test/data/webui/data_sharing/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/resources/data_sharing/OWNERS
diff --git a/chrome/test/data/webui/data_sharing/data_sharing_browsertest.cc b/chrome/test/data/webui/data_sharing/data_sharing_browsertest.cc
new file mode 100644
index 0000000..dc0d5f7
--- /dev/null
+++ b/chrome/test/data/webui/data_sharing/data_sharing_browsertest.cc
@@ -0,0 +1,24 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/web_ui_mocha_browser_test.h"
+#include "components/data_sharing/public/features.h"
+#include "content/public/test/browser_test.h"
+
+class DataSharingWebUIBrowserTest : public WebUIMochaBrowserTest {
+ protected:
+  DataSharingWebUIBrowserTest() {
+    set_test_loader_scheme(content::kChromeUIUntrustedScheme);
+    set_test_loader_host(chrome::kChromeUIUntrustedDataSharingHost);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_{
+      data_sharing::features::kDataSharingFeature};
+};
+
+IN_PROC_BROWSER_TEST_F(DataSharingWebUIBrowserTest, ConversionUtils) {
+  RunTest("data_sharing/mojom_conversion_utils_test.js", "mocha.run()");
+}
diff --git a/chrome/test/data/webui/data_sharing/mojom_conversion_utils_test.ts b/chrome/test/data/webui/data_sharing/mojom_conversion_utils_test.ts
new file mode 100644
index 0000000..0d11a79
--- /dev/null
+++ b/chrome/test/data/webui/data_sharing/mojom_conversion_utils_test.ts
@@ -0,0 +1,46 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {MemberRole} from 'chrome-untrusted://data-sharing/group_data.mojom-webui.js';
+import type {GroupData} from 'chrome-untrusted://data-sharing/group_data.mojom-webui.js';
+import {toMojomGroupData} from 'chrome-untrusted://data-sharing/mojom_conversion_utils.js';
+import {assertDeepEquals} from 'chrome-untrusted://webui-test/chai_assert.js';
+
+suite('MojomConversionUtilsTest', () => {
+  test('toMojomGroupData', () => {
+    const groupId: string = 'TEST_GROUP_ID';
+    const groupName: string = 'TEST_GROUP_NAME';
+    const avatarUrl: string = 'http://example.com';
+    const displayName: string = 'TEST_DISPLAY_NAME';
+    const gaiaId: string = 'TEST_GAIA_ID';
+    const email: string = 'test@gmail.com';
+
+    const groupData: GroupData = toMojomGroupData({
+      id: groupId,
+      name: groupName,
+      members: [{
+        profileId: gaiaId,
+        displayName: displayName,
+        displayValue: email,
+        role: 'invitee',
+        photoUrl: avatarUrl,
+      }],
+    });
+
+    const expectedGroupData: GroupData = {
+      groupId: groupId,
+      displayName: groupName,
+      accessToken: '',
+      members: [{
+        gaiaId: gaiaId,
+        displayName: displayName,
+        email: email,
+        role: MemberRole.kInvitee,
+        avatarUrl: {url: avatarUrl},
+      }],
+    };
+
+    assertDeepEquals(expectedGroupData, groupData);
+  });
+});
diff --git a/chrome/test/data/webui/tab_search/tab_organization_selector_test.ts b/chrome/test/data/webui/tab_search/tab_organization_selector_test.ts
index e145636..46e23dcb 100644
--- a/chrome/test/data/webui/tab_search/tab_organization_selector_test.ts
+++ b/chrome/test/data/webui/tab_search/tab_organization_selector_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import type {TabOrganizationSelectorElement} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {AutoTabGroupsPageElement, DeclutterPageElement, TabOrganizationSelectorElement} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {TabSearchApiProxyImpl} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
@@ -11,9 +11,12 @@
 
 suite('TabOrganizationSelectorTest', () => {
   let selector: TabOrganizationSelectorElement;
+  let noSelectionState: HTMLElement;
+  let autoTabGroupsState: AutoTabGroupsPageElement;
+  let declutterState: DeclutterPageElement;
   let testApiProxy: TestTabSearchApiProxy;
 
-  function selectorSetup() {
+  async function selectorSetup() {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
 
     testApiProxy = new TestTabSearchApiProxy();
@@ -21,21 +24,21 @@
 
     selector = document.createElement('tab-organization-selector');
     document.body.appendChild(selector);
-    return microtasksFinished();
+    await microtasksFinished();
+
+    noSelectionState = selector.shadowRoot!.querySelector('#buttonContainer')!;
+    assertTrue(!!noSelectionState);
+    autoTabGroupsState =
+        selector.shadowRoot!.querySelector('auto-tab-groups-page')!;
+    assertTrue(!!autoTabGroupsState);
+    declutterState = selector.shadowRoot!.querySelector('declutter-page')!;
+    assertTrue(!!declutterState);
   }
 
   test('Navigates to auto tab groups', async () => {
     await selectorSetup();
-    const noSelectionState =
-        selector.shadowRoot!.querySelector('#buttonContainer');
-    assertTrue(!!noSelectionState);
     assertTrue(isVisible(noSelectionState));
-    const autoTabGroupsState =
-        selector.shadowRoot!.querySelector('auto-tab-groups-page');
-    assertTrue(!!autoTabGroupsState);
     assertFalse(isVisible(autoTabGroupsState));
-    const declutterState = selector.shadowRoot!.querySelector('declutter-page');
-    assertTrue(!!declutterState);
     assertFalse(isVisible(declutterState));
 
     const autoTabGroupsButton =
@@ -51,16 +54,8 @@
 
   test('Navigates to declutter', async () => {
     await selectorSetup();
-    const noSelectionState =
-        selector.shadowRoot!.querySelector('#buttonContainer');
-    assertTrue(!!noSelectionState);
     assertTrue(isVisible(noSelectionState));
-    const autoTabGroupsState =
-        selector.shadowRoot!.querySelector('auto-tab-groups-page');
-    assertTrue(!!autoTabGroupsState);
     assertFalse(isVisible(autoTabGroupsState));
-    const declutterState = selector.shadowRoot!.querySelector('declutter-page');
-    assertTrue(!!declutterState);
     assertFalse(isVisible(declutterState));
 
     const declutterButton =
@@ -73,4 +68,27 @@
     assertFalse(isVisible(autoTabGroupsState));
     assertTrue(isVisible(declutterState));
   });
+
+  test('Declutter navigates to selector', async () => {
+    await selectorSetup();
+    const declutterButton =
+        selector.shadowRoot!.querySelector<HTMLElement>('#declutterButton');
+    assertTrue(!!declutterButton);
+    declutterButton.click();
+    await microtasksFinished();
+
+    assertFalse(isVisible(noSelectionState));
+    assertFalse(isVisible(autoTabGroupsState));
+    assertTrue(isVisible(declutterState));
+
+    const declutterBackButton =
+        declutterState.shadowRoot!.querySelector('cr-icon-button');
+    assertTrue(!!declutterBackButton);
+    declutterBackButton.click();
+    await microtasksFinished();
+
+    assertTrue(isVisible(noSelectionState));
+    assertFalse(isVisible(autoTabGroupsState));
+    assertFalse(isVisible(declutterState));
+  });
 });
diff --git a/chromecast/browser/general_audience_browsing_service.cc b/chromecast/browser/general_audience_browsing_service.cc
index 1c2b31ed..e11e440 100644
--- a/chromecast/browser/general_audience_browsing_service.cc
+++ b/chromecast/browser/general_audience_browsing_service.cc
@@ -22,7 +22,7 @@
     GeneralAudienceBrowsingService::CheckURLCallback callback,
     const GURL& /* url */,
     safe_search_api::Classification classification,
-    bool /* uncertain */) {
+    safe_search_api::ClassificationDetails details) {
   std::move(callback).Run(classification ==
                           safe_search_api::Classification::SAFE);
 }
diff --git a/chromeos/ash/components/BUILD.gn b/chromeos/ash/components/BUILD.gn
index 44fa84e4..f9ce479 100644
--- a/chromeos/ash/components/BUILD.gn
+++ b/chromeos/ash/components/BUILD.gn
@@ -13,6 +13,7 @@
   deps = [
     "//chromeos/ash/components/attestation:unit_tests",
     "//chromeos/ash/components/audio:unit_tests",
+    "//chromeos/ash/components/boca:unit_tests",
     "//chromeos/ash/components/boca/babelorca:unit_tests",
     "//chromeos/ash/components/boca/on_task:unit_tests",
     "//chromeos/ash/components/boca/session_api:unit_tests",
diff --git a/chromeos/ash/components/boca/BUILD.gn b/chromeos/ash/components/boca/BUILD.gn
index 5c8c646..c7fa0f9 100644
--- a/chromeos/ash/components/boca/BUILD.gn
+++ b/chromeos/ash/components/boca/BUILD.gn
@@ -19,5 +19,30 @@
     "//ash",
     "//ash/constants",
     "//chromeos/ash/components/boca/proto",
+    "//chromeos/ash/components/boca/session_api",
+    "//chromeos/ash/components/network",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [ "boca_session_manager_unittest.cc" ]
+
+  deps = [
+    ":boca",
+    "//ash:test_support",
+    "//base",
+    "//base/test:test_support",
+    "//chromeos/ash/components/boca/proto",
+    "//chromeos/ash/components/boca/session_api",
+    "//chromeos/ash/components/network",
+    "//components/user_manager",
+    "//components/user_manager:test_support",
+    "//content/test:test_support",
+    "//google_apis/common",
+    "//services/network:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
   ]
 }
diff --git a/chromeos/ash/components/boca/DEPS b/chromeos/ash/components/boca/DEPS
index b23cccb..1063115 100644
--- a/chromeos/ash/components/boca/DEPS
+++ b/chromeos/ash/components/boca/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
   "+ash",
-  "+ash/constants"
+  "+ash/constants",
+  "+google_apis/common",
+  "+components/user_manager",
+  "+content/public/test",
 ]
diff --git a/chromeos/ash/components/boca/boca_session_manager.cc b/chromeos/ash/components/boca/boca_session_manager.cc
index a5e7371..c6f06bb0 100644
--- a/chromeos/ash/components/boca/boca_session_manager.cc
+++ b/chromeos/ash/components/boca/boca_session_manager.cc
@@ -4,14 +4,68 @@
 
 #include "chromeos/ash/components/boca/boca_session_manager.h"
 
+#include <algorithm>
+#include <memory>
+
+#include "ash/constants/ash_constants.h"
+#include "ash/public/cpp/network_config_service.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "base/time/time.h"
 #include "chromeos/ash/components/boca/proto/bundle.pb.h"
 #include "chromeos/ash/components/boca/proto/roster.pb.h"
 #include "chromeos/ash/components/boca/proto/session.pb.h"
+#include "chromeos/ash/components/boca/session_api/constants.h"
+#include "chromeos/ash/components/boca/session_api/get_session_request.h"
+#include "chromeos/ash/components/boca/session_api/session_client_impl.h"
+#include "components/user_manager/user_manager.h"
+#include "google_apis/common/api_error_codes.h"
 
 namespace ash::boca {
+namespace {
 
-BocaSessionManager::BocaSessionManager() = default;
-BocaSessionManager::~BocaSessionManager() = default;
+::boca::SessionConfig GetSessionConfigSafe(::boca::Session* session) {
+  if (!session) {
+    return ::boca::SessionConfig();
+  }
+  if (session->student_group_configs().empty()) {
+    return ::boca::SessionConfig();
+  }
+  auto it = session->student_group_configs().find(kMainStudentGroupName);
+  if (it == session->student_group_configs().end()) {
+    return ::boca::SessionConfig();
+  }
+  return it->second;
+}
+
+google::protobuf::RepeatedPtrField<::boca::UserIdentity> GetStudentGroupsSafe(
+    ::boca::Session* session) {
+  if (!session || session->roster().student_groups().empty()) {
+    return google::protobuf::RepeatedPtrField<::boca::UserIdentity>();
+  }
+  return session->roster().student_groups()[0].students();
+}
+
+::boca::Roster GetRosterSafe(::boca::Session* session) {
+  return session ? session->roster() : ::boca::Roster();
+}
+
+}  // namespace
+
+BocaSessionManager::BocaSessionManager(AccountId account_id)
+    : BocaSessionManager(std::make_unique<SessionClientImpl>(),
+                         std::move(account_id)) {}
+BocaSessionManager::BocaSessionManager(
+    std::unique_ptr<SessionClientImpl> session_client_impl,
+    AccountId account_id)
+    : account_id_(std::move(account_id)),
+      session_client_impl_(std::move(session_client_impl)) {
+  GetNetworkConfigService(cros_network_config_.BindNewPipeAndPassReceiver());
+  cros_network_config_->AddObserver(
+      cros_network_config_observer_.BindNewPipeAndPassRemote());
+  StartSessionPolling();
+}
+BocaSessionManager::~BocaSessionManager() {}
 
 void BocaSessionManager::Observer::OnBundleUpdated(
     const ::boca::Bundle& bundle) {}
@@ -27,6 +81,17 @@
     const std::string& group_name,
     const std::vector<::boca::UserIdentity>& consumers) {}
 
+void BocaSessionManager::OnNetworkStateChanged(
+    chromeos::network_config::mojom::NetworkStatePropertiesPtr network_state) {
+  // Check network types comment here:
+  // chromeos/services/network_config/public/mojom/network_types.mojom
+  if (network_state->connection_state ==
+      chromeos::network_config::mojom::ConnectionStateType::kOnline) {
+    is_network_conntected_ = true;
+  } else {
+    is_network_conntected_ = false;
+  }
+}
 void BocaSessionManager::NotifyError(BocaError error) {}
 
 void BocaSessionManager::AddObserver(Observer* observer) {
@@ -36,4 +101,114 @@
 void BocaSessionManager::RemoveObserver(Observer* observer) {
   observers_.RemoveObserver(observer);
 }
+
+void BocaSessionManager::StartSessionPolling() {
+  if (!timer_.IsRunning()) {
+    timer_.Start(FROM_HERE, kPollingInterval, this,
+                 &BocaSessionManager::LoadCurrentSession);
+  }
+}
+
+void BocaSessionManager::LoadCurrentSession() {
+  if (!is_network_conntected_) {
+    return;
+  }
+
+  // TODO(b/361852484): We should ideally listen to user switch events. But
+  // since we'll remove polling after we have FCM, leave it as it is now.
+  if (!IsProfileActive()) {
+    return;
+  }
+  auto request = std::make_unique<GetSessionRequest>(
+      session_client_impl_->sender(), account_id_.GetGaiaId(),
+      base::BindOnce(&BocaSessionManager::ParseSessionResponse,
+                     weak_factory_.GetWeakPtr()));
+  session_client_impl_->GetSession(std::move(request));
+}
+
+void BocaSessionManager::ParseSessionResponse(
+    base::expected<std::unique_ptr<::boca::Session>, google_apis::ApiErrorCode>
+        result) {
+  if (!result.has_value()) {
+    return;
+  }
+  previous_session_ = std::move(current_session_);
+  current_session_ = std::move(result.value());
+  NotifySessionUpdate();
+  NotifyOnTaskUpdate();
+  NotifyCaptionConfigUpdate();
+  NotifyRosterUpdate();
+}
+
+bool BocaSessionManager::IsProfileActive() {
+  return user_manager::UserManager::IsInitialized() &&
+         user_manager::UserManager::Get()->GetActiveUser() &&
+         user_manager::UserManager::Get()->GetActiveUser()->GetAccountId() ==
+             account_id_;
+}
+
+void BocaSessionManager::NotifySessionUpdate() {
+  if ((!current_session_ ||
+       current_session_->session_state() != ::boca::Session::ACTIVE) &&
+      previous_session_ &&
+      previous_session_->session_state() == ::boca::Session::ACTIVE) {
+    for (auto& observer : observers_) {
+      observer.OnSessionEnded(previous_session_->session_id());
+    }
+  }
+
+  if (current_session_ &&
+      current_session_->session_state() == ::boca::Session::ACTIVE &&
+      (!previous_session_ ||
+       previous_session_->session_state() != ::boca::Session::ACTIVE)) {
+    for (auto& observer : observers_) {
+      observer.OnSessionStarted(current_session_->session_id(),
+                                current_session_->teacher());
+    }
+  }
+}
+
+void BocaSessionManager::NotifyOnTaskUpdate() {
+  auto previous_bundle = GetSessionConfigSafe(previous_session_.get())
+                             .on_task_config()
+                             .active_bundle();
+  auto current_bundle = GetSessionConfigSafe(current_session_.get())
+                            .on_task_config()
+                            .active_bundle();
+  if (previous_bundle.SerializeAsString() !=
+      current_bundle.SerializeAsString()) {
+    for (auto& observer : observers_) {
+      observer.OnBundleUpdated(current_bundle);
+    }
+  }
+}
+
+void BocaSessionManager::NotifyCaptionConfigUpdate() {
+  auto previous_session_caption_config =
+      GetSessionConfigSafe(previous_session_.get()).captions_config();
+  auto current_session_caption_config =
+      GetSessionConfigSafe(current_session_.get()).captions_config();
+  if (previous_session_caption_config.SerializeAsString() !=
+      current_session_caption_config.SerializeAsString()) {
+    for (auto& observer : observers_) {
+      observer.OnSessionCaptionConfigUpdated(kMainStudentGroupName,
+                                             current_session_caption_config);
+    }
+  }
+}
+
+void BocaSessionManager::NotifyRosterUpdate() {
+  auto previous_session_roster = GetRosterSafe(previous_session_.get());
+  auto current_session_roster = GetRosterSafe(current_session_.get());
+  if (previous_session_roster.SerializeAsString() !=
+      current_session_roster.SerializeAsString()) {
+    for (auto& observer : observers_) {
+      auto student_list = GetStudentGroupsSafe(current_session_.get());
+      observer.OnSessionRosterUpdated(
+          kMainStudentGroupName, std::vector<::boca::UserIdentity>(
+                                     student_list.begin(), student_list.end()));
+    }
+  }
+}
+
 }  // namespace ash::boca
diff --git a/chromeos/ash/components/boca/boca_session_manager.h b/chromeos/ash/components/boca/boca_session_manager.h
index ac315ff..7ba5a005 100644
--- a/chromeos/ash/components/boca/boca_session_manager.h
+++ b/chromeos/ash/components/boca/boca_session_manager.h
@@ -5,11 +5,19 @@
 #ifndef CHROMEOS_ASH_COMPONENTS_BOCA_BOCA_SESSION_MANAGER_H_
 #define CHROMEOS_ASH_COMPONENTS_BOCA_BOCA_SESSION_MANAGER_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
+#include "base/timer/timer.h"
+#include "chromeos/ash/components/boca/proto/session.pb.h"
+#include "chromeos/ash/components/boca/session_api/session_client_impl.h"
+#include "chromeos/services/network_config/public/cpp/cros_network_config_observer.h"
+#include "components/account_id/account_id.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace boca {
 class UserIdentity;
@@ -19,8 +27,12 @@
 
 namespace ash::boca {
 
-class BocaSessionManager {
+class BocaSessionManager
+    : public chromeos::network_config::CrosNetworkConfigObserver {
  public:
+  // TODO(b/361852484): Make it 5 minutes after fcm in place.
+  inline static constexpr base::TimeDelta kPollingInterval = base::Seconds(5);
+
   enum class BocaAction {
     kDefault = 0,
     kOntask = 1,
@@ -47,10 +59,13 @@
     const std::string error_message;
   };
 
-  BocaSessionManager();
+  explicit BocaSessionManager(AccountId account_id);
+  explicit BocaSessionManager(
+      std::unique_ptr<SessionClientImpl> session_client_impl,
+      AccountId account_id);
   BocaSessionManager(const BocaSessionManager&) = delete;
   BocaSessionManager& operator=(const BocaSessionManager&) = delete;
-  ~BocaSessionManager();
+  ~BocaSessionManager() override;
 
   // Interface for observing events.
   class Observer : public base::CheckedObserver {
@@ -65,7 +80,9 @@
     virtual void OnSessionEnded(const std::string& session_id) = 0;
 
     // Notifies when bundle updated. In the event of session started with a
-    // bundle configured, both events will be fired.
+    // bundle configured, both events will be fired. Will emit when only
+    // elements order changed in the vector too. Deferred to events consumer to
+    // decide on the actual action.
     virtual void OnBundleUpdated(const ::boca::Bundle& bundle);
 
     // Notifies when session config updated for specific group.
@@ -77,18 +94,48 @@
     virtual void OnLocalCaptionConfigUpdated(
         const ::boca::CaptionsConfig& config);
 
-    // Notifies when session roster updated.
+    // Notifies when session roster updated. Will emit when only elements order
+    // changed in the vector too. Deferred to events consumer to decide on
+    // the actual action.
     virtual void OnSessionRosterUpdated(
         const std::string& group_name,
         const std::vector<::boca::UserIdentity>& consumers);
   };
+  // CrosNetworkConfigObserver
+  void OnNetworkStateChanged(
+      chromeos::network_config::mojom::NetworkStatePropertiesPtr network_state)
+      override;
 
   void NotifyError(BocaError error);
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  void StartSessionPolling();
+  void LoadCurrentSession();
+  void ParseSessionResponse(base::expected<std::unique_ptr<::boca::Session>,
+                                           google_apis::ApiErrorCode> result);
+
  private:
+  bool IsProfileActive();
+  void NotifySessionUpdate();
+  void NotifyOnTaskUpdate();
+  void NotifyCaptionConfigUpdate();
+  void NotifyRosterUpdate();
+
   base::ObserverList<Observer> observers_;
+  // Timer used for periodic session polling.
+  base::RepeatingTimer timer_;
+  std::unique_ptr<::boca::Session> current_session_;
+  std::unique_ptr<::boca::Session> previous_session_;
+  bool is_network_conntected_ = false;
+  // Remote for sending requests to the CrosNetworkConfig service.
+  mojo::Remote<chromeos::network_config::mojom::CrosNetworkConfig>
+      cros_network_config_;
+  mojo::Receiver<chromeos::network_config::mojom::CrosNetworkConfigObserver>
+      cros_network_config_observer_{this};
+  AccountId account_id_;
+  std::unique_ptr<SessionClientImpl> session_client_impl_;
+  base::WeakPtrFactory<BocaSessionManager> weak_factory_{this};
 };
 }  // namespace ash::boca
 #endif  // CHROMEOS_ASH_COMPONENTS_BOCA_BOCA_SESSION_MANAGER_H_
diff --git a/chromeos/ash/components/boca/boca_session_manager_unittest.cc b/chromeos/ash/components/boca/boca_session_manager_unittest.cc
new file mode 100644
index 0000000..1d32d47
--- /dev/null
+++ b/chromeos/ash/components/boca/boca_session_manager_unittest.cc
@@ -0,0 +1,619 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/boca/boca_session_manager.h"
+
+#include <memory>
+#include <optional>
+
+#include "ash/test/ash_test_base.h"
+#include "base/types/expected.h"
+#include "chromeos/ash/components/boca/proto/session.pb.h"
+#include "chromeos/ash/components/boca/session_api/constants.h"
+#include "chromeos/ash/components/boca/session_api/get_session_request.h"
+#include "chromeos/ash/services/network_config/public/cpp/cros_network_config_test_helper.h"
+#include "components/user_manager/fake_user_manager.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "google_apis/common/api_error_codes.h"
+#include "google_apis/common/request_sender.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrictMock;
+using ::testing::WithArg;
+
+namespace ash::boca {
+
+namespace {
+class MockSessionClientImpl : public SessionClientImpl {
+ public:
+  explicit MockSessionClientImpl(
+      std::unique_ptr<google_apis::RequestSender> sender)
+      : SessionClientImpl(std::move(sender)) {}
+  MOCK_METHOD(void,
+              GetSession,
+              (std::unique_ptr<GetSessionRequest>),
+              (override));
+};
+
+class MockObserver : public BocaSessionManager::Observer {
+ public:
+  MOCK_METHOD(void,
+              OnSessionStarted,
+              (const std::string& session_id,
+               const ::boca::UserIdentity& producer),
+              (override));
+  MOCK_METHOD(void,
+              OnSessionEnded,
+              (const std::string& session_id),
+              (override));
+  MOCK_METHOD(void,
+              OnBundleUpdated,
+              (const ::boca::Bundle& bundle),
+              (override));
+  MOCK_METHOD(void,
+              OnSessionCaptionConfigUpdated,
+              (const std::string& group_name,
+               const ::boca::CaptionsConfig& config),
+              (override));
+  MOCK_METHOD(void,
+              OnLocalCaptionConfigUpdated,
+              (const ::boca::CaptionsConfig& config),
+              (override));
+  MOCK_METHOD(void,
+              OnSessionRosterUpdated,
+              (const std::string& group_name,
+               const std::vector<::boca::UserIdentity>& consumers),
+              (override));
+};
+
+constexpr char kTestGaiaId[] = "123";
+constexpr char kTestUserEmail[] = "cat@gmail.com";
+}  // namespace
+
+class BocaSessionManagerTest : public testing::Test {
+ public:
+  BocaSessionManagerTest() = default;
+  void SetUp() override {
+    // Sign in test user.
+    auto account_id =
+        AccountId::FromUserEmailGaiaId(kTestUserEmail, kTestGaiaId);
+    const std::string username_hash =
+        user_manager::FakeUserManager::GetFakeUsernameHash(account_id);
+    fake_user_manager_.Reset(std::make_unique<user_manager::FakeUserManager>());
+    fake_user_manager_->AddUser(account_id);
+    fake_user_manager_->UserLoggedIn(account_id, username_hash,
+                                     /*browser_restart=*/false,
+                                     /*is_child=*/false);
+    wifi_device_path_ =
+        cros_network_config_helper_.network_state_helper().ConfigureWiFi(
+            shill::kStateIdle);
+
+    auto session_client_impl =
+        std::make_unique<StrictMock<MockSessionClientImpl>>(nullptr);
+    session_client_impl_ = session_client_impl.get();
+
+    observer_ = std::make_unique<StrictMock<MockObserver>>();
+
+    // Start with empty session
+    EXPECT_CALL(*session_client_impl_, GetSession(_))
+        .WillOnce(testing::InvokeWithoutArgs([&]() {
+          boca_session_manager()->ParseSessionResponse(base::ok(nullptr));
+        }));
+    boca_session_manager_ = std::make_unique<BocaSessionManager>(
+        std::move(session_client_impl),
+        AccountId::FromUserEmail(kTestUserEmail));
+    boca_session_manager_->AddObserver(observer_.get());
+    ToggleOffline();
+    ToggleOnline();
+
+    // Trigger first load.
+    task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 1 +
+                                      base::Seconds(1));
+  }
+
+ protected:
+  void ToggleOnline() {
+    cros_network_config_helper_.network_state_helper().SetServiceProperty(
+        wifi_device_path_, shill::kStateProperty,
+        base::Value(shill::kStateOnline));
+  }
+
+  void ToggleOffline() {
+    cros_network_config_helper_.network_state_helper().SetServiceProperty(
+        wifi_device_path_, shill::kStateProperty,
+        base::Value(shill::kStateDisconnecting));
+  }
+
+  MockSessionClientImpl* session_client_impl() { return session_client_impl_; }
+  MockObserver* observer() { return observer_.get(); }
+  BocaSessionManager* boca_session_manager() {
+    return boca_session_manager_.get();
+  }
+  user_manager::TypedScopedUserManager<user_manager::FakeUserManager>&
+  fake_user_manager() {
+    return fake_user_manager_;
+  }
+  content::BrowserTaskEnvironment* task_environment() {
+    return &task_environment_;
+  }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  std::string wifi_device_path_;
+  network_config::CrosNetworkConfigTestHelper cros_network_config_helper_;
+  std::unique_ptr<BocaSessionManager> boca_session_manager_;
+  // Owned by BocaSessionManager, destructed before it.
+  raw_ptr<StrictMock<MockSessionClientImpl>> session_client_impl_;
+  std::unique_ptr<StrictMock<MockObserver>> observer_;
+  user_manager::TypedScopedUserManager<user_manager::FakeUserManager>
+      fake_user_manager_;
+};
+
+TEST_F(BocaSessionManagerTest, DoNothingIfSessionUpdateFailed) {
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(
+            base::unexpected<google_apis::ApiErrorCode>(
+                google_apis::ApiErrorCode::PARSE_ERROR));
+      }));
+
+  EXPECT_CALL(*observer(), OnSessionStarted(_, _)).Times(0);
+  EXPECT_CALL(*observer(), OnSessionEnded(_)).Times(0);
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 1 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, NotifySessionUpdateWhenSessionFlipBetweenEmpty) {
+  const std::string session_id = "123";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_state(::boca::Session::ACTIVE);
+  session_1->set_session_id(session_id);
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(base::ok(nullptr));
+      }));
+
+  EXPECT_CALL(*observer(), OnSessionStarted(session_id, _)).Times(1);
+  EXPECT_CALL(*observer(), OnSessionEnded(session_id)).Times(1);
+
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, NotifySessionUpdateWhenBothSessionIsEmpty) {
+  auto current_session = std::make_unique<::boca::Session>();
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(
+            std::move(current_session));
+      }));
+
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval +
+                                    base::Seconds(1));
+  EXPECT_CALL(*observer(), OnSessionEnded(_)).Times(0);
+  EXPECT_CALL(*observer(), OnSessionStarted(_, _)).Times(0);
+}
+
+TEST_F(BocaSessionManagerTest,
+       NotifySessionUpdateWhenPreviousSessionStateChanged) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_state(::boca::Session::ACTIVE);
+  session_1->set_session_id(session_id);
+  auto session_2 = std::make_unique<::boca::Session>();
+  session_2->set_session_state(::boca::Session::PLANNING);
+  session_2->set_session_id(session_id);
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_2));
+      }));
+
+  EXPECT_CALL(*observer(), OnSessionStarted(session_id, _)).Times(1);
+  EXPECT_CALL(*observer(), OnSessionEnded(session_id)).Times(1);
+
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, DoNothingWhenSessionStateIsTheSame) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_state(::boca::Session::ACTIVE);
+  session_1->set_session_id(session_id);
+  auto session_2 = std::make_unique<::boca::Session>();
+  session_2->set_session_state(::boca::Session::ACTIVE);
+  session_2->set_session_id(session_id);
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_2));
+      }));
+
+  EXPECT_CALL(*observer(), OnSessionStarted(_, _)).Times(1);
+  EXPECT_CALL(*observer(), OnSessionEnded(_)).Times(0);
+
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, NotifySessionUpdateWhenLockModeChanged) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+  ::boca::SessionConfig session_config;
+  auto* active_bundle =
+      session_config.mutable_on_task_config()->mutable_active_bundle();
+  active_bundle->set_locked(true);
+  active_bundle->mutable_content_configs()->Add()->set_url("google.com");
+  (*session_1->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config);
+
+  auto session_2 = std::make_unique<::boca::Session>();
+  session_2->set_session_id(session_id);
+  ::boca::SessionConfig session_config_2;
+  auto* active_bundle_2 =
+      session_config.mutable_on_task_config()->mutable_active_bundle();
+  active_bundle_2->set_locked(false);
+  active_bundle_2->mutable_content_configs()->Add()->set_url("google.com");
+  (*session_2->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config_2);
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_2));
+      }));
+
+  EXPECT_CALL(*observer(), OnBundleUpdated(_)).Times(2);
+
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, NotifySessionUpdateWhenBundleContentChanged) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+  ::boca::SessionConfig session_config;
+  auto* active_bundle =
+      session_config.mutable_on_task_config()->mutable_active_bundle();
+  active_bundle->set_locked(true);
+  active_bundle->mutable_content_configs()->Add()->set_url("google.com");
+  (*session_1->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config);
+
+  auto session_2 = std::make_unique<::boca::Session>();
+  session_2->set_session_id(session_id);
+  ::boca::SessionConfig session_config_2;
+  auto* active_bundle_2 =
+      session_config.mutable_on_task_config()->mutable_active_bundle();
+  active_bundle_2->set_locked(true);
+  active_bundle_2->mutable_content_configs()->Add()->set_url("youtube.com");
+  (*session_2->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config_2);
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_2));
+      }));
+
+  EXPECT_CALL(*observer(), OnBundleUpdated(_)).Times(2);
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, NotifySessionUpdateWhenBundleOrderChanged) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+  ::boca::SessionConfig session_config;
+  auto* active_bundle =
+      session_config.mutable_on_task_config()->mutable_active_bundle();
+  active_bundle->set_locked(true);
+  active_bundle->mutable_content_configs()->Add()->set_url("google.com");
+  active_bundle->mutable_content_configs()->Add()->set_url("youtube.com");
+  (*session_1->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config);
+
+  auto session_2 = std::make_unique<::boca::Session>();
+  session_2->set_session_id(session_id);
+  ::boca::SessionConfig session_config_2;
+  auto* active_bundle_2 =
+      session_config.mutable_on_task_config()->mutable_active_bundle();
+  active_bundle_2->set_locked(true);
+  active_bundle_2->mutable_content_configs()->Add()->set_url("youtube.com");
+  active_bundle_2->mutable_content_configs()->Add()->set_url("google.com");
+  (*session_2->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config_2);
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_2));
+      }));
+
+  EXPECT_CALL(*observer(), OnBundleUpdated(_)).Times(2);
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, DoNothingWhenBundledContentNoChange) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+  ::boca::SessionConfig session_config;
+  auto* active_bundle =
+      session_config.mutable_on_task_config()->mutable_active_bundle();
+  active_bundle->set_locked(true);
+  active_bundle->mutable_content_configs()->Add()->set_url("google.com");
+  (*session_1->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config);
+
+  auto session_2 = std::make_unique<::boca::Session>();
+  session_2->set_session_id(session_id);
+  ::boca::SessionConfig session_config_2;
+  auto* active_bundle_2 =
+      session_config_2.mutable_on_task_config()->mutable_active_bundle();
+  active_bundle_2->set_locked(true);
+  active_bundle_2->mutable_content_configs()->Add()->set_url("google.com");
+  (*session_2->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config_2);
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_2));
+      }));
+
+  // On emit once when flip from initial empty state.
+  EXPECT_CALL(*observer(), OnBundleUpdated(_)).Times(1);
+
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, NotifySessionUpdateWhenCurrentBundleEmpty) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }));
+
+  EXPECT_CALL(*observer(), OnBundleUpdated(_)).Times(0);
+
+  // Have updated one session.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 1 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, NotifySessionUpdateWhenSessionCaptionUpdated) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+  ::boca::SessionConfig session_config;
+  auto* caption_config_1 = session_config.mutable_captions_config();
+
+  caption_config_1->set_captions_enabled(true);
+  caption_config_1->set_translations_enabled(true);
+  (*session_1->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config);
+
+  auto session_2 = std::make_unique<::boca::Session>();
+  session_2->set_session_id(session_id);
+  ::boca::SessionConfig session_config_2;
+  auto* caption_config_2 = session_config.mutable_captions_config();
+
+  caption_config_2->set_captions_enabled(false);
+  caption_config_2->set_translations_enabled(false);
+  (*session_2->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config_2);
+
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_2));
+      }));
+
+  EXPECT_CALL(*observer(),
+              OnSessionCaptionConfigUpdated(kMainStudentGroupName, _))
+      .Times(2);
+
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, DoNothingWhenSessionCaptionSame) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+  ::boca::SessionConfig session_config;
+  auto* caption_config_1 = session_config.mutable_captions_config();
+
+  caption_config_1->set_captions_enabled(false);
+  caption_config_1->set_translations_enabled(false);
+  (*session_1->mutable_student_group_configs())[kMainStudentGroupName] =
+      std::move(session_config);
+
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }));
+
+  EXPECT_CALL(*observer(),
+              OnSessionCaptionConfigUpdated(kMainStudentGroupName, _))
+      .Times(0);
+
+  // Have updated one session.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 1 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, DoNothingWhenSessionConfigNotMatch) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+  ::boca::SessionConfig session_config;
+  auto* caption_config_1 = session_config.mutable_captions_config();
+
+  caption_config_1->set_captions_enabled(false);
+  caption_config_1->set_translations_enabled(false);
+  (*session_1->mutable_student_group_configs())["unknown"] =
+      std::move(session_config);
+
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }));
+
+  EXPECT_CALL(*observer(),
+              OnSessionCaptionConfigUpdated(kMainStudentGroupName, _))
+      .Times(0);
+
+  // Have updated one session.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 1 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, NotifySessionUpdateWhenSessionRosterUpdated) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+  auto* student_groups_1 =
+      session_1->mutable_roster()->mutable_student_groups()->Add();
+  student_groups_1->set_title(kMainStudentGroupName);
+  student_groups_1->mutable_students()->Add()->set_email("dog1@email.com");
+
+  auto session_2 = std::make_unique<::boca::Session>();
+  session_2->set_session_id(session_id);
+  auto* student_groups_2 =
+      session_2->mutable_roster()->mutable_student_groups()->Add();
+  student_groups_2->set_title(kMainStudentGroupName);
+  student_groups_2->mutable_students()->Add()->set_email("dog2@email.com");
+
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_2));
+      }));
+
+  EXPECT_CALL(*observer(), OnSessionRosterUpdated(_, _)).Times(2);
+
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest,
+       NotifySessionUpdateWhenSessionRosterOrderUpdated) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+  session_1->set_session_id(session_id);
+  auto* student_groups_1 =
+      session_1->mutable_roster()->mutable_student_groups()->Add();
+  student_groups_1->set_title(kMainStudentGroupName);
+  student_groups_1->mutable_students()->Add()->set_email("dog2@email.com");
+  student_groups_1->mutable_students()->Add()->set_email("dog1@email.com");
+
+  auto session_2 = std::make_unique<::boca::Session>();
+  session_2->set_session_id(session_id);
+  auto* student_groups_2 =
+      session_2->mutable_roster()->mutable_student_groups()->Add();
+  student_groups_2->set_title(kMainStudentGroupName);
+  student_groups_2->mutable_students()->Add()->set_email("dog1@email.com");
+  student_groups_2->mutable_students()->Add()->set_email("dog2@email.com");
+
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_2));
+      }));
+
+  EXPECT_CALL(*observer(), OnSessionRosterUpdated(_, _)).Times(2);
+
+  // Have updated two sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 2 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, DoNothingWhenSessionRosterSame) {
+  const std::string session_id = "1";
+  auto session_1 = std::make_unique<::boca::Session>();
+
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(testing::InvokeWithoutArgs([&]() {
+        boca_session_manager()->ParseSessionResponse(std::move(session_1));
+      }));
+
+  EXPECT_CALL(*observer(), OnSessionRosterUpdated(_, _)).Times(0);
+
+  // Have updated one sessions.
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 1 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, DoNotPollSessionWhenNoNetwork) {
+  ToggleOffline();
+  EXPECT_CALL(*session_client_impl(), GetSession(_)).Times(0);
+
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 1 +
+                                    base::Seconds(1));
+}
+
+TEST_F(BocaSessionManagerTest, DoNotPollSessionWhenUserNotActive) {
+  EXPECT_CALL(*session_client_impl(), GetSession(_)).Times(0);
+
+  // Sign in different user.
+  auto account_id = AccountId::FromUserEmailGaiaId("another", "user");
+  const std::string username_hash =
+      user_manager::FakeUserManager::GetFakeUsernameHash(account_id);
+  fake_user_manager().Reset(std::make_unique<user_manager::FakeUserManager>());
+  fake_user_manager()->AddUser(account_id);
+  fake_user_manager()->UserLoggedIn(account_id, username_hash,
+                                    /*browser_restart=*/false,
+                                    /*is_child=*/false);
+
+  task_environment()->FastForwardBy(BocaSessionManager::kPollingInterval * 1 +
+                                    base::Seconds(1));
+}
+
+}  // namespace ash::boca
diff --git a/chromeos/ash/components/boca/on_task/on_task_session_manager.cc b/chromeos/ash/components/boca/on_task/on_task_session_manager.cc
index 5e663d5..ead0a3f7 100644
--- a/chromeos/ash/components/boca/on_task/on_task_session_manager.cc
+++ b/chromeos/ash/components/boca/on_task/on_task_session_manager.cc
@@ -11,14 +11,28 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/sequenced_task_runner.h"
 #include "chromeos/ash/components/boca/on_task/on_task_system_web_app_manager.h"
 #include "components/sessions/core/session_id.h"
+#include "url/gurl.h"
 
 namespace ash::boca {
 
+namespace {
+
+// Delay in seconds before we attempt to add a tab.
+constexpr int kRetryAddTabTime = 3;
+
+}  // namespace
+
 OnTaskSessionManager::OnTaskSessionManager(
     std::unique_ptr<OnTaskSystemWebAppManager> system_web_app_manager)
-    : system_web_app_manager_(std::move(system_web_app_manager)) {}
+    : system_web_app_manager_(std::move(system_web_app_manager)),
+      system_web_app_launch_helper_(
+          std::make_unique<OnTaskSessionManager::SystemWebAppLaunchHelper>(
+              system_web_app_manager_.get())) {}
 
 OnTaskSessionManager::~OnTaskSessionManager() = default;
 
@@ -38,10 +52,7 @@
     OnSessionStarted(session_id, producer);
     return;
   }
-
-  system_web_app_manager_->LaunchSystemWebAppAsync(
-      base::BindOnce(&OnTaskSessionManager::OnBocaSWALaunched,
-                     weak_ptr_factory_.GetWeakPtr()));
+  system_web_app_launch_helper_->LaunchBocaSWA();
 }
 
 void OnTaskSessionManager::OnSessionEnded(const std::string& session_id) {
@@ -52,7 +63,50 @@
   }
 }
 
-void OnTaskSessionManager::OnBocaSWALaunched(bool success) {
+void OnTaskSessionManager::OnBundleUpdated(const ::boca::Bundle& bundle) {
+  for (const ::boca::ContentConfig& content_config : bundle.content_configs()) {
+    CHECK(content_config.has_url());
+    const GURL url(content_config.url());
+    system_web_app_launch_helper_->AddTab(url);
+  }
+}
+
+OnTaskSessionManager::SystemWebAppLaunchHelper::SystemWebAppLaunchHelper(
+    OnTaskSystemWebAppManager* system_web_app_manager)
+    : system_web_app_manager_(system_web_app_manager) {}
+
+OnTaskSessionManager::SystemWebAppLaunchHelper::~SystemWebAppLaunchHelper() =
+    default;
+
+void OnTaskSessionManager::SystemWebAppLaunchHelper::LaunchBocaSWA() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  launch_in_progress_ = true;
+  system_web_app_manager_->LaunchSystemWebAppAsync(
+      base::BindOnce(&SystemWebAppLaunchHelper::OnBocaSWALaunched,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void OnTaskSessionManager::SystemWebAppLaunchHelper::AddTab(GURL url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (launch_in_progress_) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&SystemWebAppLaunchHelper::AddTab,
+                       weak_ptr_factory_.GetWeakPtr(), url),
+        base::Seconds(kRetryAddTabTime));
+    return;
+  }
+  if (const SessionID window_id =
+          system_web_app_manager_->GetActiveSystemWebAppWindowID();
+      window_id.is_valid()) {
+    system_web_app_manager_->CreateBackgroundTabWithUrl(window_id, url);
+  }
+}
+
+void OnTaskSessionManager::SystemWebAppLaunchHelper::OnBocaSWALaunched(
+    bool success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  launch_in_progress_ = false;
   if (!success) {
     // TODO(b/354007279): Enforce appropriate retries.
     return;
diff --git a/chromeos/ash/components/boca/on_task/on_task_session_manager.h b/chromeos/ash/components/boca/on_task/on_task_session_manager.h
index 7a15e66..ab9dda3 100644
--- a/chromeos/ash/components/boca/on_task/on_task_session_manager.h
+++ b/chromeos/ash/components/boca/on_task/on_task_session_manager.h
@@ -8,10 +8,13 @@
 #include <memory>
 #include <string>
 
+#include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 #include "chromeos/ash/components/boca/boca_session_manager.h"
 #include "chromeos/ash/components/boca/on_task/on_task_system_web_app_manager.h"
 #include "chromeos/ash/components/boca/proto/bundle.pb.h"
+#include "url/gurl.h"
 
 namespace ash::boca {
 
@@ -29,15 +32,42 @@
   void OnSessionStarted(const std::string& session_id,
                         const ::boca::UserIdentity& producer) override;
   void OnSessionEnded(const std::string& session_id) override;
+  void OnBundleUpdated(const ::boca::Bundle& bundle) override;
 
  private:
-  // Callback triggered when the Boca SWA is launched. Normally at the onset
-  // of a Boca session.
-  void OnBocaSWALaunched(bool success);
+  // Helper class that is used to launch the Boca system web app as well as
+  // manage all interactions with the Boca system web app while it is being
+  // spawned.
+  class SystemWebAppLaunchHelper {
+   public:
+    SystemWebAppLaunchHelper(OnTaskSystemWebAppManager* system_web_app_manager);
+    SystemWebAppLaunchHelper(const SystemWebAppLaunchHelper&) = delete;
+    SystemWebAppLaunchHelper& operator=(const SystemWebAppLaunchHelper&) =
+        delete;
+    ~SystemWebAppLaunchHelper();
+
+    void LaunchBocaSWA();
+    void AddTab(GURL url);
+
+   private:
+    // Callback triggered when the Boca SWA is launched. Normally at the onset
+    // of a Boca session.
+    void OnBocaSWALaunched(bool success);
+
+    // Owned by the parent class `OnTaskSessionManager` that owns an instance of
+    // the class `SystemWebAppLaunchHelper`, so there won't be UAF errors.
+    raw_ptr<OnTaskSystemWebAppManager> system_web_app_manager_;
+
+    SEQUENCE_CHECKER(sequence_checker_);
+
+    bool launch_in_progress_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
+
+    base::WeakPtrFactory<SystemWebAppLaunchHelper> weak_ptr_factory_{this};
+  };
 
   const std::unique_ptr<OnTaskSystemWebAppManager> system_web_app_manager_;
 
-  base::WeakPtrFactory<OnTaskSessionManager> weak_ptr_factory_{this};
+  const std::unique_ptr<SystemWebAppLaunchHelper> system_web_app_launch_helper_;
 };
 
 }  // namespace ash::boca
diff --git a/chromeos/ash/components/boca/on_task/on_task_session_manager_unittest.cc b/chromeos/ash/components/boca/on_task/on_task_session_manager_unittest.cc
index f79ba818..ac4978f 100644
--- a/chromeos/ash/components/boca/on_task/on_task_session_manager_unittest.cc
+++ b/chromeos/ash/components/boca/on_task/on_task_session_manager_unittest.cc
@@ -13,13 +13,18 @@
 #include "components/sessions/core/session_id.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 using ::testing::_;
 using ::testing::Return;
+using ::testing::Sequence;
 
 namespace ash::boca {
 namespace {
 
+constexpr char kTestUrl1[] = "https://www.google.com";
+constexpr char kTestUrl2[] = "https://www.youtube.com";
+
 // Mock implementation of the `OnTaskSystemWebAppManager`.
 class OnTaskSystemWebAppManagerMock : public OnTaskSystemWebAppManager {
  public:
@@ -40,6 +45,10 @@
               SetWindowTrackerForSystemWebAppWindow,
               (SessionID window_id),
               (override));
+  MOCK_METHOD(void,
+              CreateBackgroundTabWithUrl,
+              (SessionID window_id, GURL),
+              (override));
 };
 
 class OnTaskSessionManagerTest : public ::testing::Test {
@@ -121,5 +130,77 @@
   session_manager_->OnSessionEnded("test_session_id");
 }
 
+TEST_F(OnTaskSessionManagerTest, ShouldOpenTabsOnBundleUpdated) {
+  const SessionID kWindowId = SessionID::NewUnique();
+  EXPECT_CALL(*system_web_app_manager_ptr_, GetActiveSystemWebAppWindowID())
+      .Times(2)
+      .WillRepeatedly(Return(kWindowId));
+  EXPECT_CALL(*system_web_app_manager_ptr_,
+              CreateBackgroundTabWithUrl(kWindowId, GURL(kTestUrl1)))
+      .Times(1);
+  EXPECT_CALL(*system_web_app_manager_ptr_,
+              CreateBackgroundTabWithUrl(kWindowId, GURL(kTestUrl2)))
+      .Times(1);
+
+  ::boca::Bundle bundle;
+  bundle.add_content_configs()->set_url(kTestUrl1);
+  bundle.add_content_configs()->set_url(kTestUrl2);
+  session_manager_->OnBundleUpdated(bundle);
+}
+
+TEST_F(OnTaskSessionManagerTest, ShouldIgnoreWhenNoBocaSWAOpenOnBundleUpdated) {
+  EXPECT_CALL(*system_web_app_manager_ptr_, GetActiveSystemWebAppWindowID())
+      .Times(2)
+      .WillRepeatedly(Return(SessionID::InvalidValue()));
+  EXPECT_CALL(*system_web_app_manager_ptr_, CreateBackgroundTabWithUrl(_, _))
+      .Times(0);
+
+  ::boca::Bundle bundle;
+  bundle.add_content_configs()->set_url(kTestUrl1);
+  bundle.add_content_configs()->set_url(kTestUrl2);
+  session_manager_->OnBundleUpdated(bundle);
+}
+
+TEST_F(OnTaskSessionManagerTest,
+       TabsCreatedAfterSWALaunchedWhenSessionStartsAndBundleUpdated) {
+  const SessionID kWindowId = SessionID::NewUnique();
+  Sequence s;
+  EXPECT_CALL(*system_web_app_manager_ptr_, GetActiveSystemWebAppWindowID())
+      .WillOnce(Return(
+          SessionID::InvalidValue()))  // Initial check before spawning SWA
+      .WillRepeatedly(Return(kWindowId));
+  EXPECT_CALL(*system_web_app_manager_ptr_, LaunchSystemWebAppAsync(_))
+      .InSequence(s)
+      .WillOnce([](base::OnceCallback<void(bool)> callback) {
+        std::move(callback).Run(true);
+      });
+  EXPECT_CALL(*system_web_app_manager_ptr_,
+              SetWindowTrackerForSystemWebAppWindow(kWindowId))
+      .Times(1)
+      .InSequence(s);
+  EXPECT_CALL(*system_web_app_manager_ptr_,
+              SetPinStateForSystemWebAppWindow(true, kWindowId))
+      .Times(1)
+      .InSequence(s);
+  EXPECT_CALL(*system_web_app_manager_ptr_,
+              SetPinStateForSystemWebAppWindow(false, kWindowId))
+      .Times(1)
+      .InSequence(s);
+  EXPECT_CALL(*system_web_app_manager_ptr_,
+              CreateBackgroundTabWithUrl(kWindowId, GURL(kTestUrl1)))
+      .Times(1)
+      .InSequence(s);
+  EXPECT_CALL(*system_web_app_manager_ptr_,
+              CreateBackgroundTabWithUrl(kWindowId, GURL(kTestUrl2)))
+      .Times(1)
+      .InSequence(s);
+
+  ::boca::Bundle bundle;
+  bundle.add_content_configs()->set_url(kTestUrl1);
+  bundle.add_content_configs()->set_url(kTestUrl2);
+  session_manager_->OnSessionStarted("test_session_id", ::boca::UserIdentity());
+  session_manager_->OnBundleUpdated(bundle);
+}
+
 }  // namespace
 }  // namespace ash::boca
diff --git a/chromeos/ash/components/boca/on_task/on_task_system_web_app_manager.h b/chromeos/ash/components/boca/on_task/on_task_system_web_app_manager.h
index c5c591da..288cb6dd 100644
--- a/chromeos/ash/components/boca/on_task/on_task_system_web_app_manager.h
+++ b/chromeos/ash/components/boca/on_task/on_task_system_web_app_manager.h
@@ -7,6 +7,7 @@
 
 #include "base/functional/callback_forward.h"
 #include "components/sessions/core/session_id.h"
+#include "url/gurl.h"
 
 namespace ash::boca {
 
@@ -40,6 +41,10 @@
   // id.
   virtual void SetWindowTrackerForSystemWebAppWindow(SessionID window_id) = 0;
 
+  // Creates a background tab with the given URL in the specified Boca SWA
+  // window.
+  virtual void CreateBackgroundTabWithUrl(SessionID window_id, GURL url) = 0;
+
  protected:
   OnTaskSystemWebAppManager() = default;
 };
diff --git a/chromeos/ash/components/emoji/emoji_search.cc b/chromeos/ash/components/emoji/emoji_search.cc
index 0057459..f7bfbf1a 100644
--- a/chromeos/ash/components/emoji/emoji_search.cc
+++ b/chromeos/ash/components/emoji/emoji_search.cc
@@ -10,7 +10,7 @@
 #include <set>
 #include <string>
 #include <string_view>
-#include <unordered_map>
+#include <map>
 #include <utility>
 #include <vector>
 
@@ -149,17 +149,16 @@
   }
 }
 
-std::unordered_map<std::string, double> GetResultsFromASingleWordQuery(
+std::map<std::string_view, double> GetResultsFromASingleWordQuery(
     const std::map<std::string, std::vector<EmojiSearchEntry>, std::less<>>&
         map,
-    const std::string_view query) {
+    const std::u16string_view query) {
   if (query.empty()) {
     return {};
   }
-  std::unordered_map<std::string, double> scored_emoji;
+  std::map<std::string_view, double> scored_emoji;
   // Make search case insensitive.
-  std::string lower_bound =
-      base::UTF16ToUTF8(base::i18n::ToLower(base::UTF8ToUTF16(query)));
+  std::string lower_bound = base::UTF16ToUTF8(base::i18n::ToLower(query));
   std::string upper_bound = lower_bound;
   // will break if someone searches for some very specific char, but
   // should be fine.
@@ -180,32 +179,33 @@
 std::vector<EmojiSearchEntry> GetResultsFromMap(
     const std::map<std::string, std::vector<EmojiSearchEntry>, std::less<>>&
         map,
-    const std::string_view query) {
-  std::vector<std::string> words = base::SplitStringUsingSubstr(
-      query, " ", base::WhitespaceHandling::TRIM_WHITESPACE,
+    const std::u16string_view query) {
+  std::vector<std::u16string_view> words = base::SplitStringPieceUsingSubstr(
+      query, u" ", base::WhitespaceHandling::TRIM_WHITESPACE,
       base::SplitResult::SPLIT_WANT_NONEMPTY);
   if (words.empty()) {
     return {};
   }
-  std::unordered_map<std::string, double> scored_emoji =
+  std::map<std::string_view, double> scored_emoji =
       GetResultsFromASingleWordQuery(map, words.back());
   words.pop_back();
-  for (const std::string& word : words) {
-    std::unordered_map<std::string, double> newly_scored_emoji =
+  for (const std::u16string_view word : words) {
+    std::map<std::string_view, double> newly_scored_emoji =
         GetResultsFromASingleWordQuery(map, word);
-    for (const auto& already_scored_emoji : scored_emoji) {
+    for (auto& already_scored_emoji : scored_emoji) {
       auto it = newly_scored_emoji.find(already_scored_emoji.first);
       if (it != newly_scored_emoji.end()) {
-        scored_emoji[already_scored_emoji.first] *= it->second;
+        already_scored_emoji.second *= it->second;
       } else {
-        scored_emoji[already_scored_emoji.first] = 0;
+        already_scored_emoji.second = 0;
       }
     }
   }
   std::erase_if(scored_emoji, [](auto elem) { return elem.second == 0.0; });
   std::vector<EmojiSearchEntry> ret;
+  ret.reserve(scored_emoji.size());
   for (const auto& [emoji, weighting] : scored_emoji) {
-    ret.push_back({weighting, emoji});
+    ret.push_back({weighting, std::string(emoji)});
   }
   base::ranges::sort(
       ret, base::ranges::greater(),
@@ -343,7 +343,7 @@
     default;
 
 EmojiSearchResult EmojiSearch::SearchEmoji(
-    std::string_view query,
+    std::u16string_view query,
     base::span<const std::string> language_codes) {
   std::vector<EmojiSearchEntry> emojis;
   std::set<std::string> seen_emojis;
diff --git a/chromeos/ash/components/emoji/emoji_search.h b/chromeos/ash/components/emoji/emoji_search.h
index 0efeb66..0f7bc53 100644
--- a/chromeos/ash/components/emoji/emoji_search.h
+++ b/chromeos/ash/components/emoji/emoji_search.h
@@ -83,7 +83,7 @@
   // `language_codes` span first, then prioritising emoji `weighting`. Because
   // of this, `weighting` is NOT guaranteed to be in non-increasing order.
   [[nodiscard]] EmojiSearchResult SearchEmoji(
-      std::string_view query,
+      std::u16string_view query,
       base::span<const std::string> language_codes);
 
   void LoadEmojiLanguages(base::span<const std::string> language_codes);
diff --git a/chromeos/ash/components/emoji/emoji_search_unittest.cc b/chromeos/ash/components/emoji/emoji_search_unittest.cc
index f38db5e..6ace140 100644
--- a/chromeos/ash/components/emoji/emoji_search_unittest.cc
+++ b/chromeos/ash/components/emoji/emoji_search_unittest.cc
@@ -88,7 +88,7 @@
   EmojiSearch search;
 
   search.LoadEmojiLanguages({{"ja"}});
-  EmojiSearchResult result = search.SearchEmoji("笑顔", {{"ja"}});
+  EmojiSearchResult result = search.SearchEmoji(u"笑顔", {{"ja"}});
   EXPECT_THAT(result.emojis, UnorderedElementsAre(FieldsAre(Gt(0), "😀"),
                                                   FieldsAre(Gt(0), "😺")));
   EXPECT_THAT(result.symbols, IsEmpty());
@@ -129,7 +129,7 @@
   EmojiSearch search;
 
   search.LoadEmojiLanguages({{"ja"}});
-  EmojiSearchResult result = search.SearchEmoji("矢印", {{"ja"}});
+  EmojiSearchResult result = search.SearchEmoji(u"矢印", {{"ja"}});
   EXPECT_THAT(result.symbols, UnorderedElementsAre(FieldsAre(Gt(0), "←")));
   EXPECT_THAT(result.emojis, IsEmpty());
   EXPECT_THAT(result.emoticons, IsEmpty());
@@ -168,7 +168,7 @@
   search.LoadEmojiLanguages({{"en", "fr"}});
 
   // Note that the results are to be presented in order of languages.
-  EmojiSearchResult result = search.SearchEmoji("musi", {{"fr", "en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"musi", {{"fr", "en"}});
   EXPECT_THAT(result.emojis, UnorderedElementsAre(FieldsAre(Gt(0), "🎹"),
                                                   FieldsAre(Gt(0), "🎸")));
   EXPECT_THAT(result.symbols, UnorderedElementsAre(FieldsAre(Gt(0), "♯"),
@@ -198,7 +198,7 @@
 
   EmojiSearch search;
 
-  EmojiSearchResult result = search.SearchEmoji("face", {{"en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"face", {{"en"}});
 
   EXPECT_THAT(result.emojis, ElementsAre(FieldsAre(Gt(0), "😀")));
   EXPECT_THAT(result.emoticons, ElementsAre(FieldsAre(Gt(0), ":-)")));
@@ -239,7 +239,7 @@
 
   search.LoadEmojiLanguages({{"en", "ja"}});
   EmojiSearchResult result =
-      search.SearchEmoji("grinning face", {{"en", "ja"}});
+      search.SearchEmoji(u"grinning face", {{"en", "ja"}});
   EXPECT_THAT(result.emojis, UnorderedElementsAre(FieldsAre(Gt(0), "😀")));
   EXPECT_THAT(result.symbols, IsEmpty());
   EXPECT_THAT(result.emoticons, IsEmpty());
@@ -278,14 +278,14 @@
 
   search.LoadEmojiLanguages({{"en", "ja"}});
   EmojiSearchResult en_ja_result =
-      search.SearchEmoji("grinning", {{"en", "ja"}});
+      search.SearchEmoji(u"grinning", {{"en", "ja"}});
   EXPECT_THAT(en_ja_result.emojis,
               ElementsAre(FieldsAre(Gt(0), "😀"), FieldsAre(Gt(0), "😺")));
   EXPECT_THAT(en_ja_result.symbols, IsEmpty());
   EXPECT_THAT(en_ja_result.emoticons, IsEmpty());
 
   EmojiSearchResult ja_en_result =
-      search.SearchEmoji("grinning", {{"ja", "en"}});
+      search.SearchEmoji(u"grinning", {{"ja", "en"}});
   EXPECT_THAT(ja_en_result.emojis,
               ElementsAre(FieldsAre(Gt(0), "😺"), FieldsAre(Gt(0), "😀")));
   EXPECT_THAT(ja_en_result.symbols, IsEmpty());
@@ -315,7 +315,7 @@
 
   EmojiSearch search;
 
-  EmojiSearchResult result = search.SearchEmoji("lulz", {{"en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"lulz", {{"en"}});
 
   EXPECT_THAT(result.emojis, ElementsAre(FieldsAre(Gt(0), "😀")));
   EXPECT_THAT(result.emoticons, IsEmpty());
@@ -345,7 +345,7 @@
 
   EmojiSearch search;
 
-  EmojiSearchResult result = search.SearchEmoji("gr fa", {{"en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"gr fa", {{"en"}});
 
   EXPECT_THAT(result.emojis, ElementsAre(FieldsAre(Gt(0), "😀")));
   EXPECT_THAT(result.symbols, IsEmpty());
@@ -374,7 +374,7 @@
 
   EmojiSearch search;
 
-  EmojiSearchResult result = search.SearchEmoji("smiley", {{"en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"smiley", {{"en"}});
 
   EXPECT_THAT(result.emoticons, ElementsAre(FieldsAre(Gt(0), ":-)")));
   EXPECT_THAT(result.emojis, IsEmpty());
@@ -402,7 +402,7 @@
             {"string":":-)","name":"smiley face "}}]}])-"}}});
   EmojiSearch search;
 
-  EmojiSearchResult result = search.SearchEmoji("left", {{"en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"left", {{"en"}});
 
   EXPECT_THAT(result.symbols, ElementsAre(FieldsAre(Gt(0), "←")));
   EXPECT_THAT(result.emojis, IsEmpty());
@@ -431,7 +431,7 @@
 
   EmojiSearch search;
 
-  EmojiSearchResult result = search.SearchEmoji("LEFT", {{"en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"LEFT", {{"en"}});
 
   EXPECT_THAT(result.symbols, ElementsAre(FieldsAre(Gt(0), "←")));
   EXPECT_THAT(result.emojis, IsEmpty());
@@ -460,7 +460,7 @@
 
   EmojiSearch search;
 
-  EmojiSearchResult result = search.SearchEmoji("grinning face", {{"en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"grinning face", {{"en"}});
 
   EXPECT_THAT(result.emojis,
               ElementsAre(FieldsAre(Gt(0), "😀a"), FieldsAre(Gt(0), "😀")));
@@ -490,7 +490,7 @@
 
   EmojiSearch search;
 
-  EmojiSearchResult result = search.SearchEmoji("grinning face", {{"en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"grinning face", {{"en"}});
 
   EXPECT_THAT(result.emojis,
               ElementsAre(FieldsAre(Gt(0), "😀a"), FieldsAre(Gt(0), "😀")));
@@ -521,7 +521,7 @@
 
   EmojiSearch search;
 
-  EmojiSearchResult result = search.SearchEmoji("grinning face", {{"en"}});
+  EmojiSearchResult result = search.SearchEmoji(u"grinning face", {{"en"}});
 
   EXPECT_THAT(result.emojis,
               ElementsAre(FieldsAre(DoubleNear(0.0029, 0.00005), "😀"),
diff --git a/chromeos/ash/components/feature_usage/feature_usage_metrics.cc b/chromeos/ash/components/feature_usage/feature_usage_metrics.cc
index 06891d3..ee297e253 100644
--- a/chromeos/ash/components/feature_usage/feature_usage_metrics.cc
+++ b/chromeos/ash/components/feature_usage/feature_usage_metrics.cc
@@ -65,7 +65,7 @@
   // Schedule the first run some time in the future to not overload startup
   // flow.
   SetupTimer(kInitialInterval);
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 }
 
 void FeatureUsageMetrics::SetupTimer(base::TimeDelta delta) {
@@ -77,7 +77,7 @@
 
 FeatureUsageMetrics::~FeatureUsageMetrics() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
   MaybeReportUseTime();
 }
 
diff --git a/chromeos/ash/components/feature_usage/feature_usage_metrics_unittest.cc b/chromeos/ash/components/feature_usage/feature_usage_metrics_unittest.cc
index aa4dd14..b07c581 100644
--- a/chromeos/ash/components/feature_usage/feature_usage_metrics_unittest.cc
+++ b/chromeos/ash/components/feature_usage/feature_usage_metrics_unittest.cc
@@ -31,8 +31,9 @@
                                 public FeatureUsageMetrics::Delegate {
  public:
   FeatureUsageMetricsTest() {
-    if (!base::PowerMonitor::IsInitialized()) {
-      base::PowerMonitor::Initialize(
+    if (auto* power_monitor = base::PowerMonitor::GetInstance();
+        !power_monitor->IsInitialized()) {
+      power_monitor->Initialize(
           std::make_unique<base::PowerMonitorDeviceSource>());
     }
 
diff --git a/chromeos/ash/components/fwupd/firmware_update_manager.cc b/chromeos/ash/components/fwupd/firmware_update_manager.cc
index a7cbcfa7..0bc3bc8 100644
--- a/chromeos/ash/components/fwupd/firmware_update_manager.cc
+++ b/chromeos/ash/components/fwupd/firmware_update_manager.cc
@@ -394,12 +394,19 @@
 
 bool RefreshRemoteAllowed(FirmwareUpdateManager::Source source,
                           bool refresh_remote_for_testing,
-                          bool is_metered) {
+                          const NetworkState* network) {
   FIRMWARE_LOG(DEBUG) << "RefreshRemoteAllowed()";
   DCHECK(NetworkHandler::IsInitialized());
+  if (!network) {
+    return false;
+  }
+  bool is_online = network->IsOnline();
+  bool is_metered = network->metered();
   const bool connection_ok =
-      !is_metered || source == FirmwareUpdateManager::Source::kUI;
-  FIRMWARE_LOG(DEBUG) << "Connection metered: " << is_metered
+      is_online &&
+      (!is_metered || source == FirmwareUpdateManager::Source::kUI);
+  FIRMWARE_LOG(DEBUG) << "Connection online: " << is_online
+                      << ", Connection metered: " << is_metered
                       << ", Source: " << static_cast<int>(source)
                       << ", Refresh Remote connection okay: " << connection_ok;
   if (!connection_ok) {
@@ -441,6 +448,12 @@
     FwupdClient::Get()->AddObserver(this);
   }
 
+  // NetworkHandler may not be initialized in tests.
+  if (NetworkHandler::IsInitialized()) {
+    NetworkHandler::Get()->network_state_handler()->AddObserver(this,
+                                                                FROM_HERE);
+  }
+
   DCHECK_EQ(nullptr, g_instance);
   g_instance = this;
 }
@@ -450,6 +463,12 @@
   if (FwupdClient::Get()) {
     FwupdClient::Get()->RemoveObserver(this);
   }
+
+  // NetworkHandler may not be initialized in tests.
+  if (NetworkHandler::IsInitialized()) {
+    NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
+                                                                   FROM_HERE);
+  }
   g_instance = nullptr;
 }
 
@@ -532,6 +551,17 @@
   }
 }
 
+void FirmwareUpdateManager::DefaultNetworkChanged(const NetworkState* network) {
+  FIRMWARE_LOG(DEBUG) << "DefaultNetworkChanged(): Fetching Updates: "
+                      << is_fetching_updates_
+                      << ", Pending refresh: " << is_refresh_pending_
+                      << ", Default Network: " << (network != nullptr);
+  if (is_fetching_updates_ || !is_refresh_pending_) {
+    return;
+  }
+  CheckRequirementsAndMaybeRefreshRemote(Source::kNetworkChange, network);
+}
+
 // TODO(michaelcheco): Handle the case where the app is closed during an
 // install.
 void FirmwareUpdateManager::ResetInstallState() {
@@ -580,13 +610,19 @@
   }
 
   FIRMWARE_LOG(USER) << "RequestAllUpdates: " << static_cast<int>(source);
+  is_refresh_pending_ = true;
+  CheckRequirementsAndMaybeRefreshRemote(
+      source, NetworkHandler::Get()->network_state_handler()->DefaultNetwork());
+}
+
+void FirmwareUpdateManager::CheckRequirementsAndMaybeRefreshRemote(
+    Source source,
+    const NetworkState* network) {
   is_fetching_updates_ = true;
   task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE,
       base::BindOnce(&RefreshRemoteAllowed, source, refresh_remote_for_testing_,
-                     NetworkHandler::Get()
-                         ->network_state_handler()
-                         ->default_network_is_metered()),
+                     network),
       base::BindOnce(&FirmwareUpdateManager::MaybeRefreshRemote,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -1197,6 +1233,9 @@
     DEVICE_LOG(device_event_log::LOG_TYPE_FIRMWARE, LogLevelForFileErrors())
         << "Refreshing LVFS remote failed: " << static_cast<int>(result);
   } else {
+    // Only set to false when refresh remote successful, otherwise retry when
+    // network changes (infrequent)
+    is_refresh_pending_ = false;
     FIRMWARE_LOG(USER) << "RefreshRemote completed";
   }
   firmware_update::metrics::EmitRefreshRemoteResult(result);
diff --git a/chromeos/ash/components/fwupd/firmware_update_manager.h b/chromeos/ash/components/fwupd/firmware_update_manager.h
index 141490b..82a3c322 100644
--- a/chromeos/ash/components/fwupd/firmware_update_manager.h
+++ b/chromeos/ash/components/fwupd/firmware_update_manager.h
@@ -24,6 +24,7 @@
 #include "chromeos/ash/components/dbus/fwupd/fwupd_properties.h"
 #include "chromeos/ash/components/dbus/fwupd/fwupd_request.h"
 #include "chromeos/ash/components/dbus/fwupd/fwupd_update.h"
+#include "chromeos/ash/components/network/network_state_handler_observer.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
@@ -106,14 +107,16 @@
 class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_FWUPD) FirmwareUpdateManager
     : public FwupdClient::Observer,
       public firmware_update::mojom::UpdateProvider,
-      public firmware_update::mojom::InstallController {
+      public firmware_update::mojom::InstallController,
+      public NetworkStateHandlerObserver {
  public:
   enum class Source {
     kUI = 0,
     kStartup = 1,
     kUSBChange = 2,
     kInstallComplete = 3,
-    kMaxValue = kInstallComplete,
+    kNetworkChange = 4,
+    kMaxValue = kNetworkChange,
   };
 
   FirmwareUpdateManager();
@@ -182,6 +185,9 @@
   // Query all updates for all devices.
   void RequestAllUpdates(Source source);
 
+  // NetworkStateHandlerObserver:
+  void DefaultNetworkChanged(const NetworkState* network) override;
+
   void BindInterface(
       mojo::PendingReceiver<firmware_update::mojom::UpdateProvider>
           pending_receiver);
@@ -258,6 +264,9 @@
       MethodCallback callback,
       base::FilePath download_path);
 
+  void CheckRequirementsAndMaybeRefreshRemote(Source source,
+                                              const NetworkState* network);
+
   // If refresh remote is allowed and call RefreshRemote otherwise continue with
   // RequestUpdates()
   void MaybeRefreshRemote(bool refresh_allowed);
@@ -371,6 +380,10 @@
   // Whether or not fetching updates in inflight.
   bool is_fetching_updates_ = false;
 
+  // Whether Refresh Remote has been requested and pending successful
+  // completion.
+  bool is_refresh_pending_ = false;
+
   // Checksum and firmware paths and File objects are held temporarily during
   // download, and are used for cleanup which must be done on task_runner_.
   base::FilePath checksum_filepath_;
diff --git a/chromeos/ash/components/fwupd/firmware_update_manager_unittest.cc b/chromeos/ash/components/fwupd/firmware_update_manager_unittest.cc
index b441138..ad1d907 100644
--- a/chromeos/ash/components/fwupd/firmware_update_manager_unittest.cc
+++ b/chromeos/ash/components/fwupd/firmware_update_manager_unittest.cc
@@ -258,6 +258,11 @@
     firmware_update_manager_ = std::make_unique<FirmwareUpdateManager>();
     firmware_update_manager_->BindInterface(
         update_provider_remote_.BindNewPipeAndPassReceiver());
+
+    // Default network to online
+    network_handler_test_helper_->ResetDevicesAndServices();
+    default_wifi_path_ =
+        network_handler_test_helper_->ConfigureWiFi(shill::kStateOnline);
   }
 
   FirmwareUpdateManagerTest(const FirmwareUpdateManagerTest&) = delete;
@@ -719,10 +724,17 @@
     task_environment_.RunUntilIdle();
   }
 
+  void SetDefaultNetworkState(const std::string& state) {
+    network_handler_test_helper_->SetServiceProperty(
+        default_wifi_path_, shill::kStateProperty, base::Value(state));
+  }
+
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
   std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
+  std::string default_wifi_path_;
+
   // `FwupdClient` must be be before `FirmwareUpdateManager`.
   raw_ptr<FwupdClient, DanglingUntriaged> dbus_client_ = nullptr;
   std::unique_ptr<FakeFwupdDownloadClient> fake_fwupd_download_client_;
@@ -1531,6 +1543,48 @@
                                          base::Minutes(5), 1);
 }
 
+TEST_F(FirmwareUpdateManagerTest, RefreshRemoteNetworkConnection) {
+  base::HistogramTester histogram_tester;
+
+  // Set connection to "idle" (disconnected).
+  SetDefaultNetworkState(shill::kStateIdle);
+
+  // Clear default empty dbus response expected from RefreshRemote in SetUp()
+  dbus_responses_.clear();
+  // Provide one device and one update for RequestUpdates() call from
+  // SetupObserver.
+  CreateOneDeviceAndUpdateResponse();
+
+  RequestAllUpdatesFromSource(FirmwareUpdateManager::Source::kUSBChange);
+  task_environment_.RunUntilIdle();
+  // Verify updates were caught even though RefreshRemote was skipped
+  histogram_tester.ExpectTotalCount(
+      "ChromeOS.FirmwareUpdateUi.RefreshRemoteResult", 0);
+  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());
+
+  CreateOneDeviceAndUpdateResponse();
+  RequestAllUpdatesFromSource(FirmwareUpdateManager::Source::kUSBChange);
+  task_environment_.RunUntilIdle();
+  // RefreshRemote should not be triggered since connection state is
+  // "disconnecting".
+  histogram_tester.ExpectTotalCount(
+      "ChromeOS.FirmwareUpdateUi.RefreshRemoteResult", 0);
+  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());
+
+  PrepareForRefreshRemote();
+  CreateOneDeviceAndUpdateResponse();
+
+  // Set connection to "online"
+  SetDefaultNetworkState(shill::kStateOnline);
+
+  // Allow FirmwareUpdateManager to respond to the state change.
+  task_environment_.RunUntilIdle();
+
+  histogram_tester.ExpectBucketCount(
+      "ChromeOS.FirmwareUpdateUi.RefreshRemoteResult", MethodResult::kSuccess,
+      1);
+}
+
 TEST_F(FirmwareUpdateManagerTest, RefreshRemoteMeteredConnectionFromUI) {
   base::HistogramTester histogram_tester;
 
diff --git a/chromeos/ash/components/phonehub/browser_tabs_model_controller_unittest.cc b/chromeos/ash/components/phonehub/browser_tabs_model_controller_unittest.cc
index 6f54379..cc446eb 100644
--- a/chromeos/ash/components/phonehub/browser_tabs_model_controller_unittest.cc
+++ b/chromeos/ash/components/phonehub/browser_tabs_model_controller_unittest.cc
@@ -55,11 +55,10 @@
   MutablePhoneModel* phone_model() { return phone_model_.get(); }
 
  private:
-  std::unique_ptr<BrowserTabsModelController> controller_;
-
   std::unique_ptr<MutablePhoneModel> phone_model_;
   multidevice_setup::FakeMultiDeviceSetupClient fake_multidevice_setup_client_;
   FakeBrowserTabsModelProvider fake_browser_tabs_model_provider_;
+  std::unique_ptr<BrowserTabsModelController> controller_;
 };
 
 TEST_F(BrowserTabsModelControllerTest, MutablePhoneModelProperlySet) {
diff --git a/chromeos/ash/components/policy/weekly_time/test_support.cc b/chromeos/ash/components/policy/weekly_time/test_support.cc
index 64d61f4..8d92f21 100644
--- a/chromeos/ash/components/policy/weekly_time/test_support.cc
+++ b/chromeos/ash/components/policy/weekly_time/test_support.cc
@@ -27,17 +27,36 @@
   return dict;
 }
 
-base::Value::Dict BuildWeeklyTimeIntervalCheckedDict(
-    WeeklyTimeChecked::Day start_day, int start_milliseconds,
-    WeeklyTimeChecked::Day end_day, int end_milliseconds) {
+base::Value::Dict BuildWeeklyTimeCheckedDict(base::TimeDelta time_delta) {
+  auto w = WeeklyTimeChecked::FromTimeDelta(time_delta);
+  return BuildWeeklyTimeCheckedDict(w.day_of_week(),
+                                    w.milliseconds_since_midnight());
+}
+
+base::Value::Dict BuildWeeklyTimeIntervalCheckedDict(base::Value::Dict start,
+                                                     base::Value::Dict end) {
   base::Value::Dict dict;
-  auto start = BuildWeeklyTimeCheckedDict(start_day, start_milliseconds);
-  auto end = BuildWeeklyTimeCheckedDict(end_day, end_milliseconds);
   EXPECT_TRUE(dict.Set(WeeklyTimeIntervalChecked::kStart, std::move(start)));
   EXPECT_TRUE(dict.Set(WeeklyTimeIntervalChecked::kEnd, std::move(end)));
   return dict;
 }
 
+base::Value::Dict BuildWeeklyTimeIntervalCheckedDict(base::TimeDelta start,
+                                                     base::TimeDelta end) {
+  return BuildWeeklyTimeIntervalCheckedDict(BuildWeeklyTimeCheckedDict(start),
+                                            BuildWeeklyTimeCheckedDict(end));
+}
+
+base::Value::Dict BuildWeeklyTimeIntervalCheckedDict(
+    WeeklyTimeChecked::Day start_day,
+    int start_milliseconds,
+    WeeklyTimeChecked::Day end_day,
+    int end_milliseconds) {
+  return BuildWeeklyTimeIntervalCheckedDict(
+      BuildWeeklyTimeCheckedDict(start_day, start_milliseconds),
+      BuildWeeklyTimeCheckedDict(end_day, end_milliseconds));
+}
+
 base::Value::List BuildList(std::string_view json_str) {
   std::optional<base::Value> value = base::JSONReader::Read(json_str);
   if (!value.has_value()) {
@@ -51,6 +70,21 @@
   return std::move(value.value()).TakeList();
 }
 
+base::Value::List BuildList(base::Time now,
+                            base::TimeDelta from_now,
+                            base::TimeDelta duration) {
+  // TODO(isandrk): Can probably simplify function to only pass in start_time
+  // (which would be equal to now + from_now).
+  WeeklyTimeChecked current_weekly_time_checked =
+      WeeklyTimeChecked::FromTimeAsLocalTime(now);
+  base::TimeDelta current_time_of_week =
+      current_weekly_time_checked.ToTimeDelta();
+  base::TimeDelta start = current_time_of_week + from_now;
+  base::TimeDelta end = start + duration;
+  return base::Value::List().Append(
+      BuildWeeklyTimeIntervalCheckedDict(start, end));
+}
+
 std::vector<WeeklyTimeIntervalChecked> BuildIntervals(
     std::string_view json_str) {
   base::Value::List list = BuildList(json_str);
diff --git a/chromeos/ash/components/policy/weekly_time/test_support.h b/chromeos/ash/components/policy/weekly_time/test_support.h
index a03b7ea..076463f 100644
--- a/chromeos/ash/components/policy/weekly_time/test_support.h
+++ b/chromeos/ash/components/policy/weekly_time/test_support.h
@@ -22,8 +22,13 @@
 // `chromeos::prefs::kDeviceRestrictionSchedule`.
 base::Value::Dict BuildWeeklyTimeCheckedDict(WeeklyTimeChecked::Day day_of_week,
                                              int milliseconds_since_midnight);
+base::Value::Dict BuildWeeklyTimeCheckedDict(base::TimeDelta time_delta);
 
 // Represents an interval from `chromeos::prefs::kDeviceRestrictionSchedule`.
+base::Value::Dict BuildWeeklyTimeIntervalCheckedDict(base::TimeDelta start,
+                                                     base::TimeDelta end);
+base::Value::Dict BuildWeeklyTimeIntervalCheckedDict(base::Value::Dict start,
+                                                     base::Value::Dict end);
 base::Value::Dict BuildWeeklyTimeIntervalCheckedDict(
     WeeklyTimeChecked::Day start_day, int start_milliseconds,
     WeeklyTimeChecked::Day end_day, int end_milliseconds);
@@ -31,6 +36,13 @@
 // Builds a `base::Value::List` from the given `json_str`.
 base::Value::List BuildList(std::string_view json_str);
 
+// Builds a list with one interval starting `from_now` from `now` with duration
+// `duration`. `from_now` can be negative meaning the interval started in the
+// past.
+base::Value::List BuildList(base::Time now,
+                            base::TimeDelta from_now,
+                            base::TimeDelta duration);
+
 // Builds a list of intervals from the given `json_str`.
 std::vector<WeeklyTimeIntervalChecked> BuildIntervals(
     std::string_view json_str);
diff --git a/chromeos/ash/components/policy/weekly_time/weekly_time_checked.cc b/chromeos/ash/components/policy/weekly_time/weekly_time_checked.cc
index 83de90a..4a224ba 100644
--- a/chromeos/ash/components/policy/weekly_time/weekly_time_checked.cc
+++ b/chromeos/ash/components/policy/weekly_time/weekly_time_checked.cc
@@ -84,6 +84,21 @@
   return FromExploded(exploded);
 }
 
+// static
+WeeklyTimeChecked WeeklyTimeChecked::FromTimeDelta(base::TimeDelta time_delta) {
+  time_delta %= base::Days(7);
+  if (time_delta.is_negative()) {
+    time_delta += base::Days(7);
+  }
+
+  int day = time_delta.InDays();
+  time_delta -= base::Days(day);
+  int millis = time_delta.InMilliseconds();
+
+  // Add one to day since Monday starts at 1, not at 0.
+  return WeeklyTimeChecked(static_cast<Day>(day + 1), millis);
+}
+
 base::TimeDelta WeeklyTimeChecked::ToTimeDelta() const {
   return base::Days(static_cast<int>(day_of_week_) - 1) +
          base::Milliseconds(milliseconds_since_midnight_);
diff --git a/chromeos/ash/components/policy/weekly_time/weekly_time_checked.h b/chromeos/ash/components/policy/weekly_time/weekly_time_checked.h
index e527b119..bc96d2e 100644
--- a/chromeos/ash/components/policy/weekly_time/weekly_time_checked.h
+++ b/chromeos/ash/components/policy/weekly_time/weekly_time_checked.h
@@ -64,6 +64,9 @@
   // Constructs from `time` in local time (as opposed to GMT, PST, etc.).
   static WeeklyTimeChecked FromTimeAsLocalTime(base::Time time);
 
+  // Constructs from `time_delta`. Opposite of `ToTimeDelta()`.
+  static WeeklyTimeChecked FromTimeDelta(base::TimeDelta time_delta);
+
   // Convert to base::TimeDelta. (Monday, 0) = 0, (Monday, 5h) = 5h,
   // (Tuesday, 0) = 1d, (Tuesday, 5m) = 1d + 5m, etc.
   base::TimeDelta ToTimeDelta() const;
diff --git a/chromeos/ash/components/policy/weekly_time/weekly_time_checked_unittest.cc b/chromeos/ash/components/policy/weekly_time/weekly_time_checked_unittest.cc
index 5e91437..b03caf0 100644
--- a/chromeos/ash/components/policy/weekly_time/weekly_time_checked_unittest.cc
+++ b/chromeos/ash/components/policy/weekly_time/weekly_time_checked_unittest.cc
@@ -112,6 +112,42 @@
   EXPECT_EQ(w.milliseconds_since_midnight(), 10 * kMillisecondsInHour);
 }
 
+TEST(WeeklyTimeCheckedTest, FromTimeDelta) {
+  // clang-format off
+  struct TestData {
+    base::TimeDelta time_delta;
+    Day day;
+    int millis;
+  } test_data[] = {
+    {base::TimeDelta(), Day::kMonday, 0},
+    {base::Milliseconds(100), Day::kMonday, 100},
+    {base::Days(7), Day::kMonday, 0},
+    {base::Days(8), Day::kTuesday, 0},
+    {base::Days(16), Day::kWednesday, 0},
+    {base::Days(16) + base::Milliseconds(100), Day::kWednesday, 100},
+    // Negative values.
+    {-base::Days(1), Day::kSunday, 0},
+    {-base::Days(2), Day::kSaturday, 0},
+    {-base::Days(3), Day::kFriday, 0},
+    {-base::Days(7), Day::kMonday, 0},
+    {-base::Days(8), Day::kSunday, 0},
+    {-base::Days(1) - base::Hours(1), Day::kSaturday, base::Hours(23).InMilliseconds()},
+    {-base::Days(1) - base::Hours(23), Day::kSaturday, base::Hours(1).InMilliseconds()},
+    {-base::Days(6) - base::Hours(1), Day::kMonday, base::Hours(23).InMilliseconds()},
+    {-base::Days(6) - base::Hours(23), Day::kMonday, base::Hours(1).InMilliseconds()},
+    {-base::Days(6) - base::Hours(24), Day::kMonday, 0},
+  };
+  // clang-format on
+
+  int i = 0;
+  for (const auto& t : test_data) {
+    auto actual = WeeklyTimeChecked::FromTimeDelta(t.time_delta);
+    auto expected = WeeklyTimeChecked(t.day, t.millis);
+    EXPECT_EQ(actual, expected) << "Failed test case #" << i;
+    i++;
+  }
+}
+
 TEST(WeeklyTimeCheckedTest, ToTimeDelta) {
   auto w = WeeklyTimeChecked(Day::kTuesday, 3 * kMillisecondsInHour);
   auto td = w.ToTimeDelta();
diff --git a/chromeos/ash/components/timezone/timezone_resolver.cc b/chromeos/ash/components/timezone/timezone_resolver.cc
index 8f80727..08a194e 100644
--- a/chromeos/ash/components/timezone/timezone_resolver.cc
+++ b/chromeos/ash/components/timezone/timezone_resolver.cc
@@ -269,7 +269,7 @@
   DCHECK(!resolver_->apply_timezone().is_null());
   DCHECK(!resolver_->delay_network_call().is_null());
 
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 
   const int64_t last_refresh_at_us =
       resolver_->local_state()->GetInt64(kLastTimeZoneRefreshTime);
@@ -285,7 +285,7 @@
 }
 
 TimeZoneResolver::TimeZoneResolverImpl::~TimeZoneResolverImpl() {
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 }
 
 void TimeZoneResolver::TimeZoneResolverImpl::Start() {
diff --git a/chromeos/ash/resources/internal b/chromeos/ash/resources/internal
index 23ef9d1..6f536dc 160000
--- a/chromeos/ash/resources/internal
+++ b/chromeos/ash/resources/internal
@@ -1 +1 @@
-Subproject commit 23ef9d168e94db8a6f5630e059dabb2c0b24994e
+Subproject commit 6f536dc3c7bb9557fe54bb4ebef02eb9143e8a92
diff --git a/chromeos/ash/services/multidevice_setup/wifi_sync_notification_controller.cc b/chromeos/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
index d2f291e..0eb3244 100644
--- a/chromeos/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
+++ b/chromeos/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
@@ -80,7 +80,7 @@
       delegate_notifier_(delegate_notifier) {
   if (pref_service_->GetBoolean(kCanShowWifiSyncAnnouncementPrefName)) {
     session_manager::SessionManager::Get()->AddObserver(this);
-    base::PowerMonitor::AddPowerSuspendObserver(this);
+    base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
     did_register_session_observers_ = true;
   }
 }
@@ -88,7 +88,7 @@
 WifiSyncNotificationController::~WifiSyncNotificationController() {
   if (did_register_session_observers_) {
     session_manager::SessionManager::Get()->RemoveObserver(this);
-    base::PowerMonitor::RemovePowerSuspendObserver(this);
+    base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
   }
 }
 
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index c65baab..39fffff 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -176,7 +176,7 @@
 // Controls whether mahi sends url when making request to the server.
 BASE_FEATURE(kMahiSendingUrl,
              "MahiSendingUrl",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Controls enabling / disabling the sparky feature.
diff --git a/chromeos/dbus/power/power_manager_client_unittest.cc b/chromeos/dbus/power/power_manager_client_unittest.cc
index 61605e3..8a1ba488 100644
--- a/chromeos/dbus/power/power_manager_client_unittest.cc
+++ b/chromeos/dbus/power/power_manager_client_unittest.cc
@@ -772,7 +772,7 @@
 TEST_F(PowerManagerClientTest, ChangeThermalState) {
   base::test::ScopedPowerMonitorTestSource power_monitor_source;
   PowerMonitorTestObserverLocal observer;
-  base::PowerMonitor::AddPowerThermalObserver(&observer);
+  base::PowerMonitor::GetInstance()->AddPowerThermalObserver(&observer);
 
   typedef struct {
     power_manager::ThermalEvent::ThermalState dbus_state;
@@ -810,7 +810,7 @@
     EXPECT_EQ(observer.GetThermalState(), p.expected_state);
   }
 
-  base::PowerMonitor::RemovePowerThermalObserver(&observer);
+  base::PowerMonitor::GetInstance()->RemovePowerThermalObserver(&observer);
 }
 
 // Test that |RequestSuspend| calls the DBus method with the same name.
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index 8b78ce5..fb571f5e 100644
--- a/chromeos/profiles/arm.afdo.newest.txt
+++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-none-130-6668.0-1725243337-benchmark-130.0.6694.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-130-6668.0-1725243337-benchmark-130.0.6695.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index d6a6ce2..2e1d5da8 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-130-6668.0-1725243046-benchmark-130.0.6694.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-130-6668.0-1725243046-benchmark-130.0.6695.0-r1-redacted.afdo.xz
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index e834c8e..8f6a60b 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -133,10 +133,6 @@
   # b/362457684: test is failing on betty, blocking LKGM.
   "cryptohome.UssMigrationChallengeCredential.rsassa_all@betty",
 
-  # b/362767913: test is failing on DCHECK enabled builds
-  # Teporarily disable in the CI until LKGM is upreved.
-  "arc.SettingsBridge.vm@betty",
-
   # READ COMMENT AT TOP BEFORE ADDING NEW TESTS HERE.
 ]
 
diff --git a/chromeos/ui/vector_icons/BUILD.gn b/chromeos/ui/vector_icons/BUILD.gn
index 2882643..cf72da8 100644
--- a/chromeos/ui/vector_icons/BUILD.gn
+++ b/chromeos/ui/vector_icons/BUILD.gn
@@ -13,6 +13,7 @@
     "assistant.icon",
     "calculate.icon",
     "conversion_path.icon",
+    "dictionary.icon",
     "editor_menu_elaborate.icon",
     "editor_menu_emojify.icon",
     "editor_menu_formalize.icon",
diff --git a/chromeos/ui/vector_icons/dictionary.icon b/chromeos/ui/vector_icons/dictionary.icon
new file mode 100644
index 0000000..08553a0
--- /dev/null
+++ b/chromeos/ui/vector_icons/dictionary.icon
@@ -0,0 +1,107 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+FILL_RULE_NONZERO,
+MOVE_TO, 3.5f, 11.79f,
+R_H_LINE_TO, 0.88f,
+R_LINE_TO, 0.46f, -1.29f,
+R_H_LINE_TO, 2.06f,
+R_LINE_TO, 0.46f, 1.29f,
+R_H_LINE_TO, 0.9f,
+LINE_TO, 6.33f, 6.71f,
+R_H_LINE_TO, -0.91f,
+CLOSE,
+MOVE_TO, 5.11f, 9.77f,
+R_LINE_TO, 0.75f, -2.12f,
+R_H_LINE_TO, 0.04f,
+R_LINE_TO, 0.75f, 2.13f,
+CLOSE,
+R_MOVE_TO, 6.65f, -1.81f,
+V_LINE_TO, 6.38f,
+R_CUBIC_TO, 0.43f, -0.14f, 0.88f, -0.24f, 1.33f, -0.3f,
+R_CUBIC_TO, 0.46f, -0.06f, 0.93f, -0.09f, 1.42f, -0.09f,
+R_CUBIC_TO, 0.32f, 0, 0.64f, 0.02f, 0.97f, 0.05f,
+R_CUBIC_TO, 0.33f, 0.04f, 0.67f, 0.09f, 1.03f, 0.16f,
+V_LINE_TO, 7.73f,
+R_ARC_TO, 11.87f, 11.87f, 0, 0, 0, -1.1f, -0.21f,
+R_ARC_TO, 7.39f, 7.39f, 0, 0, 0, -0.89f, -0.06f,
+R_CUBIC_TO, -0.47f, 0, -0.94f, 0.04f, -1.39f, 0.13f,
+R_CUBIC_TO, -0.46f, 0.09f, -0.91f, 0.21f, -1.35f, 0.38f,
+CLOSE,
+R_MOVE_TO, 0, 4.92f,
+R_V_LINE_TO, -1.58f,
+R_CUBIC_TO, 0.39f, -0.12f, 0.81f, -0.23f, 1.25f, -0.3f,
+R_ARC_TO, 9.16f, 9.16f, 0, 0, 1, 1.5f, -0.11f,
+R_CUBIC_TO, 0.38f, 0, 0.73f, 0.02f, 1.05f, 0.06f,
+R_CUBIC_TO, 0.33f, 0.04f, 0.65f, 0.1f, 0.95f, 0.17f,
+R_V_LINE_TO, 1.54f,
+R_ARC_TO, 11.73f, 11.73f, 0, 0, 0, -1.1f, -0.21f,
+R_ARC_TO, 6.68f, 6.68f, 0, 0, 0, -0.89f, -0.06f,
+R_CUBIC_TO, -0.47f, 0, -0.94f, 0.04f, -1.39f, 0.13f,
+R_CUBIC_TO, -0.46f, 0.08f, -0.91f, 0.21f, -1.35f, 0.38f,
+CLOSE,
+R_MOVE_TO, 0, -2.46f,
+V_LINE_TO, 8.83f,
+R_ARC_TO, 9.45f, 9.45f, 0, 0, 1, 1.36f, -0.31f,
+R_CUBIC_TO, 0.47f, -0.07f, 0.93f, -0.1f, 1.39f, -0.1f,
+R_CUBIC_TO, 0.38f, 0, 0.73f, 0.02f, 1.05f, 0.06f,
+R_CUBIC_TO, 0.33f, 0.04f, 0.65f, 0.1f, 0.95f, 0.17f,
+R_V_LINE_TO, 1.54f,
+R_ARC_TO, 7.19f, 7.19f, 0, 0, 0, -1.03f, -0.21f,
+R_ARC_TO, 7.42f, 7.42f, 0, 0, 0, -2.31f, 0.06f,
+R_ARC_TO, 9.42f, 9.42f, 0, 0, 0, -1.41f, 0.38f,
+CLOSE,
+MOVE_TO, 5.5f, 14,
+R_CUBIC_TO, 0.65f, 0, 1.29f, 0.08f, 1.92f, 0.25f,
+R_CUBIC_TO, 0.63f, 0.17f, 1.23f, 0.38f, 1.83f, 0.63f,
+V_LINE_TO, 5.42f,
+R_ARC_TO, 7.79f, 7.79f, 0, 0, 0, -1.81f, -0.69f,
+ARC_TO, 8.15f, 8.15f, 0, 0, 0, 5.5f, 4.5f,
+R_CUBIC_TO, -0.52f, 0, -1.02f, 0.05f, -1.53f, 0.14f,
+R_ARC_TO, 6.44f, 6.44f, 0, 0, 0, -1.47f, 0.45f,
+V_LINE_TO, 14.5f,
+R_CUBIC_TO, 0.48f, -0.18f, 0.98f, -0.31f, 1.48f, -0.39f,
+CUBIC_TO, 4.48f, 14.04f, 4.98f, 14, 5.5f, 14,
+CLOSE,
+R_MOVE_TO, 5.25f, 0.88f,
+R_ARC_TO, 9.92f, 9.92f, 0, 0, 1, 1.83f, -0.64f,
+ARC_TO, 8.08f, 8.08f, 0, 0, 1, 14.5f, 14,
+R_CUBIC_TO, 0.52f, 0, 1.02f, 0.03f, 1.53f, 0.09f,
+R_CUBIC_TO, 0.51f, 0.06f, 1, 0.2f, 1.47f, 0.41f,
+V_LINE_TO, 5.08f,
+R_CUBIC_TO, -0.48f, -0.18f, -0.98f, -0.32f, -1.48f, -0.43f,
+ARC_TO, 7.32f, 7.32f, 0, 0, 0, 14.5f, 4.5f,
+R_CUBIC_TO, -0.67f, 0, -1.31f, 0.08f, -1.94f, 0.23f,
+R_ARC_TO, 7.79f, 7.79f, 0, 0, 0, -1.81f, 0.69f,
+CLOSE,
+MOVE_TO, 10, 17,
+R_ARC_TO, 10.62f, 10.62f, 0, 0, 0, -2.14f, -1.08f,
+ARC_TO, 6.74f, 6.74f, 0, 0, 0, 5.5f, 15.5f,
+R_CUBIC_TO, -0.53f, 0, -1.05f, 0.05f, -1.58f, 0.16f,
+R_ARC_TO, 7.82f, 7.82f, 0, 0, 0, -1.52f, 0.47f,
+R_ARC_TO, 0.97f, 0.97f, 0, 0, 1, -0.95f, -0.07f,
+R_ARC_TO, 0.91f, 0.91f, 0, 0, 1, -0.45f, -0.82f,
+V_LINE_TO, 4.75f,
+R_CUBIC_TO, 0, -0.19f, 0.05f, -0.37f, 0.16f, -0.54f,
+R_CUBIC_TO, 0.11f, -0.16f, 0.25f, -0.29f, 0.43f, -0.37f,
+ARC_TO, 9.82f, 9.82f, 0, 0, 1, 3.5f, 3.21f,
+R_ARC_TO, 9.73f, 9.73f, 0, 0, 1, 4.32f, 0.07f,
+R_CUBIC_TO, 0.75f, 0.19f, 1.48f, 0.47f, 2.18f, 0.84f,
+R_ARC_TO, 10.63f, 10.63f, 0, 0, 1, 2.19f, -0.83f,
+ARC_TO, 9.13f, 9.13f, 0, 0, 1, 14.5f, 3,
+R_CUBIC_TO, 0.68f, 0, 1.35f, 0.07f, 2, 0.21f,
+R_CUBIC_TO, 0.65f, 0.14f, 1.29f, 0.35f, 1.92f, 0.63f,
+R_CUBIC_TO, 0.18f, 0.09f, 0.32f, 0.21f, 0.44f, 0.38f,
+R_CUBIC_TO, 0.11f, 0.17f, 0.17f, 0.35f, 0.17f, 0.54f,
+R_V_LINE_TO, 10.48f,
+R_CUBIC_TO, 0, 0.35f, -0.11f, 0.63f, -0.32f, 0.83f,
+R_CUBIC_TO, -0.22f, 0.21f, -0.44f, 0.26f, -0.68f, 0.14f,
+R_ARC_TO, 7.5f, 7.5f, 0, 0, 0, -1.72f, -0.54f,
+R_ARC_TO, 9.54f, 9.54f, 0, 0, 0, -1.8f, -0.17f,
+R_CUBIC_TO, -0.82f, 0, -1.6f, 0.14f, -2.35f, 0.42f,
+R_CUBIC_TO, -0.75f, 0.28f, -1.46f, 0.64f, -2.14f, 1.08f,
+CLOSE,
+MOVE_TO, 5.88f, 9.69f,
+CLOSE
diff --git a/clank b/clank
index 13f9fa6..474110c 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 13f9fa6614f55af1da0a158af6c69ea6e4686179
+Subproject commit 474110c7a5ee2acfa8f3781ae4a19c8b314f1e63
diff --git a/components/affiliations/core/browser/affiliation_fetch_throttler.cc b/components/affiliations/core/browser/affiliation_fetch_throttler.cc
index cf4c2bb..b53052d 100644
--- a/components/affiliations/core/browser/affiliation_fetch_throttler.cc
+++ b/components/affiliations/core/browser/affiliation_fetch_throttler.cc
@@ -122,10 +122,11 @@
 
   // The release time might have been increased if network connectivity was lost
   // and restored while this callback was in the task queue. If so, reschedule.
-  if (exponential_backoff_->ShouldRejectRequest())
+  if (exponential_backoff_->ShouldRejectRequest()) {
     EnsureCallbackIsScheduled();
-  else
+  } else {
     state_ = delegate_->OnCanSendNetworkRequest() ? FETCH_IN_FLIGHT : IDLE;
+  }
 }
 
 void AffiliationFetchThrottler::OnConnectionChanged(
diff --git a/components/affiliations/core/browser/facet_manager.cc b/components/affiliations/core/browser/facet_manager.cc
index 35c1769..e7318fb 100644
--- a/components/affiliations/core/browser/facet_manager.cc
+++ b/components/affiliations/core/browser/facet_manager.cc
@@ -144,10 +144,11 @@
   // If an initial fetch if needed, trigger that (the refetch will be scheduled
   // once the initial fetch completes). Otherwise schedule the next refetch.
   base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
-  if (next_required_fetch <= clock_->Now())
+  if (next_required_fetch <= clock_->Now()) {
     backend_->SignalNeedNetworkRequest();
-  else if (next_required_fetch < base::Time::Max())
+  } else if (next_required_fetch < base::Time::Max()) {
     backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
+  }
 
   // For a finite |keep_fresh_until|, schedule a callback so that once the
   // prefetch expires, it can be removed from |keep_fresh_untils_|, and also the
@@ -188,10 +189,11 @@
 
 void FacetManager::NotifyAtRequestedTime() {
   base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
-  if (next_required_fetch <= clock_->Now())
+  if (next_required_fetch <= clock_->Now()) {
     backend_->SignalNeedNetworkRequest();
-  else if (next_required_fetch < base::Time::Max())
+  } else if (next_required_fetch < base::Time::Max()) {
     backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
+  }
 
   auto iter_first_non_expired =
       keep_fresh_until_thresholds_.upper_bound(clock_->Now());
diff --git a/components/affiliations/core/browser/sql_table_builder.cc b/components/affiliations/core/browser/sql_table_builder.cc
index 1567ec0..a2ed6b8e0 100644
--- a/components/affiliations/core/browser/sql_table_builder.cc
+++ b/components/affiliations/core/browser/sql_table_builder.cc
@@ -26,10 +26,11 @@
 
 // Appends |name| to |list_of_names|, separating items with ", ".
 void Append(const std::string& name, std::string* list_of_names) {
-  if (list_of_names->empty())
+  if (list_of_names->empty()) {
     *list_of_names = name;
-  else
+  } else {
     *list_of_names += ", " + name;
+  }
 }
 
 // Returns true iff the foreign keys can be safely re-enabled on the database.
diff --git a/components/android_autofill/browser/android_autofill_client.cc b/components/android_autofill/browser/android_autofill_client.cc
index 4329e2508..31b4d21 100644
--- a/components/android_autofill/browser/android_autofill_client.cc
+++ b/components/android_autofill/browser/android_autofill_client.cc
@@ -183,10 +183,12 @@
   NOTIMPLEMENTED();
 }
 
-void AndroidAutofillClient::ShowAutofillSuggestions(
+autofill::AutofillClient::SuggestionUiSessionId
+AndroidAutofillClient::ShowAutofillSuggestions(
     const autofill::AutofillClient::PopupOpenArgs& open_args,
     base::WeakPtr<autofill::AutofillSuggestionDelegate> delegate) {
   NOTIMPLEMENTED();
+  return SuggestionUiSessionId();
 }
 
 void AndroidAutofillClient::UpdateAutofillDataListValues(
diff --git a/components/android_autofill/browser/android_autofill_client.h b/components/android_autofill/browser/android_autofill_client.h
index 5d8c0704..3cf06604 100644
--- a/components/android_autofill/browser/android_autofill_client.h
+++ b/components/android_autofill/browser/android_autofill_client.h
@@ -106,7 +106,7 @@
   void ShowDeleteAddressProfileDialog(
       const autofill::AutofillProfile& profile,
       AddressProfileDeleteDialogCallback delete_dialog_callback) override;
-  void ShowAutofillSuggestions(
+  SuggestionUiSessionId ShowAutofillSuggestions(
       const autofill::AutofillClient::PopupOpenArgs& open_args,
       base::WeakPtr<autofill::AutofillSuggestionDelegate> delegate) override;
   void UpdateAutofillDataListValues(
diff --git a/components/autofill/android/touch_to_fill_keyboard_suppressor.cc b/components/autofill/android/touch_to_fill_keyboard_suppressor.cc
index ac2cd6f8..dbda82b 100644
--- a/components/autofill/android/touch_to_fill_keyboard_suppressor.cc
+++ b/components/autofill/android/touch_to_fill_keyboard_suppressor.cc
@@ -62,26 +62,6 @@
   autofill_manager_observations_.AddObservation(&driver.GetAutofillManager());
 }
 
-void TouchToFillKeyboardSuppressor::OnContentAutofillDriverStateChanged(
-    ContentAutofillDriverFactory& factory,
-    ContentAutofillDriver& driver,
-    AutofillDriver::LifecycleState old_state,
-    AutofillDriver::LifecycleState new_state) {
-  switch (new_state) {
-    case AutofillDriver::LifecycleState::kInactive:
-    case AutofillDriver::LifecycleState::kActive:
-    case AutofillDriver::LifecycleState::kPendingReset:
-      break;
-    case AutofillDriver::LifecycleState::kPendingDeletion:
-      if (suppressed_manager_.get() == &driver.GetAutofillManager()) {
-        Unsuppress();
-      }
-      autofill_manager_observations_.RemoveObservation(
-          &driver.GetAutofillManager());
-      break;
-  }
-}
-
 void TouchToFillKeyboardSuppressor::OnAutofillManagerStateChanged(
     AutofillManager& manager,
     AutofillManager::LifecycleState old_state,
diff --git a/components/autofill/android/touch_to_fill_keyboard_suppressor.h b/components/autofill/android/touch_to_fill_keyboard_suppressor.h
index 616806a..a563f445 100644
--- a/components/autofill/android/touch_to_fill_keyboard_suppressor.h
+++ b/components/autofill/android/touch_to_fill_keyboard_suppressor.h
@@ -89,11 +89,6 @@
       ContentAutofillDriverFactory& factory) override;
   void OnContentAutofillDriverCreated(ContentAutofillDriverFactory& factory,
                                       ContentAutofillDriver& driver) override;
-  void OnContentAutofillDriverStateChanged(
-      ContentAutofillDriverFactory& factory,
-      ContentAutofillDriver& driver,
-      AutofillDriver::LifecycleState old_state,
-      AutofillDriver::LifecycleState new_state) override;
 
   // AutofillManager::Observer:
   void OnAutofillManagerStateChanged(
diff --git a/components/autofill/content/browser/scoped_autofill_managers_observation.cc b/components/autofill/content/browser/scoped_autofill_managers_observation.cc
index 3cf1348..ceb6891 100644
--- a/components/autofill/content/browser/scoped_autofill_managers_observation.cc
+++ b/components/autofill/content/browser/scoped_autofill_managers_observation.cc
@@ -78,6 +78,13 @@
     case AutofillDriver::LifecycleState::kPendingReset:
       break;
     case AutofillDriver::LifecycleState::kPendingDeletion:
+      // `AutofillDriverFactory::SetLifecycleStateAndNotifyObservers` first
+      // calls this method to remove the `AutofillManager` observer. Since
+      // `SetLifecycleStateAndNotifyObservers` would only then notify the
+      // `AutofillManager` about the state change, the observer needs to be
+      // notified now, before its removal.
+      autofill_manager_observations_.observer()->OnAutofillManagerStateChanged(
+          driver.GetAutofillManager(), old_state, new_state);
       autofill_manager_observations_.RemoveObservation(
           &driver.GetAutofillManager());
       break;
diff --git a/components/autofill/content/browser/scoped_autofill_managers_observation_unittest.cc b/components/autofill/content/browser/scoped_autofill_managers_observation_unittest.cc
index 452c61e..7de69fc 100644
--- a/components/autofill/content/browser/scoped_autofill_managers_observation_unittest.cc
+++ b/components/autofill/content/browser/scoped_autofill_managers_observation_unittest.cc
@@ -5,10 +5,12 @@
 #include "components/autofill/content/browser/scoped_autofill_managers_observation.h"
 
 #include "base/test/gtest_util.h"
+#include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/content/browser/test_autofill_client_injector.h"
 #include "components/autofill/content/browser/test_autofill_driver_injector.h"
 #include "components/autofill/content/browser/test_autofill_manager_injector.h"
 #include "components/autofill/content/browser/test_content_autofill_client.h"
+#include "components/autofill/core/browser/autofill_driver.h"
 #include "components/autofill/core/browser/mock_autofill_manager_observer.h"
 #include "components/autofill/core/browser/test_browser_autofill_manager.h"
 #include "content/public/test/navigation_simulator.h"
@@ -18,6 +20,7 @@
 
 namespace autofill {
 
+using ::testing::_;
 using ::testing::NiceMock;
 using ::testing::Ref;
 
@@ -152,4 +155,18 @@
       &AutofillManager::Observer::OnBeforeLanguageDetermined);
 }
 
+TEST_F(ScopedAutofillManagersObservationTest,
+       StateChangedToPendingDeletionNotifiesObserver) {
+  MockAutofillManagerObserver observer;
+  ScopedAutofillManagersObservation observation(&observer);
+  observation.Observe(web_contents());
+  NavigateAndCommit(GURL("https://a.com/"));
+
+  EXPECT_CALL(observer, OnAutofillManagerStateChanged(
+                            Ref(*manager(main_rfh())), _,
+                            AutofillDriver::LifecycleState::kPendingDeletion));
+  ContentAutofillDriverFactory::FromWebContents(web_contents())
+      ->RenderFrameDeleted(main_rfh());
+}
+
 }  // namespace autofill
diff --git a/components/autofill/content/common/mojom/autofill_agent.mojom b/components/autofill/content/common/mojom/autofill_agent.mojom
index 03fc5590..ce3909152 100644
--- a/components/autofill/content/common/mojom/autofill_agent.mojom
+++ b/components/autofill/content/common/mojom/autofill_agent.mojom
@@ -24,8 +24,8 @@
   // fields. `action_type` denotes if the action is a Fill or Undo.
   // `action_persistence` denotes if the operation is a Fill or Preview.
   ApplyFieldsAction(FormActionType action_type,
-                  ActionPersistence action_persistence,
-                  array<FormFieldData_FillData> fields);
+                    ActionPersistence action_persistence,
+                    array<FormFieldData_FillData> fields);
 
   // Fills or previews the field identified by `field` with `value`.
   // `action_persistence` denotes if the operation is a Fill or Preview.
@@ -61,24 +61,24 @@
   // its coordinates to be re-extracted.
   // Assuming the `field` is found, the driver's `AskForValuesToFill()` will be
   // called.
-  TriggerSuggestions(FieldRendererId field,
-                     AutofillSuggestionTriggerSource trigger_source);
+  TriggerSuggestions(
+      FieldRendererId field, AutofillSuggestionTriggerSource trigger_source);
 
   // Sets the autofill/autocomplete suggestion availability of `field` (if it is
   // still the currently selected node).
   SetSuggestionAvailability(
-    FieldRendererId field,
-    AutofillSuggestionAvailability suggestion_availability);
+      FieldRendererId field,
+      AutofillSuggestionAvailability suggestion_availability);
 
   // Sets the value of `field` (if it is still the currently selected node).
   // to the given data list value.
-  AcceptDataListSuggestion(FieldRendererId field,
-                           mojo_base.mojom.String16 value);
+  AcceptDataListSuggestion(
+      FieldRendererId field, mojo_base.mojom.String16 value);
 
   // Tells the renderer to preview the username and password with the given
   // values.
-  PreviewPasswordSuggestion(mojo_base.mojom.String16 username,
-                            mojo_base.mojom.String16 password);
+  PreviewPasswordSuggestion(
+      mojo_base.mojom.String16 username, mojo_base.mojom.String16 password);
 
   // Tells the renderer to preview the generated password.
   PreviewPasswordGenerationSuggestion(mojo_base.mojom.String16 password);
@@ -87,12 +87,12 @@
   // webpage. Used to check for the presence of the virtual card last four in
   // the DOM prior to offering CVC autofill for a virtual card saved on a
   // merchant website.
-  GetPotentialLastFourCombinationsForStandaloneCvc() =>
-      (array<string> potential_matches);
+  GetPotentialLastFourCombinationsForStandaloneCvc()
+      => (array<string> potential_matches);
 };
 
 // There is one instance of this interface per render frame in the render
-// process.
+// process. This renderer interface is called by the browser.
 interface PasswordAutofillAgent {
   // Provides fill information for a password form, which can fill the form and
   // prepare field autocomplete for multiple matching logins. Lets the renderer
@@ -100,17 +100,31 @@
   // the popup UI.
   SetPasswordFillData(PasswordFormFillData form_data);
 
-  // Tells the renderer to fill the username and password with with given
-  // values.
-  FillPasswordSuggestion(mojo_base.mojom.String16 username,
-                         mojo_base.mojom.String16 password);
+  // Fills the username and password with with given values.
+  FillPasswordSuggestion(
+      mojo_base.mojom.String16 username, mojo_base.mojom.String16 password);
 
-  // Lets the renderer know that there are no saved credentials for filling.
+  // Similar to FillPasswordSuggestion, but is also provided the
+  // FieldRendererIds of the elements to be filled.
+  FillPasswordSuggestionById(FieldRendererId username_element_id,
+                             FieldRendererId password_element_id,
+                             mojo_base.mojom.String16 username,
+                             mojo_base.mojom.String16 password);
+
+  // Previews the username and password, determined from the given
+  // FieldRendererIds, with the given values.
+  PreviewPasswordSuggestionById(FieldRendererId username_element_id,
+                                FieldRendererId password_element_id,
+                                mojo_base.mojom.String16 username,
+                                mojo_base.mojom.String16 password);
+
+  // Informs that there are no saved credentials for filling.
   // This is the "no results" equivalent of SetPasswordFillData.
   InformNoSavedCredentials(bool should_show_popup_without_passwords);
 
   // Fills the given `credential` into the last focused text input.
-  FillIntoFocusedField(bool is_password, mojo_base.mojom.String16 credential);
+  FillIntoFocusedField(
+      bool is_password, mojo_base.mojom.String16 credential);
 
   // Previews the given `value` into the field identified by `field_id`.
   PreviewField(FieldRendererId field_id, mojo_base.mojom.String16 value);
@@ -122,9 +136,10 @@
   // logging the decisions made about saving the password.
   SetLoggingState(bool active);
 
-  // Informs the renderer that the Keyboard Replacing Surface has been closed.
+  // Informs that the Keyboard Replacing Surface has been closed.
   // Indicates whether the virtual keyboard should be shown instead.
-  // TODO(crbug.com/40274966): Remove this API once PasswordSuggestionBottomSheetV2
+  // TODO(crbug.com/40274966): Remove this API once
+  // PasswordSuggestionBottomSheetV2
   // is launched.
   [EnableIf=is_android]
   KeyboardReplacingSurfaceClosed(bool show_virtual_keyboard);
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 0f65818..000aaca 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -48,6 +48,7 @@
 #include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "components/autofill/core/common/signatures.h"
+#include "components/autofill/core/common/unique_ids.h"
 #include "components/password_manager/core/common/password_manager_constants.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/password_manager/core/common/password_manager_util.h"
@@ -854,42 +855,64 @@
   if (!element || !IsElementEditable(element)) {
     return;
   }
-
   WebInputElement username_element;
   WebInputElement password_element;
   PasswordInfo* password_info = nullptr;
-
   if (!HasElementsToFill(element, UseFallbackData(true), &username_element,
                          &password_element, &password_info)) {
     return;
   }
-
-  ClearPreviewedForm();
-
   if (element.FormControlTypeForAutofill() == kInputPassword) {
     CHECK(password_element);
     password_info->password_field_suggestion_was_accepted = true;
     password_info->password_field = FieldRef(password_element);
   }
+  FillUsernameAndPasswordElements(username_element, password_element, username,
+                                  password);
+}
 
+void PasswordAutofillAgent::FillPasswordSuggestionById(
+    FieldRendererId username_element_id,
+    FieldRendererId password_element_id,
+    const std::u16string& username,
+    const std::u16string& password) {
+  WebInputElement username_element =
+      GetFormControlByRendererId(username_element_id)
+          .DynamicTo<WebInputElement>();
+  WebInputElement password_element =
+      GetFormControlByRendererId(password_element_id)
+          .DynamicTo<WebInputElement>();
+  bool is_password_field_focused =
+      password_element && password_element.Focused();
+  bool is_username_field_focused =
+      username_element && username_element.Focused();
+  if (!is_username_field_focused && !is_password_field_focused) {
+    return;
+  }
+  FillUsernameAndPasswordElements(username_element, password_element, username,
+                                  password);
+}
+
+void PasswordAutofillAgent::FillUsernameAndPasswordElements(
+    blink::WebInputElement username_element,
+    blink::WebInputElement password_element,
+    const std::u16string& username,
+    const std::u16string& password) {
+  ClearPreviewedForm();
   // Call OnFieldAutofilled before WebInputElement::SetAutofillState which may
   // cause frame closing.
   if (password_element && password_generation_agent_) {
     password_generation_agent_->OnFieldAutofilled(password_element);
   }
-
-  if (IsUsernameAmendable(
-          username_element,
-          element.FormControlTypeForAutofill() == kInputPassword) &&
-      !(username.empty() &&
-        element.FormControlTypeForAutofill() == kInputPassword) &&
+  bool is_password_field_focused =
+      password_element && password_element.Focused();
+  if (IsUsernameAmendable(username_element, is_password_field_focused) &&
+      !(username.empty() && is_password_field_focused) &&
       username_element.Value().Utf16() != username) {
     DoFillField(username_element, username);
   }
-
   if (password_element && IsElementEditable(password_element)) {
     FillPasswordFieldAndSave(password_element, password);
-
     // TODO(crbug.com/40223173): As Touch-To-Fill and auto-submission don't
     // currently support filling single username fields, the code below is
     // within `password_element`. Support such fields too and move the
@@ -905,9 +928,10 @@
       field_renderer_id_to_submit_ = GetFieldRendererId(password_element);
     }
   }
-
-  auto length = base::checked_cast<unsigned>(element.Value().length());
-  element.SetSelectionRange(length, length);
+  WebInputElement focused_element =
+      is_password_field_focused ? password_element : username_element;
+  auto length = base::checked_cast<unsigned>(focused_element.Value().length());
+  focused_element.SetSelectionRange(length, length);
 }
 
 void PasswordAutofillAgent::FillIntoFocusedField(
@@ -997,9 +1021,39 @@
                          &password_element, &password_info)) {
     return;
   }
-  if (IsUsernameAmendable(
-          username_element,
-          element.FormControlTypeForAutofill() == kInputPassword)) {
+  PreviewUsernameAndPasswordElements(username_element, password_element,
+                                     username, password);
+}
+
+void PasswordAutofillAgent::PreviewPasswordSuggestionById(
+    FieldRendererId username_element_id,
+    FieldRendererId password_element_id,
+    const std::u16string& username,
+    const std::u16string& password) {
+  WebInputElement username_element =
+      GetFormControlByRendererId(username_element_id)
+          .DynamicTo<WebInputElement>();
+  WebInputElement password_element =
+      GetFormControlByRendererId(password_element_id)
+          .DynamicTo<WebInputElement>();
+  bool is_password_field_focused =
+      password_element && password_element.Focused();
+  bool is_username_field_focused =
+      username_element && username_element.Focused();
+  if (!is_username_field_focused && !is_password_field_focused) {
+    return;
+  }
+  PreviewUsernameAndPasswordElements(username_element, password_element,
+                                     username, password);
+}
+
+void PasswordAutofillAgent::PreviewUsernameAndPasswordElements(
+    blink::WebInputElement username_element,
+    blink::WebInputElement password_element,
+    const std::u16string& username,
+    const std::u16string& password) {
+  if (IsUsernameAmendable(username_element,
+                          password_element && password_element.Focused())) {
     DoPreviewField(username_element, username, /*is_password=*/false);
   }
   if (password_element && IsElementEditable(password_element)) {
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index fb6097a..1b364db 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -141,6 +141,14 @@
   void SetPasswordFillData(const PasswordFormFillData& form_data) override;
   void FillPasswordSuggestion(const std::u16string& username,
                               const std::u16string& password) override;
+  void FillPasswordSuggestionById(FieldRendererId username_element_id,
+                                  FieldRendererId password_element_id,
+                                  const std::u16string& username,
+                                  const std::u16string& password) override;
+  void PreviewPasswordSuggestionById(FieldRendererId username_element_id,
+                                     FieldRendererId password_element_id,
+                                     const std::u16string& username,
+                                     const std::u16string& password) override;
   void InformNoSavedCredentials(
       bool should_show_popup_without_passwords) override;
   void FillIntoFocusedField(bool is_password,
@@ -428,6 +436,21 @@
   void DoFillField(blink::WebInputElement input,
                    const std::u16string& credential);
 
+  // Given `username_element` and `password_element`, previews `username` and
+  // `password` respectively into them.
+  void PreviewUsernameAndPasswordElements(
+      blink::WebInputElement username_element,
+      blink::WebInputElement password_element,
+      const std::u16string& username,
+      const std::u16string& password);
+
+  // Given `username_element` and `password_element`, fills `username` and
+  // `password` respectively into them.
+  void FillUsernameAndPasswordElements(blink::WebInputElement username_element,
+                                       blink::WebInputElement password_element,
+                                       const std::u16string& username,
+                                       const std::u16string& password);
+
   // Uses `FillField` to fill the given `credential` into the `password_input`.
   // Saves the password for its associated form.
   void FillPasswordFieldAndSave(blink::WebInputElement password_input,
diff --git a/components/autofill/core/browser/autofill_client.cc b/components/autofill/core/browser/autofill_client.cc
index 88fc4de..fe4dbde1 100644
--- a/components/autofill/core/browser/autofill_client.cc
+++ b/components/autofill/core/browser/autofill_client.cc
@@ -152,6 +152,11 @@
   return std::nullopt;
 }
 
+std::optional<AutofillClient::SuggestionUiSessionId>
+AutofillClient::GetSessionIdForCurrentAutofillSuggestions() const {
+  return std::nullopt;
+}
+
 base::span<const Suggestion> AutofillClient::GetAutofillSuggestions() const {
   NOTIMPLEMENTED();
   return {};
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index be3e1d1..7aec68c 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -16,6 +16,7 @@
 #include "base/i18n/rtl.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/types/id_type.h"
 #include "base/types/optional_ref.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_trigger_details.h"
@@ -345,11 +346,25 @@
       bool is_migration_to_account,
       AddressProfileSavePromptCallback callback) = 0;
 
+  // A unique identifier for suggestions UI (i.e. the keyboard accessory on
+  // mobile and the popup on Desktop). Calling `ShowAutofillSuggestions`
+  // generates a new identifier, but calling `UpdateAutofillSuggestions` does
+  // not. Therefore the identifier can be used to decide whether to update or
+  // close suggestions UI in asynchronous execution flows. There is at most one
+  // suggestion UI showing at a time.
+  using SuggestionUiSessionId =
+      base::IdTypeU32<struct SuggestionUiSessionIdTag>;
+
   // Shows Autofill suggestions with the given `values`, `labels`, `icons`, and
   // `identifiers` for the element at `element_bounds`. `delegate` will be
   // notified of suggestion events, e.g., the user accepting a suggestion.
-  // The suggestions are shown asynchronously on Desktop and Android.
-  virtual void ShowAutofillSuggestions(
+  // Note that suggestions are shown asynchronously on Desktop and Android. As a
+  // result, calling `GetSessionIdForCurrentAutofillSuggestions` directly after
+  // this method will return not return the same identifier, since the UI is not
+  // showing yet.
+  // `SuggestionUiSessionId` is only implemented on Chrome for Desktop and
+  // Android. On other platforms, the returned identifier is meaningless.
+  virtual SuggestionUiSessionId ShowAutofillSuggestions(
       const PopupOpenArgs& open_args,
       base::WeakPtr<AutofillSuggestionDelegate> delegate) = 0;
 
@@ -365,6 +380,11 @@
   // showing. Note that this implemented only on Desktop.
   virtual std::optional<PopupScreenLocation> GetPopupScreenLocation() const;
 
+  // Returns the identifier of the suggestion UI that is currently showing or
+  // `std::nullopt` is there is none.
+  virtual std::optional<SuggestionUiSessionId>
+  GetSessionIdForCurrentAutofillSuggestions() const;
+
   // Returns (not elided) suggestions currently held by the UI.
   virtual base::span<const Suggestion> GetAutofillSuggestions() const;
 
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index d7621d7..4f4c720 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -428,13 +428,8 @@
          GetFillingProductFromSuggestionType(suggestions[0].type) !=
              FillingProduct::kPassword);
 
-  std::vector<SuggestionType> shown_suggestion_types;
-  // TODO(crbug.com/362630793): Use a `DenseSet` instead of a vector.
-  shown_suggestion_types.reserve(suggestions.size());
-  base::ranges::transform(suggestions,
-                          std::back_insert_iterator(shown_suggestion_types),
-                          &Suggestion::type);
-
+  const DenseSet<SuggestionType> shown_suggestion_types(suggestions,
+                                                        &Suggestion::type);
   const bool has_autofill_suggestions = std::ranges::any_of(
       shown_suggestion_types, IsAutofillAndFirstLayerSuggestionId);
 
@@ -456,8 +451,8 @@
   } else if (has_autofill_suggestions) {
     OnAutofillAvailabilityEvent(
         mojom::AutofillSuggestionAvailability::kAutofillAvailable);
-    if (base::Contains(shown_suggestion_types,
-                       SuggestionType::kDevtoolsTestAddresses)) {
+    if (shown_suggestion_types.contains(
+            SuggestionType::kDevtoolsTestAddresses)) {
       autofill_metrics::OnDevtoolsTestAddressesShown();
     }
   } else {
@@ -467,8 +462,7 @@
     // entries.
     OnAutofillAvailabilityEvent(
         mojom::AutofillSuggestionAvailability::kAutocompleteAvailable);
-    if (base::Contains(shown_suggestion_types,
-                       SuggestionType::kAutocompleteEntry)) {
+    if (shown_suggestion_types.contains(SuggestionType::kAutocompleteEntry)) {
       AutofillMetrics::OnAutocompleteSuggestionsShown();
     }
   }
@@ -476,8 +470,7 @@
   manager_->DidShowSuggestions(shown_suggestion_types, query_form_,
                                query_field_);
 
-  if (base::Contains(shown_suggestion_types,
-                     SuggestionType::kShowAccountCards)) {
+  if (shown_suggestion_types.contains(SuggestionType::kShowAccountCards)) {
     autofill_metrics::LogAutofillShowCardsFromGoogleAccountButtonEventMetric(
         autofill_metrics::ShowCardsFromGoogleAccountButtonEvent::
             kButtonAppeared);
@@ -488,7 +481,7 @@
     }
   }
 
-  if (base::Contains(shown_suggestion_types, SuggestionType::kScanCreditCard)) {
+  if (shown_suggestion_types.contains(SuggestionType::kScanCreditCard)) {
     AutofillMetrics::LogScanCreditCardPromptMetric(
         AutofillMetrics::SCAN_CARD_ITEM_SHOWN);
   }
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index d1679ed..650ddeb 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -24,6 +24,7 @@
 #include "base/uuid.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/address_data_manager.h"
+#include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_compose_delegate.h"
 #include "components/autofill/core/browser/autofill_experiments.h"
 #include "components/autofill/core/browser/autofill_form_test_utils.h"
@@ -84,6 +85,7 @@
 using ::testing::_;
 using ::testing::AllOf;
 using ::testing::AnyOf;
+using ::testing::DoAll;
 using ::testing::ElementsAre;
 using ::testing::Field;
 using ::testing::InSequence;
@@ -92,6 +94,7 @@
 using ::testing::NiceMock;
 using ::testing::Property;
 using ::testing::Return;
+using ::testing::SaveArg;
 using ::testing::SizeIs;
 using ::testing::StartsWith;
 
@@ -204,10 +207,10 @@
   }
   MockAutofillClient(const MockAutofillClient&) = delete;
   MockAutofillClient& operator=(const MockAutofillClient&) = delete;
-  MOCK_METHOD(void,
+  MOCK_METHOD(AutofillClient::SuggestionUiSessionId,
               ShowAutofillSuggestions,
-              (const autofill::AutofillClient::PopupOpenArgs& open_args,
-               base::WeakPtr<AutofillSuggestionDelegate> delegate),
+              (const autofill::AutofillClient::PopupOpenArgs&,
+               base::WeakPtr<AutofillSuggestionDelegate>),
               (override));
   MOCK_METHOD(void,
               UpdateAutofillSuggestions,
@@ -1074,7 +1077,8 @@
 
   AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(client(), ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(DoAll(SaveArg<0>(&open_args),
+                      Return(AutofillClient::SuggestionUiSessionId())));
 
   // This should call ShowAutofillSuggestions.
   std::vector<Suggestion> autofill_item;
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 62a5fee..6c7823e 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -1970,19 +1970,18 @@
 }
 
 void BrowserAutofillManager::DidShowSuggestions(
-    base::span<const SuggestionType> shown_suggestions_types,
+    DenseSet<SuggestionType> shown_suggestion_types,
     const FormData& form,
     const FormFieldData& field) {
   NotifyObservers(&Observer::OnSuggestionsShown);
 
-  bool has_autofill_suggestions = std::ranges::any_of(
-      shown_suggestions_types,
-      AutofillExternalDelegate::IsAutofillAndFirstLayerSuggestionId);
-  if (!has_autofill_suggestions) {
+  if (!std::ranges::any_of(
+          shown_suggestion_types,
+          AutofillExternalDelegate::IsAutofillAndFirstLayerSuggestionId)) {
     return;
   }
 
-  if (base::Contains(shown_suggestions_types, FillingProduct::kCreditCard,
+  if (base::Contains(shown_suggestion_types, FillingProduct::kCreditCard,
                      GetFillingProductFromSuggestionType) &&
       IsCreditCardFidoAuthenticationEnabled()) {
     GetCreditCardAccessManager().PrepareToFetchCreditCard();
@@ -2000,13 +1999,13 @@
   // not mess with the current denominator (classified forms).
   const bool is_address_manual_fallback_on_non_address_field =
       std::ranges::any_of(
-          shown_suggestions_types, [autofill_field](SuggestionType type) {
+          shown_suggestion_types, [autofill_field](SuggestionType type) {
             return IsAddressAutofillManuallyTriggeredOnNonAddressField(
                 type, autofill_field);
           });
   const bool is_payments_manual_fallback_on_non_payments_field =
       std::ranges::any_of(
-          shown_suggestions_types, [autofill_field](SuggestionType type) {
+          shown_suggestion_types, [autofill_field](SuggestionType type) {
             return IsCreditCardAutofillManuallyTriggeredOnNonCreditCardField(
                 type, autofill_field);
           });
@@ -2728,12 +2727,15 @@
           case FieldTypeGroup::kUnfillable:
           case FieldTypeGroup::kIban:
           case FieldTypeGroup::kNoGroup:
+          case FieldTypeGroup::kPredictionImprovements:
             // Since we early return on non-address types.
             NOTREACHED();
         }
         NOTREACHED();
       case SuggestionType::kAddressFieldByFieldFilling:
         return SuggestionType::kAddressFieldByFieldFilling;
+      case SuggestionType::kFillPredictionImprovements:
+        return SuggestionType::kFillPredictionImprovements;
       default:
         // `last_suggestion_type` is only one of the address filling suggestion
         // types, therefore no other type should be passed to this function.
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index 99c5161..ac636c0 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -164,7 +164,7 @@
                             const FormFieldData& trigger_field);
   // Virtual for testing
   virtual void DidShowSuggestions(
-      base::span<const SuggestionType> shown_suggestions_types,
+      DenseSet<SuggestionType> shown_suggestion_types,
       const FormData& form,
       const FormFieldData& field);
 
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index 42d243d..7900ec82 100644
--- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -910,8 +910,8 @@
       const FormData& form,
       size_t field_index = 0,
       SuggestionType type = SuggestionType::kAddressEntry) {
-    browser_autofill_manager_->DidShowSuggestions(
-        std::vector<SuggestionType>({type}), form, form.fields()[field_index]);
+    browser_autofill_manager_->DidShowSuggestions({type}, form,
+                                                  form.fields()[field_index]);
   }
 
   void TryToShowTouchToFill(const FormData& form,
@@ -6117,8 +6117,7 @@
 
   base::HistogramTester histogram_tester;
   browser_autofill_manager_->DidShowSuggestions(
-      std::vector<SuggestionType>({SuggestionType::kAutocompleteEntry}), form,
-      form.fields().back());
+      {SuggestionType::kAutocompleteEntry}, form, form.fields().back());
   // No Autofill logs.
   const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
   EXPECT_THAT(histograms,
@@ -6733,8 +6732,7 @@
   EXPECT_CALL(cc_access_manager(), PrepareToFetchCreditCard)
       .Times(IsCreditCardFidoAuthenticationEnabled() ? 1 : 0);
   browser_autofill_manager_->DidShowSuggestions(
-      std::vector<SuggestionType>({SuggestionType::kCreditCardEntry}), form,
-      form.fields()[0]);
+      {SuggestionType::kCreditCardEntry}, form, form.fields()[0]);
 }
 
 TEST_F(BrowserAutofillManagerTest,
@@ -6743,9 +6741,8 @@
   FormsSeen({form});
 
   EXPECT_CALL(cc_access_manager(), PrepareToFetchCreditCard).Times(0);
-  browser_autofill_manager_->DidShowSuggestions(
-      std::vector<SuggestionType>({SuggestionType::kAddressEntry}), form,
-      form.fields()[0]);
+  browser_autofill_manager_->DidShowSuggestions({SuggestionType::kAddressEntry},
+                                                form, form.fields()[0]);
 }
 
 TEST_F(BrowserAutofillManagerTest, PageLanguageGetsCorrectlySet) {
diff --git a/components/autofill/core/browser/data_model/autofill_i18n_api.cc b/components/autofill/core/browser/data_model/autofill_i18n_api.cc
index 2a7adb8..93dd87d 100644
--- a/components/autofill/core/browser/data_model/autofill_i18n_api.cc
+++ b/components/autofill/core/browser/data_model/autofill_i18n_api.cc
@@ -203,6 +203,7 @@
     case ONE_TIME_CODE:
     case SINGLE_USERNAME_FORGOT_PASSWORD:
     case SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES:
+    case IMPROVED_PREDICTION:
     case MAX_VALID_FIELD_TYPE:
       return nullptr;
   }
diff --git a/components/autofill/core/browser/data_model/autofill_profile.cc b/components/autofill/core/browser/data_model/autofill_profile.cc
index a2d4cfcd..d75eea1 100644
--- a/components/autofill/core/browser/data_model/autofill_profile.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile.cc
@@ -534,7 +534,7 @@
 
   // When adding field types, ensure that they don't need to be added here and
   // update the last checked value.
-  static_assert(FieldType::MAX_VALID_FIELD_TYPE == 162,
+  static_assert(FieldType::MAX_VALID_FIELD_TYPE == 163,
                 "New field type needs to be reviewed for inclusion in the "
                 "profile comparison logic.");
 
@@ -1131,6 +1131,7 @@
     case FieldTypeGroup::kTransaction:
     case FieldTypeGroup::kStandaloneCvcField:
     case FieldTypeGroup::kUnfillable:
+    case FieldTypeGroup::kPredictionImprovements:
       return nullptr;
   }
   NOTREACHED_IN_MIGRATION();
@@ -1244,6 +1245,7 @@
     case FieldTypeGroup::kUnfillable:
     case FieldTypeGroup::kIban:
     case FieldTypeGroup::kStandaloneCvcField:
+    case FieldTypeGroup::kPredictionImprovements:
       NOTREACHED();
   }
   NOTREACHED();
diff --git a/components/autofill/core/browser/field_type_utils.cc b/components/autofill/core/browser/field_type_utils.cc
index d533af0e..3711211 100644
--- a/components/autofill/core/browser/field_type_utils.cc
+++ b/components/autofill/core/browser/field_type_utils.cc
@@ -93,6 +93,7 @@
     case FieldTypeGroup::kTransaction:
     case FieldTypeGroup::kIban:
     case FieldTypeGroup::kStandaloneCvcField:
+    case FieldTypeGroup::kPredictionImprovements:
       return false;
   }
   NOTREACHED();
diff --git a/components/autofill/core/browser/field_types.cc b/components/autofill/core/browser/field_types.cc
index 244ff83..8fd9b17 100644
--- a/components/autofill/core/browser/field_types.cc
+++ b/components/autofill/core/browser/field_types.cc
@@ -140,7 +140,8 @@
          {"ADDRESS_HOME_STREET_LOCATION_AND_LANDMARK",
           ADDRESS_HOME_STREET_LOCATION_AND_LANDMARK},
          {"ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK",
-          ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK}});
+          ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK},
+         {"IMPROVED_PREDICTION", IMPROVED_PREDICTION}});
 
 bool IsFillableFieldType(FieldType field_type) {
   switch (field_type) {
@@ -240,6 +241,9 @@
     case NOT_USERNAME:
       return false;
 
+    case IMPROVED_PREDICTION:
+      return false;
+
     // Credential field types that the server should never return as
     // classifications.
     case NOT_ACCOUNT_CREATION_PASSWORD:
@@ -454,6 +458,8 @@
     case CREDIT_CARD_STANDALONE_VERIFICATION_CODE:
     case ONE_TIME_CODE:
       return "One time code";
+    case IMPROVED_PREDICTION:
+      return "Improved prediction";
     case MAX_VALID_FIELD_TYPE:
       return "";
   }
@@ -558,6 +564,9 @@
     case COMPANY_NAME:
       return FieldTypeGroup::kCompany;
 
+    case IMPROVED_PREDICTION:
+      return FieldTypeGroup::kPredictionImprovements;
+
     case PASSWORD:
     case ACCOUNT_CREATION_PASSWORD:
     case NOT_ACCOUNT_CREATION_PASSWORD:
diff --git a/components/autofill/core/browser/field_types.h b/components/autofill/core/browser/field_types.h
index 13f8b08..7ae7629 100644
--- a/components/autofill/core/browser/field_types.h
+++ b/components/autofill/core/browser/field_types.h
@@ -456,6 +456,13 @@
   // classification for the field.
   // SERVER_RESPONSE_PENDING = 161;
 
+  // Improved Prediction indicates that this field is support by the predition
+  // improvement system.
+  // This type is a metatype and does not correspond to a specific sort of
+  // data.
+  // It should not take precedence over existing types.
+  IMPROVED_PREDICTION = 162,
+
   // No new types can be added without a corresponding change to the Autofill
   // server.
   // This enum must be kept in sync with FieldType from
@@ -466,7 +473,7 @@
   // If the newly added type is a storable type of AutofillProfile, update
   // AutofillProfile.StorableTypes in
   // tools/metrics/histograms/metadata/autofill/histograms.xml.
-  MAX_VALID_FIELD_TYPE = 162,
+  MAX_VALID_FIELD_TYPE = 163,
 };
 // LINT.ThenChange(//chrome/common/extensions/api/autofill_private.idl)
 
@@ -484,7 +491,8 @@
   kUnfillable,
   kIban,
   kStandaloneCvcField,
-  kMaxValue = kStandaloneCvcField,
+  kPredictionImprovements,
+  kMaxValue = kPredictionImprovements,
 };
 
 template <>
diff --git a/components/autofill/core/browser/field_types_unittest.cc b/components/autofill/core/browser/field_types_unittest.cc
index 970f986..2cbd2ab 100644
--- a/components/autofill/core/browser/field_types_unittest.cc
+++ b/components/autofill/core/browser/field_types_unittest.cc
@@ -117,7 +117,8 @@
       ADDRESS_HOME_STREET_LOCATION_AND_LANDMARK,
       ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK,
       SINGLE_USERNAME_FORGOT_PASSWORD,
-      SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES};
+      SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES,
+      IMPROVED_PREDICTION};
   FieldType kInvalidValue = static_cast<FieldType>(123456);
   ASSERT_FALSE(kValidFieldTypes.count(kInvalidValue));
   for (int i = -10; i < MAX_VALID_FIELD_TYPE + 10; ++i) {
diff --git a/components/autofill/core/browser/filling_product.cc b/components/autofill/core/browser/filling_product.cc
index d6a5df9..0c179be 100644
--- a/components/autofill/core/browser/filling_product.cc
+++ b/components/autofill/core/browser/filling_product.cc
@@ -133,6 +133,8 @@
       return FillingProduct::kPassword;
     case FieldTypeGroup::kIban:
       return FillingProduct::kIban;
+    case autofill::FieldTypeGroup::kPredictionImprovements:
+      return FillingProduct::kPredictionImprovements;
   }
   NOTREACHED();
 }
diff --git a/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc b/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc
index fd72d452..926f4d4 100644
--- a/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc
+++ b/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc
@@ -663,6 +663,7 @@
     case PRICE:
     case NUMERIC_QUANTITY:
     case SEARCH_TERM:
+    case IMPROVED_PREDICTION:
     case MAX_VALID_FIELD_TYPE:
       return std::nullopt;
   }
diff --git a/components/autofill/core/browser/form_types.cc b/components/autofill/core/browser/form_types.cc
index 20590015..3517b754 100644
--- a/components/autofill/core/browser/form_types.cc
+++ b/components/autofill/core/browser/form_types.cc
@@ -30,6 +30,7 @@
     case FieldTypeGroup::kNoGroup:
     case FieldTypeGroup::kTransaction:
     case FieldTypeGroup::kUnfillable:
+    case FieldTypeGroup::kPredictionImprovements:
       return FormType::kUnknownFormType;
   }
 }
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc
index 4d4874e..9d0be4d 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -149,6 +149,7 @@
   GROUP_ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK = 45,
   GROUP_ADDRESS_HOME_HOUSE_NUMBER_AND_APT = 46,
   GROUP_STANDALONE_CREDIT_CARD_VERIFICATION = 47,
+  GROUP_PREDICTION_IMPROVEMENTS = 48,
   // Note: if adding an enum value here, run
   // tools/metrics/histograms/update_autofill_enums.py
   NUM_FIELD_TYPE_GROUPS_FOR_METRICS
@@ -252,6 +253,10 @@
       group = GROUP_IBAN;
       break;
 
+    case FieldTypeGroup::kPredictionImprovements:
+      group = GROUP_PREDICTION_IMPROVEMENTS;
+      break;
+
     case FieldTypeGroup::kAddress:
       switch (field_type) {
         case ADDRESS_HOME_LINE1:
@@ -411,6 +416,7 @@
         case CREDIT_CARD_STANDALONE_VERIFICATION_CODE:
         case SINGLE_USERNAME_FORGOT_PASSWORD:
         case SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES:
+        case IMPROVED_PREDICTION:
           NOTREACHED_IN_MIGRATION()
               << field_type << " type is not in that group.";
           group = GROUP_AMBIGUOUS;
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_test_base.h b/components/autofill/core/browser/metrics/autofill_metrics_test_base.h
index b7cab8eb..3b7422f6 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_test_base.h
+++ b/components/autofill/core/browser/metrics/autofill_metrics_test_base.h
@@ -194,9 +194,8 @@
       const FormData& form,
       size_t field_index = 0,
       SuggestionType suggestion_type = SuggestionType::kAddressEntry) {
-    autofill_manager().DidShowSuggestions(
-        std::vector<SuggestionType>({suggestion_type}), form,
-        form.fields()[field_index]);
+    autofill_manager().DidShowSuggestions({suggestion_type}, form,
+                                          form.fields()[field_index]);
   }
 
   void FillTestProfile(const FormData& form) {
diff --git a/components/autofill/core/browser/metrics/form_events/form_event_logger_base_unittest.cc b/components/autofill/core/browser/metrics/form_events/form_event_logger_base_unittest.cc
index ae35327c..7b649d9 100644
--- a/components/autofill/core/browser/metrics/form_events/form_event_logger_base_unittest.cc
+++ b/components/autofill/core/browser/metrics/form_events/form_event_logger_base_unittest.cc
@@ -290,9 +290,7 @@
   // The manual fallback code assumes that suggestions have been shown before
   // they can be filled. Not showing them will result in a crash.
   autofill_manager().DidShowSuggestions(
-      std::vector<SuggestionType>{
-          SuggestionType::kCreditCardFieldByFieldFilling},
-      form, form.fields()[0]);
+      {SuggestionType::kCreditCardFieldByFieldFilling}, form, form.fields()[0]);
   autofill_manager().FillOrPreviewProfileForm(
       mojom::ActionPersistence::kFill, form, form.fields()[0],
       test::GetFullProfile(),
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index 4369709..3caeec7 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -254,10 +254,12 @@
       AutofillClient::AddressProfileDeleteDialogCallback delete_dialog_callback)
       override {}
 
-  void ShowAutofillSuggestions(
+  AutofillClient::SuggestionUiSessionId ShowAutofillSuggestions(
       const AutofillClient::PopupOpenArgs& open_args,
       base::WeakPtr<AutofillSuggestionDelegate> delegate) override {
     is_showing_popup_ = true;
+    static AutofillClient::SuggestionUiSessionId::Generator generator;
+    return generator.GenerateNextId();
   }
 
   void UpdateAutofillDataListValues(
diff --git a/components/autofill/core/browser/test_autofill_manager_waiter.h b/components/autofill/core/browser/test_autofill_manager_waiter.h
index 77939c4..16c93a7 100644
--- a/components/autofill/core/browser/test_autofill_manager_waiter.h
+++ b/components/autofill/core/browser/test_autofill_manager_waiter.h
@@ -72,17 +72,25 @@
 // In browser tests, it is important to create the waiter soon enough and not
 // create it between two On{Before,After}Foo() events:
 //
-//   class TestAutofillManager : public BrowserAutofillManager {
+//   class MyAutofillManager : public BrowserAutofillManager {
 //    public:
 //     ...
-//     TestAutofillManagerWaiter waiter(manager,
-//                                      {AutofillManagerEvent::kFoo,
-//                                       AutofillManagerEvent::kBar,
-//                                       ...});
+//     TestAutofillManagerWaiter waiter_{*this,
+//                                       {AutofillManagerEvent::kFormsSeen,
+//                                        ...}};
 //   };
-//   TestAutofillManagerInjector<TestAutofillManager> injector;
-//   ... trigger events ...
-//   ASSERT_TRUE(injector[main_rfh()].waiter.Wait());  // Blocks.
+//
+//   class MyFixture : public InProcessBrowserTest {
+//    public:
+//     ...
+//     TestAutofillManagerInjector<MyAutofillManager> injector_;
+//   };
+//
+//   TEST_F(MyFixture, MyTest) {
+//     NavigateToUrl("https://foo.com");
+//     ASSERT_TRUE(injector_[main_rfh()].waiter_.Wait(
+//         /*num_expected_relevant_events=*/1));
+//   }
 //
 // In case of failure, the error message of Wait() informs about the pending
 // OnAfterFoo() calls.
@@ -99,7 +107,8 @@
   ~TestAutofillManagerWaiter() override;
 
   // Blocks until all pending OnAfterFoo() events have been observed and at
-  // least `num_expected_relevant_events` relevant events have been observed.
+  // least `num_expected_relevant_events` relevant events have been observed
+  // since the waiter's creation or last Reset().
   //
   // Since the asynchronous-parsing task runner in AutofillManager has
   // relatively low priority, a high timeout may be necessary on slow bots.
@@ -109,6 +118,17 @@
       const base::Location& location = FROM_HERE);
 
   // Equivalent to re-initialization.
+  //
+  // A waiter must be reset only if all pending OnAfterEvents() events have been
+  // observed, as is the case after Wait(). Therefore, the following pattern is
+  // valid:
+  //
+  //   TestAutofillManagerWaiter waiter(manager, {AutofillManagerEvent::kFoo});
+  //   TriggerFoo();
+  //   ASSERT_TRUE(waiter.Wait());
+  //   waiter.Reset();
+  //   TriggerFoo();
+  //   ASSERT_TRUE(waiter.Wait());
   void Reset();
 
  private:
@@ -264,12 +284,15 @@
 //
 // Typical usage is as follows:
 //
-//   TestAutofillManagerSingleEventWaiter waiter(
-//      *autofill_manager,
-//      &AutofillManager::Observer::OnFillOrPreviewDataModelForm,
-//      _, mojom::ActionPersistence::kPreview, _, _);
-//   ...
-//   EXPECT_TRUE(std::move(waiter).Wait());
+//   TEST_F(MyFixture, MyTest) {
+//     ...
+//     TestAutofillManagerSingleEventWaiter waiter(
+//        *autofill_manager,
+//        &AutofillManager::Observer::OnFillOrPreviewDataModelForm,
+//        _, mojom::ActionPersistence::kPreview, _, _);
+//     ...
+//     EXPECT_TRUE(std::move(waiter).Wait());
+//   }
 class TestAutofillManagerSingleEventWaiter {
  public:
   // Creates a waiter for `event` without matchers.
diff --git a/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc b/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc
index aa000a22..9ffd288 100644
--- a/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc
+++ b/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc
@@ -562,7 +562,7 @@
 
   // When adding field types, ensure that they don't need to be added here and
   // update the last checked value.
-  static_assert(FieldType::MAX_VALID_FIELD_TYPE == 162,
+  static_assert(FieldType::MAX_VALID_FIELD_TYPE == 163,
                 "New field type needs to be reviewed for inclusion in sync");
 
   // The profile may be in a legacy state. By calling |FinalizeAfterImport()|
diff --git a/components/autofill/core/common/unique_ids.h b/components/autofill/core/common/unique_ids.h
index 416adc2..5d2fd48 100644
--- a/components/autofill/core/common/unique_ids.h
+++ b/components/autofill/core/common/unique_ids.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/types/id_type.h"
+#include "base/types/strong_alias.h"
 #include "base/unguessable_token.h"
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
diff --git a/components/commerce_strings.grdp b/components/commerce_strings.grdp
index 3ab6f7f..491fb1b 100644
--- a/components/commerce_strings.grdp
+++ b/components/commerce_strings.grdp
@@ -332,9 +332,6 @@
     <message name="IDS_DISCOUNT_EXPIRATION_DATE" desc="Text shown in the discount dialog to tell users the discount expiration date.">
       Valid until <ph name="DATE">$1<ex>2023-01-01</ex></ph>.
     </message>
-    <message name="IDS_DISCOUNT_CODE_COPY_BUTTON_TEXT" desc="The copy button text. User uses this button to copy the discount code, so that they can paste the code during checkout.">
-      Copy
-    </message>
     <message name="IDS_TWO_STRINGS_CONNECTOR" desc="This is used to connect two sentences together by using a ending period and a space. The first sentence does not contain an ending period, but the second sentence will.">
       <ph name="FIRST_STRING">$1<ex>Use this code at checkout</ex></ph>. <ph name="SECOND_STRING">$2<ex>Valid until Aug 15 2023.</ex></ph>
     </message>
@@ -356,16 +353,6 @@
     <message name="IDS_DISCOUNTS_COUPON_CODE_BUTTON_TOOLTIP_CLICKED" desc="The tooltip message for a button containing a copyable coupon code in the discounts bubble on Desktop. This message is shown when the button is hovered over and if the button has been clicked.">
       Copied to clipboard
     </message>
-    <if expr="use_titlecase">
-      <message name="IDS_DISCOUNT_ICON_EXPANDED_TEXT" desc="The text to use when the icon is expanded in the location bar, indicating discount has been found for the viewing product.">
-        Discount Found
-      </message>
-    </if> <!-- use_titlecase -->
-    <if expr="not use_titlecase">
-      <message name="IDS_DISCOUNT_ICON_EXPANDED_TEXT" desc="The text to use when the icon is expanded in the location bar, indicating discount has been found for the viewing product.">
-        Discount found
-      </message>
-    </if> <!-- not use_titlecase -->
   </if>
 
   <!-- Android only -->
@@ -530,6 +517,30 @@
     Shopping list
   </message>
 
+  <!-- For Discounts -->
+  <message name="IDS_DISCOUNT_CODE_COPY_BUTTON_TEXT" desc="The copy button text. User uses this button to copy the discount code, so that they can paste the code during checkout.">
+      Copy
+  </message>
+  <if expr="use_titlecase">
+    <message name="IDS_DISCOUNT_ICON_EXPANDED_TEXT" desc="The text to use when the icon is expanded in the location bar, indicating discount has been found for the viewing product.">
+      Discount Found
+    </message>
+  </if> <!-- use_titlecase -->
+  <if expr="not use_titlecase">
+    <message name="IDS_DISCOUNT_ICON_EXPANDED_TEXT" desc="The text to use when the icon is expanded in the location bar, indicating discount has been found for the viewing product.">
+      Discount found
+    </message>
+  </if> <!-- not use_titlecase -->
+    <!-- Discounts Android only -->
+  <if expr="is_android">
+    <message name="IDS_DISCOUNT_CODE_COPIED_BUTTON_TEXT" desc="The copy button text. User uses this button to copy the discount code, so that they can paste the code during checkout. This text will show on the button after user has clicked on the copy button to indicate that the discount code has been copied.">
+      Copied
+    </message>
+    <message name="IDS_DISCOUNT_EXPIRATION_DATE_ANDROID" desc="Text shown in the discount dialog to tell users the discount expiration date.">
+      Valid until <ph name="DATE">$1<ex>2023-01-01</ex></ph>
+    </message>
+    </if> <!-- is_android -->
+
   <!-- For Compare -->
   <message name="IDS_COMPARE_CITATION_A11Y_LABEL" desc="The accessibility label for a citation in the compare feature's UI.">
     Citation <ph name="CURRENT_CITATION">$1<ex>1</ex></ph> of <ph name="MAX_CITATIONS">$2<ex>3</ex></ph>, <ph name="PRODUCT_NAME">$3<ex>Laptop</ex></ph>, <ph name="URL">$4<ex>http://example.com</ex></ph>
diff --git a/components/commerce_strings_grdp/IDS_DISCOUNT_CODE_COPIED_BUTTON_TEXT.png.sha1 b/components/commerce_strings_grdp/IDS_DISCOUNT_CODE_COPIED_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..ba370c4
--- /dev/null
+++ b/components/commerce_strings_grdp/IDS_DISCOUNT_CODE_COPIED_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+225e812da79409dbe9e5e58434e280a3cd1baa94
\ No newline at end of file
diff --git a/components/commerce_strings_grdp/IDS_DISCOUNT_EXPIRATION_DATE_ANDROID.png.sha1 b/components/commerce_strings_grdp/IDS_DISCOUNT_EXPIRATION_DATE_ANDROID.png.sha1
new file mode 100644
index 0000000..4c9c262
--- /dev/null
+++ b/components/commerce_strings_grdp/IDS_DISCOUNT_EXPIRATION_DATE_ANDROID.png.sha1
@@ -0,0 +1 @@
+16b11e9ba6cb65029fbc1e14c3a97f59cb64e60c
\ No newline at end of file
diff --git a/components/constrained_window/BUILD.gn b/components/constrained_window/BUILD.gn
index 763bfd45..90c6955 100644
--- a/components/constrained_window/BUILD.gn
+++ b/components/constrained_window/BUILD.gn
@@ -17,9 +17,11 @@
   ]
 
   deps = [
+    "//base",
     "//components/guest_view/browser",
     "//components/web_modal",
     "//content/public/browser",
+    "//ui/base/mojom",
     "//ui/display",
     "//ui/views",
   ]
diff --git a/components/constrained_window/constrained_window_views.cc b/components/constrained_window/constrained_window_views.cc
index a1242433..b82cb01 100644
--- a/components/constrained_window/constrained_window_views.cc
+++ b/components/constrained_window/constrained_window_views.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <memory>
 
+#include "base/check_op.h"
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/no_destructor.h"
diff --git a/components/content_settings/core/browser/cookie_settings.cc b/components/content_settings/core/browser/cookie_settings.cc
index 68f057f..cbad74f 100644
--- a/components/content_settings/core/browser/cookie_settings.cc
+++ b/components/content_settings/core/browser/cookie_settings.cc
@@ -219,6 +219,19 @@
       CONTENT_SETTING_DEFAULT);
 }
 
+bool CookieSettings::AreThirdPartyCookiesLimited() const {
+  // Checks whether we are in the limited state via Mode B or
+  // `CookieControlsMode`
+  return (tracking_protection_settings_ &&
+          tracking_protection_settings_->IsTrackingProtection3pcdEnabled() &&
+          !tracking_protection_settings_->AreAllThirdPartyCookiesBlocked()) ||
+         (static_cast<CookieControlsMode>(
+              pref_change_registrar_->prefs()->GetInteger(
+                  prefs::kCookieControlsMode)) ==
+              CookieControlsMode::kLimited &&
+          !is_incognito_);
+}
+
 // TODO(crbug.com/40247160): Update to take in CookieSettingOverrides.
 bool CookieSettings::IsThirdPartyAccessAllowed(
     const GURL& first_party_url,
@@ -387,6 +400,7 @@
 
   switch (mode) {
     case CookieControlsMode::kBlockThirdParty:
+    case CookieControlsMode::kLimited:
       return true;
     case CookieControlsMode::kIncognitoOnly:
       return is_incognito_;
@@ -397,17 +411,8 @@
 }
 
 bool CookieSettings::MitigationsEnabledFor3pcdInternal() const {
-  if (tracking_protection_settings_ &&
-      tracking_protection_settings_->IsTrackingProtection3pcdEnabled()) {
-    // Mitigations should be on iff we are not blocking all 3PC.
-    return !tracking_protection_settings_->AreAllThirdPartyCookiesBlocked();
-  }
-
-  if (net::cookie_util::IsForceThirdPartyCookieBlockingEnabled()) {
-    return true;
-  }
-
-  return false;
+  return AreThirdPartyCookiesLimited() ||
+         net::cookie_util::IsForceThirdPartyCookieBlockingEnabled();
 }
 
 void CookieSettings::OnContentSettingChanged(
diff --git a/components/content_settings/core/browser/cookie_settings.h b/components/content_settings/core/browser/cookie_settings.h
index 04252d0..f8c9d6ca 100644
--- a/components/content_settings/core/browser/cookie_settings.h
+++ b/components/content_settings/core/browser/cookie_settings.h
@@ -45,7 +45,8 @@
   kOff = 0,
   kBlockThirdParty = 1,
   kIncognitoOnly = 2,
-  kMaxValue = kIncognitoOnly,
+  kLimited = 3,
+  kMaxValue = kLimited,
 };
 
 // Default value for |extension_scheme|.
@@ -169,6 +170,12 @@
   // This should only be called on the UI thread.
   void ResetCookieSetting(const GURL& primary_url);
 
+  // Returns true if third party cookies should be limited (blocked with
+  // mitigations).
+  //
+  // This should only be called on the UI thread.
+  bool AreThirdPartyCookiesLimited() const;
+
   // Returns true if cookies are allowed for *most* third parties on |url|.
   // There might be rules allowing or blocking specific third parties from
   // accessing cookies.
diff --git a/components/content_settings/core/browser/cookie_settings_unittest.cc b/components/content_settings/core/browser/cookie_settings_unittest.cc
index 95b64b5..ed1ee472 100644
--- a/components/content_settings/core/browser/cookie_settings_unittest.cc
+++ b/components/content_settings/core/browser/cookie_settings_unittest.cc
@@ -776,6 +776,34 @@
   EXPECT_FALSE(cookie_settings_->IsCookieSessionOnly(kBlockedSite));
 }
 
+using AreThirdPartyCookiesLimited = CookieSettingsTestP;
+
+TEST_P(AreThirdPartyCookiesLimited, TrueWhen3pcsNotBlockedInModeB) {
+  prefs_.SetBoolean(prefs::kTrackingProtection3pcdEnabled, true);
+  prefs_.SetBoolean(prefs::kBlockAll3pcToggleEnabled, false);
+  EXPECT_TRUE(cookie_settings_->AreThirdPartyCookiesLimited());
+}
+
+TEST_P(AreThirdPartyCookiesLimited, FalseWhenAll3pcsBlockedInModeB) {
+  prefs_.SetBoolean(prefs::kTrackingProtection3pcdEnabled, true);
+  prefs_.SetBoolean(prefs::kBlockAll3pcToggleEnabled, true);
+  EXPECT_FALSE(cookie_settings_->AreThirdPartyCookiesLimited());
+}
+
+TEST_P(AreThirdPartyCookiesLimited,
+       TrueWhenCookieControlsModePrefSetToLimited) {
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kLimited));
+  EXPECT_TRUE(cookie_settings_->AreThirdPartyCookiesLimited());
+}
+
+TEST_P(AreThirdPartyCookiesLimited,
+       FalseWhenCookieControlsModePrefSetToBlockThirdParty) {
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
+  EXPECT_FALSE(cookie_settings_->AreThirdPartyCookiesLimited());
+}
+
 class CookieSettingsTestUserBypass : public CookieSettingsTest {
  public:
   CookieSettingsTestUserBypass() {
@@ -2099,10 +2127,19 @@
     /* no prefix */,
     CookieSettingsTestP,
 #if BUILDFLAG(IS_IOS)
-        testing::Values(GrantSource::kNoneGranted),
+    testing::Values(GrantSource::kNoneGranted),
 #else
-        testing::Range(GrantSource::kNoneGranted,
-                       GrantSource::kGrantSourceCount),
+    testing::Range(GrantSource::kNoneGranted, GrantSource::kGrantSourceCount),
+#endif
+    CustomTestName);
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    AreThirdPartyCookiesLimited,
+#if BUILDFLAG(IS_IOS)
+    testing::Values(GrantSource::kNoneGranted),
+#else
+    testing::Range(GrantSource::kNoneGranted, GrantSource::kGrantSourceCount),
 #endif
     CustomTestName);
 
diff --git a/components/data_sharing/internal/android/data_sharing_service_android.cc b/components/data_sharing/internal/android/data_sharing_service_android.cc
index d3794b7..ec9dec7 100644
--- a/components/data_sharing/internal/android/data_sharing_service_android.cc
+++ b/components/data_sharing/internal/android/data_sharing_service_android.cc
@@ -11,6 +11,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/notimplemented.h"
 #include "base/scoped_observation.h"
 #include "components/data_sharing/internal/android/data_sharing_conversion_bridge.h"
 #include "components/data_sharing/internal/android/data_sharing_network_loader_android.h"
@@ -90,6 +91,8 @@
   void OnGroupChanged(const GroupData& group_data) override;
   void OnGroupAdded(const GroupData& group_data) override;
   void OnGroupRemoved(const GroupId& group_id) override;
+  void OnServiceStatusChanged(
+      const ServiceStatusUpdate& status_update) override;
 
  private:
   ScopedJavaGlobalRef<jobject> java_obj_;
@@ -132,6 +135,11 @@
       env, java_obj_, ConvertUTF8ToJavaString(env, group_id.value()));
 }
 
+void DataSharingServiceAndroid::GroupDataObserverBridge::OnServiceStatusChanged(
+    const ServiceStatusUpdate& status_update) {
+  NOTIMPLEMENTED();
+}
+
 // This function is declared in data_sharing_service.h and
 // should be linked in to any binary using
 // DataSharingService::GetJavaObject.
diff --git a/components/data_sharing/internal/data_sharing_service_impl.cc b/components/data_sharing/internal/data_sharing_service_impl.cc
index 7269b88..f1cbb29b 100644
--- a/components/data_sharing/internal/data_sharing_service_impl.cc
+++ b/components/data_sharing/internal/data_sharing_service_impl.cc
@@ -591,6 +591,11 @@
   return ui_delegate_.get();
 }
 
+DataSharingService::ServiceStatus DataSharingServiceImpl::GetServiceStatus() {
+  NOTIMPLEMENTED();
+  return DataSharingService::ServiceStatus();
+}
+
 void DataSharingServiceImpl::OnAccessTokenAdded(
     base::OnceCallback<void(const GroupDataOrFailureOutcome&)> callback,
     const base::expected<data_sharing_pb::AddAccessTokenResult, absl::Status>&
diff --git a/components/data_sharing/internal/data_sharing_service_impl.h b/components/data_sharing/internal/data_sharing_service_impl.h
index 78efe8e..65da7ac9 100644
--- a/components/data_sharing/internal/data_sharing_service_impl.h
+++ b/components/data_sharing/internal/data_sharing_service_impl.h
@@ -104,6 +104,7 @@
       base::OnceCallback<void(const SharedDataPreviewOrFailureOutcome&)>
           callback) override;
   DataSharingUIDelegate* GetUIDelegate() override;
+  ServiceStatus GetServiceStatus() override;
 
   // CollaborationGroupSyncBridge::Observer implementation.
   void OnGroupsUpdated(const std::vector<GroupId>& added_group_ids,
diff --git a/components/data_sharing/internal/data_sharing_service_impl_unittest.cc b/components/data_sharing/internal/data_sharing_service_impl_unittest.cc
index b432fc8..978388714 100644
--- a/components/data_sharing/internal/data_sharing_service_impl_unittest.cc
+++ b/components/data_sharing/internal/data_sharing_service_impl_unittest.cc
@@ -88,6 +88,10 @@
   MOCK_METHOD(void, OnGroupChanged, (const GroupData&), (override));
   MOCK_METHOD(void, OnGroupAdded, (const GroupData&), (override));
   MOCK_METHOD(void, OnGroupRemoved, (const GroupId&), (override));
+  MOCK_METHOD(void,
+              OnServiceStatusChanged,
+              (const ServiceStatusUpdate&),
+              (override));
 };
 
 
diff --git a/components/data_sharing/internal/empty_data_sharing_service.cc b/components/data_sharing/internal/empty_data_sharing_service.cc
index 8015f20..ccc5040 100644
--- a/components/data_sharing/internal/empty_data_sharing_service.cc
+++ b/components/data_sharing/internal/empty_data_sharing_service.cc
@@ -91,4 +91,8 @@
   return nullptr;
 }
 
+DataSharingService::ServiceStatus EmptyDataSharingService::GetServiceStatus() {
+  return DataSharingService::ServiceStatus();
+}
+
 }  // namespace data_sharing
diff --git a/components/data_sharing/internal/empty_data_sharing_service.h b/components/data_sharing/internal/empty_data_sharing_service.h
index 13e45d8..0c94de1 100644
--- a/components/data_sharing/internal/empty_data_sharing_service.h
+++ b/components/data_sharing/internal/empty_data_sharing_service.h
@@ -65,6 +65,7 @@
       base::OnceCallback<void(const SharedDataPreviewOrFailureOutcome&)>
           callback) override;
   DataSharingUIDelegate* GetUIDelegate() override;
+  DataSharingService::ServiceStatus GetServiceStatus() override;
 };
 
 }  // namespace data_sharing
diff --git a/components/data_sharing/public/data_sharing_service.h b/components/data_sharing/public/data_sharing_service.h
index ec3427d..d51d887 100644
--- a/components/data_sharing/public/data_sharing_service.h
+++ b/components/data_sharing/public/data_sharing_service.h
@@ -28,20 +28,6 @@
 // The core class for managing data sharing.
 class DataSharingService : public KeyedService, public base::SupportsUserData {
  public:
-  class Observer : public base::CheckedObserver {
-   public:
-    Observer() = default;
-    Observer(const Observer&) = delete;
-    Observer& operator=(const Observer&) = delete;
-    ~Observer() override = default;
-
-    virtual void OnGroupChanged(const GroupData& group_data) {}
-    // User either created a new group or has been invited to the existing one.
-    virtual void OnGroupAdded(const GroupData& group_data) {}
-    // Either group has been deleted or user has been removed from the group.
-    virtual void OnGroupRemoved(const GroupId& group_id) {}
-  };
-
   // GENERATED_JAVA_ENUM_PACKAGE: (
   //   org.chromium.components.data_sharing)
   enum class PeopleGroupActionFailure {
@@ -68,6 +54,71 @@
     kQueryMissingFailure = 3
   };
 
+  // GENERATED_JAVA_ENUM_PACKAGE: (
+  //   org.chromium.components.data_sharing)
+  enum class SigninStatus {
+    kNotSignedIn = 0,
+    kSignedInPaused = 1,
+    kSignedIn = 2
+  };
+
+  // GENERATED_JAVA_ENUM_PACKAGE: (
+  //   org.chromium.components.data_sharing)
+  enum class SyncStatus {
+    kNotSyncing = 0,
+    kSyncWithoutTabGroup = 1,
+    kSyncEnabled = 2
+  };
+
+  // GENERATED_JAVA_ENUM_PACKAGE: (
+  //   org.chromium.components.data_sharing)
+  enum class CollaborationStatus {
+    // Users are not allowed to either join or create.
+    kDisabled = 0,
+    // The Chrome policy disables this feature, eg: enterprise policies.
+    kDisabledForPolicy = 1,
+    // Users are allowed to join only but have not joined a shared tab group
+    // yet.
+    kAllowedToJoin = 2,
+    // Users are allowed to join only and have already joined at least 1 shared
+    // tab group.
+    kEnabledJoinOnly = 3,
+    // Users are allowed to join and create shared tab groups.
+    kEnabledCreateAndJoin = 4
+  };
+
+  struct ServiceStatus {
+    SigninStatus signin_status;
+    SyncStatus sync_status;
+    CollaborationStatus collaboration_status;
+  };
+
+  class Observer : public base::CheckedObserver {
+   public:
+    Observer() = default;
+    Observer(const Observer&) = delete;
+    Observer& operator=(const Observer&) = delete;
+    ~Observer() override = default;
+
+    virtual void OnGroupChanged(const GroupData& group_data) {}
+    // User either created a new group or has been invited to the existing one.
+    virtual void OnGroupAdded(const GroupData& group_data) {}
+    // Either group has been deleted or user has been removed from the group.
+    virtual void OnGroupRemoved(const GroupId& group_id) {}
+
+    // The update details of a service's collaboration status.
+    struct ServiceStatusUpdate {
+      ServiceStatus old_status;
+      ServiceStatus new_status;
+
+      // Add helper functions as needed here.
+    };
+
+    // The service status has been changed.
+    virtual void OnServiceStatusChanged(
+        const ServiceStatusUpdate& status_update) {}
+  };
+
   using GroupDataOrFailureOutcome =
       base::expected<GroupData, PeopleGroupActionFailure>;
   using GroupsDataSetOrFailureOutcome =
@@ -180,6 +231,9 @@
 
   // Get the current DataSharingUIDelegate instance.
   virtual DataSharingUIDelegate* GetUIDelegate() = 0;
+
+  // Get the current ServiceStatus.
+  virtual ServiceStatus GetServiceStatus() = 0;
 };
 
 }  // namespace data_sharing
diff --git a/components/data_sharing/test_support/mock_data_sharing_service.h b/components/data_sharing/test_support/mock_data_sharing_service.h
index 70a8e31..eaae4bd 100644
--- a/components/data_sharing/test_support/mock_data_sharing_service.h
+++ b/components/data_sharing/test_support/mock_data_sharing_service.h
@@ -68,6 +68,7 @@
       void(const GroupToken&,
            base::OnceCallback<void(const SharedDataPreviewOrFailureOutcome&)>));
   MOCK_METHOD0(GetUIDelegate, DataSharingUIDelegate*());
+  MOCK_METHOD0(GetServiceStatus, ServiceStatus());
 };
 
 }  // namespace data_sharing
diff --git a/components/download/internal/background_service/scheduler/battery_status_listener.h b/components/download/internal/background_service/scheduler/battery_status_listener.h
index 70136423..28cf547 100644
--- a/components/download/internal/background_service/scheduler/battery_status_listener.h
+++ b/components/download/internal/background_service/scheduler/battery_status_listener.h
@@ -24,7 +24,7 @@
   virtual int GetBatteryPercentage() = 0;
 
   // Is the device is using battery power instead of charging.
-  virtual bool IsOnBatteryPower() = 0;
+  virtual bool IsOnBatteryPower() const = 0;
 
   // Start/Stop to listen to battery status changes.
   virtual void Start(Observer* observer) = 0;
diff --git a/components/download/internal/background_service/scheduler/battery_status_listener_impl.cc b/components/download/internal/background_service/scheduler/battery_status_listener_impl.cc
index 05d42d6d1..a780ab23 100644
--- a/components/download/internal/background_service/scheduler/battery_status_listener_impl.cc
+++ b/components/download/internal/background_service/scheduler/battery_status_listener_impl.cc
@@ -22,21 +22,22 @@
   return battery_percentage_;
 }
 
-bool BatteryStatusListenerImpl::IsOnBatteryPower() {
-  return base::PowerMonitor::IsOnBatteryPower();
+bool BatteryStatusListenerImpl::IsOnBatteryPower() const {
+  return base::PowerMonitor::GetInstance()->IsOnBatteryPower();
 }
 
 void BatteryStatusListenerImpl::Start(Observer* observer) {
   observer_ = observer;
 
-  DCHECK(base::PowerMonitor::IsInitialized());
-  base::PowerMonitor::AddPowerStateObserver(this);
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  DCHECK(power_monitor->IsInitialized());
+  power_monitor->AddPowerStateObserver(this);
 
   UpdateBatteryPercentage(true);
 }
 
 void BatteryStatusListenerImpl::Stop() {
-  base::PowerMonitor::RemovePowerStateObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerStateObserver(this);
 }
 
 int BatteryStatusListenerImpl::GetBatteryPercentageInternal() {
diff --git a/components/download/internal/background_service/scheduler/battery_status_listener_impl.h b/components/download/internal/background_service/scheduler/battery_status_listener_impl.h
index d4c179a..91f2ae8 100644
--- a/components/download/internal/background_service/scheduler/battery_status_listener_impl.h
+++ b/components/download/internal/background_service/scheduler/battery_status_listener_impl.h
@@ -33,7 +33,7 @@
  private:
   // BatteryStatusListener implementation.
   int GetBatteryPercentage() override;
-  bool IsOnBatteryPower() override;
+  bool IsOnBatteryPower() const override;
   void Start(Observer* observer) override;
   void Stop() override;
 
diff --git a/components/download/internal/background_service/scheduler/battery_status_listener_mac.cc b/components/download/internal/background_service/scheduler/battery_status_listener_mac.cc
index 5feeac3..9e8c4df 100644
--- a/components/download/internal/background_service/scheduler/battery_status_listener_mac.cc
+++ b/components/download/internal/background_service/scheduler/battery_status_listener_mac.cc
@@ -14,7 +14,7 @@
   return 100;
 }
 
-bool BatteryStatusListenerMac::IsOnBatteryPower() {
+bool BatteryStatusListenerMac::IsOnBatteryPower() const {
   return false;
 }
 
diff --git a/components/download/internal/background_service/scheduler/battery_status_listener_mac.h b/components/download/internal/background_service/scheduler/battery_status_listener_mac.h
index 1915b86d..e8e48d3 100644
--- a/components/download/internal/background_service/scheduler/battery_status_listener_mac.h
+++ b/components/download/internal/background_service/scheduler/battery_status_listener_mac.h
@@ -25,7 +25,7 @@
  private:
   // BatteryStatusListener implementation.
   int GetBatteryPercentage() override;
-  bool IsOnBatteryPower() override;
+  bool IsOnBatteryPower() const override;
   void Start(Observer* observer) override;
   void Stop() override;
 };
diff --git a/components/exo/wayland/clients/blur.cc b/components/exo/wayland/clients/blur.cc
index b7af1fee..00d759e 100644
--- a/components/exo/wayland/clients/blur.cc
+++ b/components/exo/wayland/clients/blur.cc
@@ -18,7 +18,7 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/effects/SkImageFilters.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "ui/gl/gl_bindings.h"
 
diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc
index e34e70be..cb43cf4 100644
--- a/components/exo/wayland/clients/client_base.cc
+++ b/components/exo/wayland/clients/client_base.cc
@@ -48,14 +48,14 @@
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLAssembleInterface.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_enums.h"
diff --git a/components/exo/wayland/clients/explicit_synchronization.cc b/components/exo/wayland/clients/explicit_synchronization.cc
index a8cb329..b1c9e02 100644
--- a/components/exo/wayland/clients/explicit_synchronization.cc
+++ b/components/exo/wayland/clients/explicit_synchronization.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/exo/wayland/clients/client_base.h"
-
 #include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
 
 #include <memory>
@@ -14,10 +12,11 @@
 #include "base/files/scoped_file.h"
 #include "base/message_loop/message_pump_type.h"
 #include "base/task/single_thread_task_executor.h"
+#include "components/exo/wayland/clients/client_base.h"
 #include "components/exo/wayland/clients/client_helper.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gfx/gpu_fence.h"
 #include "ui/gl/gl_bindings.h"
 
diff --git a/components/exo/wayland/clients/fullscreen_shell.cc b/components/exo/wayland/clients/fullscreen_shell.cc
index c14356d..b61954c 100644
--- a/components/exo/wayland/clients/fullscreen_shell.cc
+++ b/components/exo/wayland/clients/fullscreen_shell.cc
@@ -10,10 +10,10 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLAssembleInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/gl/gl_bindings.h"
diff --git a/components/exo/wayland/clients/rects.cc b/components/exo/wayland/clients/rects.cc
index 474a210b..96080a5 100644
--- a/components/exo/wayland/clients/rects.cc
+++ b/components/exo/wayland/clients/rects.cc
@@ -41,7 +41,7 @@
 #include "third_party/skia/include/core/SkFont.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gl/gl_bindings.h"
 
 namespace exo {
diff --git a/components/exo/wayland/clients/simple.cc b/components/exo/wayland/clients/simple.cc
index 9d0d926..051940e5 100644
--- a/components/exo/wayland/clients/simple.cc
+++ b/components/exo/wayland/clients/simple.cc
@@ -24,7 +24,7 @@
 #include "components/exo/wayland/clients/client_helper.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gl/gl_bindings.h"
 
 namespace exo {
diff --git a/components/exo/wayland/clients/subsurface.cc b/components/exo/wayland/clients/subsurface.cc
index 1ca8982..66088dd3 100644
--- a/components/exo/wayland/clients/subsurface.cc
+++ b/components/exo/wayland/clients/subsurface.cc
@@ -15,7 +15,7 @@
 #include "components/exo/wayland/clients/client_helper.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gl/gl_bindings.h"
 
 namespace exo {
diff --git a/components/exo/wayland/clients/vulkan.cc b/components/exo/wayland/clients/vulkan.cc
index d5b48771..ec11a9b 100644
--- a/components/exo/wayland/clients/vulkan.cc
+++ b/components/exo/wayland/clients/vulkan.cc
@@ -19,7 +19,7 @@
 #include "gpu/vulkan/vulkan_function_pointers.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 namespace exo {
 namespace wayland {
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
index a2b28d65..61e70a8 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
@@ -119,7 +119,8 @@
   // Trigger Pix code validation.
   utility_process_validator_.ValidatePixCode(
       pix_code, base::BindOnce(&FacilitatedPaymentsManager::OnPixCodeValidated,
-                               weak_ptr_factory_.GetWeakPtr(), pix_code));
+                               weak_ptr_factory_.GetWeakPtr(), pix_code,
+                               base::TimeTicks::Now()));
 }
 
 void FacilitatedPaymentsManager::RegisterPixAllowlist() const {
@@ -188,7 +189,10 @@
 
 void FacilitatedPaymentsManager::OnPixCodeValidated(
     std::string pix_code,
+    base::TimeTicks start_time,
     base::expected<bool, std::string> is_pix_code_valid) {
+  LogPaymentCodeValidationResultAndLatency(
+      is_pix_code_valid, (base::TimeTicks::Now() - start_time));
   if (!is_pix_code_valid.has_value()) {
     // Pix code validator encountered an error.
     LogPaymentNotOfferedReason(PaymentNotOfferedReason::kCodeValidatorFailed);
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.h b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
index 0d9e410..b8d4b2e 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.h
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
@@ -267,8 +267,10 @@
   // Called by the utility process after validation of the `pix_code`. If the
   // utility processes has disconnected (e.g., due to a crash in the validation
   // code), then `is_pix_code_valid` contains an error string instead of the
-  // boolean validation result.
+  // boolean validation result. The call to validate the PIX code was made at
+  // `start_time`.
   void OnPixCodeValidated(std::string pix_code,
+                          base::TimeTicks start_time,
                           base::expected<bool, std::string> is_pix_code_valid);
 
   // Lazily initializes an API client and returns a pointer to it. Returns a
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc b/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
index 5cbeddfc..ef93c04d 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
@@ -1231,6 +1231,7 @@
   EXPECT_CALL(GetApiClient(), IsAvailable(testing::_));
 
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/true);
 }
 
@@ -1243,6 +1244,7 @@
   EXPECT_CALL(GetApiClient(), IsAvailable(testing::_)).Times(0);
 
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/false);
 }
 
@@ -1252,6 +1254,7 @@
        PaymentNotOfferedReason_CodeValidatorReturnsFalse) {
   base::HistogramTester histogram_tester;
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/false);
 
   histogram_tester.ExpectUniqueSample(
@@ -1270,9 +1273,9 @@
   EXPECT_CALL(GetApiClient(), IsAvailable(testing::_)).Times(0);
 
   manager_->OnPixCodeValidated(
-      /*pix_code=*/std::string(),
-      /*is_pix_code_valid=*/base::unexpected(
-          "Data Decoder terminated unexpectedly"));
+      /*pix_code=*/std::string(), base::TimeTicks::Now(),
+      /*is_pix_code_valid=*/
+      base::unexpected("Data Decoder terminated unexpectedly"));
 }
 
 // If the validation utility process has disconnected (e.g., due to a crash in
@@ -1282,9 +1285,9 @@
        PaymentNotOfferedReason_CodeValidatorFailed) {
   base::HistogramTester histogram_tester;
   manager_->OnPixCodeValidated(
-      /*pix_code=*/std::string(),
-      /*is_pix_code_valid=*/base::unexpected(
-          "Data Decoder terminated unexpectedly"));
+      /*pix_code=*/std::string(), base::TimeTicks::Now(),
+      /*is_pix_code_valid=*/
+      base::unexpected("Data Decoder terminated unexpectedly"));
 
   histogram_tester.ExpectUniqueSample(
       "FacilitatedPayments.Pix.PaymentNotOfferedReason",
@@ -1302,6 +1305,7 @@
   EXPECT_CALL(GetApiClient(), IsAvailable(testing::_)).Times(0);
 
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/true);
 }
 
@@ -1311,6 +1315,7 @@
   EXPECT_CALL(GetApiClient(), IsAvailable(testing::_)).Times(0);
 
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/true);
 }
 
@@ -1325,6 +1330,7 @@
   EXPECT_CALL(GetApiClient(), IsAvailable(testing::_)).Times(0);
 
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/true);
 }
 
@@ -1464,6 +1470,7 @@
   payments_data_manager_->AddMaskedBankAccountForTest(CreatePixBankAccount(1));
   EXPECT_CALL(GetApiClient(), IsAvailable(testing::_));
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/true);
   FastForwardBy(base::Seconds(2));
 
@@ -1659,6 +1666,7 @@
   EXPECT_EQ(nullptr, manager_->api_client_.get());
 
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/true);
 
   EXPECT_NE(nullptr, manager_->api_client_.get());
@@ -1673,6 +1681,7 @@
   EXPECT_EQ(nullptr, manager_->api_client_.get());
 
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/true);
 
   EXPECT_EQ(nullptr, manager_->api_client_.get());
diff --git a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.cc b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.cc
index 2f7da18d..aa8393f0 100644
--- a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.cc
+++ b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.cc
@@ -12,6 +12,23 @@
 
 namespace payments::facilitated {
 
+void LogPaymentCodeValidationResultAndLatency(
+    base::expected<bool, std::string> result,
+    base::TimeDelta duration) {
+  std::string payment_code_validation_result_type;
+  if (!result.has_value()) {
+    payment_code_validation_result_type = "ValidatorFailed";
+  } else if (!result.value()) {
+    payment_code_validation_result_type = "InvalidCode";
+  } else {
+    payment_code_validation_result_type = "ValidCode";
+  }
+  base::UmaHistogramLongTimes(
+      base::StrCat({"FacilitatedPayments.Pix.PaymentCodeValidation.",
+                    payment_code_validation_result_type, ".Latency"}),
+      duration);
+}
+
 void LogIsApiAvailableResult(bool result, base::TimeDelta duration) {
   // TODO(b/337929926): Remove hardcoding for Pix and use
   // FacilitatedPaymentsType enum.
diff --git a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h
index bfea56f6..001ee33 100644
--- a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h
+++ b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_FACILITATED_PAYMENTS_CORE_METRICS_FACILITATED_PAYMENTS_METRICS_H_
 #define COMPONENTS_FACILITATED_PAYMENTS_CORE_METRICS_FACILITATED_PAYMENTS_METRICS_H_
 
+#include "base/types/expected.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace base {
@@ -40,6 +41,12 @@
   kMaxValue = kCopyEvent
 };
 
+// Log the result and latency for validating a payment code using
+// `data_decoder::DataDecoder`.
+void LogPaymentCodeValidationResultAndLatency(
+    base::expected<bool, std::string> result,
+    base::TimeDelta duration);
+
 // Log the result of whether the facilitated payments is available or not.
 void LogIsApiAvailableResult(bool result, base::TimeDelta duration);
 
diff --git a/components/facilitated_payments/core/metrics/facilitated_payments_metrics_unittest.cc b/components/facilitated_payments/core/metrics/facilitated_payments_metrics_unittest.cc
index 6619681f..e9a10f2 100644
--- a/components/facilitated_payments/core/metrics/facilitated_payments_metrics_unittest.cc
+++ b/components/facilitated_payments/core/metrics/facilitated_payments_metrics_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
+#include "base/types/expected.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
@@ -13,6 +14,46 @@
 
 namespace payments::facilitated {
 
+TEST(FacilitatedPaymentsMetricsTest,
+     LogPaymentCodeValidationResultAndLatency_ValidatorFailed) {
+  base::HistogramTester histogram_tester;
+
+  LogPaymentCodeValidationResultAndLatency(
+      /*result=*/base::unexpected("Data Decoder terminated unexpectedly"),
+      base::Milliseconds(10));
+
+  histogram_tester.ExpectUniqueSample(
+      "FacilitatedPayments.Pix.PaymentCodeValidation.ValidatorFailed.Latency",
+      /*sample=*/10,
+      /*expected_bucket_count=*/1);
+}
+
+TEST(FacilitatedPaymentsMetricsTest,
+     LogPaymentCodeValidationResultAndLatency_InvalidCode) {
+  base::HistogramTester histogram_tester;
+
+  LogPaymentCodeValidationResultAndLatency(/*result=*/false,
+                                           base::Milliseconds(10));
+
+  histogram_tester.ExpectUniqueSample(
+      "FacilitatedPayments.Pix.PaymentCodeValidation.InvalidCode.Latency",
+      /*sample=*/10,
+      /*expected_bucket_count=*/1);
+}
+
+TEST(FacilitatedPaymentsMetricsTest,
+     LogPaymentCodeValidationResultAndLatency_ValidCode) {
+  base::HistogramTester histogram_tester;
+
+  LogPaymentCodeValidationResultAndLatency(/*result=*/true,
+                                           base::Milliseconds(10));
+
+  histogram_tester.ExpectUniqueSample(
+      "FacilitatedPayments.Pix.PaymentCodeValidation.ValidCode.Latency",
+      /*sample=*/10,
+      /*expected_bucket_count=*/1);
+}
+
 TEST(FacilitatedPaymentsMetricsTest, LogIsAvailableResult) {
   base::HistogramTester histogram_tester;
 
diff --git a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
index 94a3ab7..bfb6175 100644
--- a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
+++ b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
@@ -3655,9 +3655,6 @@
     FeedApiTest::SetUp();
     // Sometimes the clock starts near zero; move it forward just in case.
     task_environment_.AdvanceClock(base::Minutes(10));
-
-    features_.InitAndEnableFeatureWithParameters(
-        kFeedCloseRefresh, {{"require_interaction", "true"}});
   }
 
  private:
@@ -3745,54 +3742,6 @@
                 .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
 }
 
-TEST_F(FeedCloseRefreshTest, FeedViewed) {
-  // Disable the interaction requirement for refreshes.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      kFeedCloseRefresh, {{"require_interaction", "false"}});
-  TestForYouSurface surface(stream_.get());
-  WaitForIdleTaskQueue();
-  stream_->ReportFeedViewed(surface.GetSurfaceId());
-  // The schedule should have been updated.
-  EXPECT_EQ(base::Minutes(30),
-            refresh_scheduler_
-                .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
-
-  // Only a surface's first view should cause the schedule to be set.
-  refresh_scheduler_.Clear();
-  stream_->ReportFeedViewed(surface.GetSurfaceId());
-  // Zero means the scheudle wasn't updated.
-  EXPECT_EQ(base::Seconds(0),
-            refresh_scheduler_
-                .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
-
-  task_environment_.AdvanceClock(base::Minutes(6));
-
-  // Opening another surface should cause a refresh to be scheduled.
-  refresh_scheduler_.Clear();
-  TestForYouSurface surface2(stream_.get());
-  WaitForIdleTaskQueue();
-  stream_->ReportFeedViewed(surface2.GetSurfaceId());
-  // The schedule should have been updated.
-  EXPECT_EQ(base::Minutes(30),
-            refresh_scheduler_
-                .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
-
-  task_environment_.AdvanceClock(base::Minutes(6));
-
-  // Leaving the surface and returning should schedule a refresh.
-  refresh_scheduler_.Clear();
-  surface.Detach();
-  WaitForIdleTaskQueue();
-  surface.Attach(stream_.get());
-  WaitForIdleTaskQueue();
-  stream_->ReportFeedViewed(surface.GetSurfaceId());
-  // The schedule should have been updated.
-  EXPECT_EQ(base::Minutes(30),
-            refresh_scheduler_
-                .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
-}
-
 TEST_F(FeedCloseRefreshTest, ExistingScheduleGetsReplaced) {
   // Inject a typical network response, with a server-defined request schedule.
   {
@@ -3823,14 +3772,11 @@
 }
 
 TEST_F(FeedCloseRefreshTest, Retry) {
-  // Disable the interaction requirement for refreshes.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      kFeedCloseRefresh, {{"require_interaction", "false"}});
   TestForYouSurface surface(stream_.get());
   WaitForIdleTaskQueue();
   // Update the schedule.
-  stream_->ReportFeedViewed(surface.GetSurfaceId());
+  stream_->ReportOpenAction(GURL("http://example.com"), surface.GetSurfaceId(),
+                            "", OpenActionType::kDefault);
   EXPECT_EQ(base::Minutes(30),
             refresh_scheduler_
                 .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc
index ab6dd0f..ba3ec1c 100644
--- a/components/feed/core/v2/feed_stream.cc
+++ b/components/feed/core/v2/feed_stream.cc
@@ -71,6 +71,7 @@
 namespace feed {
 namespace {
 constexpr size_t kMaxRecentFeedNavigations = 10;
+constexpr base::TimeDelta kFeedCloseRefreshDelay = base::Minutes(30);
 
 void UpdateDebugStreamData(
     const UploadActionsTask::Result& upload_actions_result,
@@ -1534,7 +1535,7 @@
     privacy_notice_card_tracker_.OnOpenAction(
         stream.model->FindContentId(ToContentRevision(slice_id)));
   }
-  ScheduleFeedCloseRefreshOnInteraction(surface->GetStreamType());
+  ScheduleFeedCloseRefresh(surface->GetStreamType());
 }
 
 void FeedStream::ReportOpenVisitComplete(SurfaceId /*surface_id*/,
@@ -1596,12 +1597,6 @@
     return;
   }
 
-  // Skip feed-close refresh scheduling if this surface was already viewed.
-  // entry should never be null, but if it is, we will skip rescheduling.
-  StreamSurfaceSet::Entry* entry = stream->surfaces.FindSurface(surface_id);
-  if (entry && !entry->feed_viewed)
-    ScheduleFeedCloseRefreshOnFirstView(stream->type);
-
   stream->surfaces.FeedViewed(surface_id);
   MaybeNotifyHasUnreadContent(stream->type);
 }
@@ -1616,7 +1611,7 @@
   }
   metrics_reporter_->StreamScrolled(surface->GetStreamType(), distance_dp);
   if (GetStream(surface->GetStreamType()).surfaces.HasSurfaceShowingContent()) {
-    ScheduleFeedCloseRefreshOnInteraction(surface->GetStreamType());
+    ScheduleFeedCloseRefresh(surface->GetStreamType());
   }
 }
 void FeedStream::ReportStreamScrollStart(SurfaceId /*surface_id*/) {
@@ -1742,20 +1737,6 @@
   return prefs::GetWebFeedContentOrder(*profile_prefs_);
 }
 
-void FeedStream::ScheduleFeedCloseRefreshOnInteraction(const StreamType& type) {
-  if (!base::FeatureList::IsEnabled(kFeedCloseRefresh))
-    return;
-  ScheduleFeedCloseRefresh(type);
-}
-
-void FeedStream::ScheduleFeedCloseRefreshOnFirstView(const StreamType& type) {
-  if (!base::FeatureList::IsEnabled(kFeedCloseRefresh) ||
-      kFeedCloseRefreshRequireInteraction.Get()) {
-    return;
-  }
-  ScheduleFeedCloseRefresh(type);
-}
-
 void FeedStream::ScheduleFeedCloseRefresh(const StreamType& type) {
   // To avoid causing jank, only schedule the refresh once every several
   // minutes.
@@ -1765,7 +1746,7 @@
 
   last_refresh_scheduled_on_interaction_time_ = now;
 
-  base::TimeDelta delay = base::Minutes(kFeedCloseRefreshDelayMinutes.Get());
+  base::TimeDelta delay = kFeedCloseRefreshDelay;
   RequestSchedule schedule;
   schedule.anchor_time = base::Time::Now();
   schedule.refresh_offsets = {delay, delay * 2, delay * 3};
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h
index 887923d..84dfdad8 100644
--- a/components/feed/core/v2/feed_stream.h
+++ b/components/feed/core/v2/feed_stream.h
@@ -448,11 +448,6 @@
 
   // Schedule a feed-close refresh when the user has taken some kind of action
   // on the feed.
-  void ScheduleFeedCloseRefreshOnInteraction(const StreamType& type);
-  // Schedule a feed-close refresh when the user has viewed content for the
-  // first time.
-  void ScheduleFeedCloseRefreshOnFirstView(const StreamType& type);
-  // Internal method for scheduling the feed-close refresh.
   void ScheduleFeedCloseRefresh(const StreamType& type);
 
   void CheckDuplicatedContentsOnRefresh();
diff --git a/components/feed/feed_feature_list.cc b/components/feed/feed_feature_list.cc
index 69778c87..50ee7c24 100644
--- a/components/feed/feed_feature_list.cc
+++ b/components/feed/feed_feature_list.cc
@@ -94,14 +94,6 @@
              "InfoCardAcknowledgementTracking",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kFeedCloseRefresh,
-             "FeedCloseRefresh",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-const base::FeatureParam<int> kFeedCloseRefreshDelayMinutes{
-    &kFeedCloseRefresh, "delay_minutes", 30};
-const base::FeatureParam<bool> kFeedCloseRefreshRequireInteraction{
-    &kFeedCloseRefresh, "require_interaction", true};
-
 BASE_FEATURE(kFeedNoViewCache,
              "FeedNoViewCache",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/feed/feed_feature_list.h b/components/feed/feed_feature_list.h
index b56dbb3..5ca0c370 100644
--- a/components/feed/feed_feature_list.h
+++ b/components/feed/feed_feature_list.h
@@ -75,16 +75,6 @@
 // Feature that enables tracking the acknowledgement state for the info cards.
 BASE_DECLARE_FEATURE(kInfoCardAcknowledgementTracking);
 
-// When enabled, schedule a background refresh for a feed sometime after the
-// last user engagement with that feed.
-BASE_DECLARE_FEATURE(kFeedCloseRefresh);
-// On each qualifying user engagement, schedule a background refresh this many
-// minutes out.
-extern const base::FeatureParam<int> kFeedCloseRefreshDelayMinutes;
-// If true, schedule the refresh only when the user scrolls or interacts. If
-// false, schedule only when the feed surface is opened to content.
-extern const base::FeatureParam<bool> kFeedCloseRefreshRequireInteraction;
-
 // When enabled, no view cache is used.
 BASE_DECLARE_FEATURE(kFeedNoViewCache);
 
diff --git a/components/mirroring/service/mirror_settings.h b/components/mirroring/service/mirror_settings.h
index 80ae64ec..5f28c43 100644
--- a/components/mirroring/service/mirror_settings.h
+++ b/components/mirroring/service/mirror_settings.h
@@ -25,15 +25,7 @@
 // Default end-to-end latency. Currently adaptive latency control is disabled
 // because of audio playout regressions (b/32876644).
 // TODO(openscreen/44): Re-enable in port to Open Screen.
-//
-// NOTE: currently ChromeOS does not support a lower default playout delay.
-// TODO(https://issuetracker.google.com/327950363): investigate.
-inline constexpr base::TimeDelta kDefaultPlayoutDelay =
-#if BUILDFLAG(IS_CHROMEOS)
-    base::Milliseconds(400);
-#else
-    base::Milliseconds(200);
-#endif
+inline constexpr base::TimeDelta kDefaultPlayoutDelay = base::Milliseconds(200);
 
 // Holds the default settings for a mirroring session. This class provides the
 // audio/video configs that this sender supports. And also provides the
diff --git a/components/nacl/loader/nacl_main.cc b/components/nacl/loader/nacl_main.cc
index 355fa77..38e4fe1e 100644
--- a/components/nacl/loader/nacl_main.cc
+++ b/components/nacl/loader/nacl_main.cc
@@ -31,7 +31,7 @@
   base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::IO);
   base::PlatformThread::SetName("CrNaClMain");
 
-  base::PowerMonitor::Initialize(MakePowerMonitorDeviceSource());
+  base::PowerMonitor::GetInstance()->Initialize(MakePowerMonitorDeviceSource());
   base::HighResolutionTimerManager hi_res_timer_manager;
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
diff --git a/components/offline_pages/core/background/initialize_store_task.cc b/components/offline_pages/core/background/initialize_store_task.cc
index 00308c9..478ef05c 100644
--- a/components/offline_pages/core/background/initialize_store_task.cc
+++ b/components/offline_pages/core/background/initialize_store_task.cc
@@ -51,10 +51,11 @@
 }
 
 void InitializeStoreTask::OnStoreResetDone(bool success) {
-  if (success)
+  if (success) {
     InitializeStore();
-  else
+  } else {
     TryToResetStore();
+  }
 }
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/background/offliner_client.cc b/components/offline_pages/core/background/offliner_client.cc
index ab78439..8847ee4 100644
--- a/components/offline_pages/core/background/offliner_client.cc
+++ b/components/offline_pages/core/background/offliner_client.cc
@@ -38,8 +38,9 @@
 }
 
 void OfflinerClient::Stop(Offliner::RequestStatus status) {
-  if (!active_request_ || stopping_)
+  if (!active_request_ || stopping_) {
     return;
+  }
   if (offliner_->Cancel(base::BindOnce(&OfflinerClient::CancelComplete,
                                        base::Unretained(this), status))) {
     stopping_ = true;
diff --git a/components/offline_pages/core/background/pick_request_task.cc b/components/offline_pages/core/background/pick_request_task.cc
index 915f35a..3e39800 100644
--- a/components/offline_pages/core/background/pick_request_task.cc
+++ b/components/offline_pages/core/background/pick_request_task.cc
@@ -87,10 +87,11 @@
   RequestCompareFunction comparator = nullptr;
 
   // Choose which comparison function to use based on policy.
-  if (policy_->RetryCountIsMoreImportantThanRecency())
+  if (policy_->RetryCountIsMoreImportantThanRecency()) {
     comparator = &PickRequestTask::RetryCountFirstCompareFunction;
-  else
+  } else {
     comparator = &PickRequestTask::RecencyFirstCompareFunction;
+  }
 
   bool non_user_requested_tasks_remaining = false;
   bool cleanup_needed = false;
diff --git a/components/offline_pages/core/background/request_coordinator_unittest.cc b/components/offline_pages/core/background/request_coordinator_unittest.cc
index da128c5..149decf 100644
--- a/components/offline_pages/core/background/request_coordinator_unittest.cc
+++ b/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -284,10 +284,11 @@
 
   void CallRequestNotPicked(bool non_user_requested_tasks_remaining,
                             bool disabled_tasks_remaining) {
-    if (disabled_tasks_remaining)
+    if (disabled_tasks_remaining) {
       coordinator()->disabled_requests_.insert(kRequestId1);
-    else
+    } else {
       coordinator()->disabled_requests_.clear();
+    }
 
     coordinator()->RequestNotPicked(non_user_requested_tasks_remaining, false,
                                     base::Time());
diff --git a/components/offline_pages/core/page_criteria.cc b/components/offline_pages/core/page_criteria.cc
index bcf2700..504751a 100644
--- a/components/offline_pages/core/page_criteria.cc
+++ b/components/offline_pages/core/page_criteria.cc
@@ -60,14 +60,17 @@
   if (!MeetsCriteria(criteria, item.client_id))
     return false;
 
-  if (criteria.file_size && item.file_size != criteria.file_size.value())
+  if (criteria.file_size && item.file_size != criteria.file_size.value()) {
     return false;
-  if (!criteria.digest.empty() && item.digest != criteria.digest)
+  }
+  if (!criteria.digest.empty() && item.digest != criteria.digest) {
     return false;
+  }
 
   if (!criteria.request_origin.empty() &&
-      item.request_origin != criteria.request_origin)
+      item.request_origin != criteria.request_origin) {
     return false;
+  }
 
   if (!criteria.url.is_empty() &&
       !EqualsIgnoringFragment(criteria.url, item.url) &&
@@ -81,8 +84,9 @@
     return false;
   }
 
-  if (criteria.additional_criteria && !criteria.additional_criteria.Run(item))
+  if (criteria.additional_criteria && !criteria.additional_criteria.Run(item)) {
     return false;
+  }
 
   return true;
 }
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index bae07ac..42b598ff 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit bae07ac124624f1389990348b3d2df951083f28d
+Subproject commit 42b598fff77ed715391248780615f506aea2860f
diff --git a/components/page_load_metrics/browser/observers/abandoned_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/abandoned_page_load_metrics_observer.cc
index bb2a239..18f7d24 100644
--- a/components/page_load_metrics/browser/observers/abandoned_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/abandoned_page_load_metrics_observer.cc
@@ -87,6 +87,7 @@
 
 const char kSuffixWasBackgrounded[] = ".WasBackgrounded";
 const char kSuffixWasHidden[] = ".WasHidden";
+const char kSuffixResponseFromCache[] = ".ResponseFromCache";
 
 const char kMilestoneNavigationStart[] = "NavigationStart";
 const char kMilestoneLoaderStart[] = "LoaderStart";
@@ -240,7 +241,11 @@
 
 std::vector<std::string>
 AbandonedPageLoadMetricsObserver::GetAdditionalSuffixes() const {
-  return {""};
+  std::vector<std::string> suffixes = {""};
+  if (was_cached_) {
+    suffixes.push_back(internal::kSuffixResponseFromCache);
+  }
+  return suffixes;
 }
 
 const base::flat_map<std::string,
@@ -625,6 +630,13 @@
     }
   }
 
+  // Check if the response came from http cache or not.
+  if (!latest_navigation_handle_timing_.non_redirect_response_start_time
+           .is_null() &&
+      latest_navigation_handle_timing_.navigation_commit_sent_time.is_null()) {
+    was_cached_ = navigation_handle->WasResponseCached();
+  }
+
   if (navigation_handle->GetNetErrorCode() != net::OK) {
     // The navigation will commit an error page instead of the intended URL.
     // Record this as an abandonment as soon as we notice.
diff --git a/components/page_load_metrics/browser/observers/abandoned_page_load_metrics_observer.h b/components/page_load_metrics/browser/observers/abandoned_page_load_metrics_observer.h
index 27e31fb..6885b045 100644
--- a/components/page_load_metrics/browser/observers/abandoned_page_load_metrics_observer.h
+++ b/components/page_load_metrics/browser/observers/abandoned_page_load_metrics_observer.h
@@ -14,6 +14,7 @@
 extern const char kAbandonedPageLoadMetricsHistogramPrefix[];
 extern const char kSuffixWasBackgrounded[];
 extern const char kSuffixWasHidden[];
+extern const char kSuffixResponseFromCache[];
 extern const char kRendererProcessCreatedBeforeNavHistogramName[];
 extern const char kRendererProcessInitHistogramName[];
 
@@ -157,6 +158,9 @@
       content::NavigationHandle* navigation_handle,
       const GURL& currently_committed_url) override;
 
+ protected:
+  virtual std::vector<std::string> GetAdditionalSuffixes() const;
+
  private:
   using LoadingMilestone = std::pair<NavigationMilestone, base::TimeDelta>;
   // Returns the suffix to be added to the histograms logged. This is not static
@@ -194,7 +198,6 @@
   // Carveouts for child classes that want to differentiate the logged histogram
   // or react differently on the navigation events (e.g. filtering the URL).
   virtual std::string GetHistogramPrefix() const;
-  virtual std::vector<std::string> GetAdditionalSuffixes() const;
   virtual ObservePolicy OnNavigationEvent(
       content::NavigationHandle* navigation_handle);
   virtual bool IsAllowedToLogMetrics() const;
@@ -235,6 +238,9 @@
   // navigation, has been logged before.
   bool did_log_navigation_start_ = false;
 
+  // Whether the Navigation Response came from http cache or not.
+  bool was_cached_ = false;
+
   // LCP is finalized in `FlushMetricsOnAppEnterBackground()` or `OnComplete()`.
   // In `AbandonedPageLoadMetricsObserver`, we keep observing events even after
   // the app is backgrounded, and that means both events are called. To prevent
diff --git a/components/page_load_metrics/google/browser/gws_abandoned_page_load_metrics_observer.cc b/components/page_load_metrics/google/browser/gws_abandoned_page_load_metrics_observer.cc
index d8a49b3..e12b6af 100644
--- a/components/page_load_metrics/google/browser/gws_abandoned_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/google/browser/gws_abandoned_page_load_metrics_observer.cc
@@ -98,11 +98,19 @@
 
 std::vector<std::string>
 GWSAbandonedPageLoadMetricsObserver::GetAdditionalSuffixes() const {
+  auto base_suffixes =
+      AbandonedPageLoadMetricsObserver::GetAdditionalSuffixes();
+
+  std::string request_source_suffix =
+      did_request_non_srp_ ? internal::kSuffixWasNonSRP : "";
+  std::vector<std::string> suffixes_with_request_source;
   // Add suffix that indicates the navigation prevviously requested a non-SRP
   // URL (instead of immediately targeting a SRP URL) to all histograms, if
   // necessary.
-  std::string suffix = did_request_non_srp_ ? internal::kSuffixWasNonSRP : "";
-  return {suffix};
+  for (auto base_suffix : base_suffixes) {
+    suffixes_with_request_source.push_back(base_suffix + request_source_suffix);
+  }
+  return suffixes_with_request_source;
 }
 
 void GWSAbandonedPageLoadMetricsObserver::AddSRPMetricsToUKMIfNeeded(
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index cdeb882..c6862814 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -243,6 +243,16 @@
   GetPasswordAutofillAgent()->FillPasswordSuggestion(username, password);
 }
 
+void ContentPasswordManagerDriver::FillSuggestionById(
+    autofill::FieldRendererId username_element_id,
+    autofill::FieldRendererId password_element_id,
+    const std::u16string& username,
+    const std::u16string& password) {
+  LogFilledFieldType();
+  GetPasswordAutofillAgent()->FillPasswordSuggestionById(
+      username_element_id, password_element_id, username, password);
+}
+
 void ContentPasswordManagerDriver::FillIntoFocusedField(
     bool is_password,
     const std::u16string& credential) {
@@ -278,6 +288,15 @@
   GetAutofillAgent()->PreviewPasswordSuggestion(username, password);
 }
 
+void ContentPasswordManagerDriver::PreviewSuggestionById(
+    autofill::FieldRendererId username_element_id,
+    autofill::FieldRendererId password_element_id,
+    const std::u16string& username,
+    const std::u16string& password) {
+  GetPasswordAutofillAgent()->PreviewPasswordSuggestionById(
+      username_element_id, password_element_id, username, password);
+}
+
 void ContentPasswordManagerDriver::PreviewGenerationSuggestion(
     const std::u16string& password) {
   GetAutofillAgent()->PreviewPasswordGenerationSuggestion(password);
@@ -609,7 +628,7 @@
 
 void ContentPasswordManagerDriver::LogFilledFieldType() {
   bool field_classified_as_target_filling_password =
-      GetPasswordManager()->GetPasswordFormCache()->HasPasswordForm(
+      GetPasswordManager()->GetPasswordFormCache()->GetPasswordForm(
           this, last_triggering_field_id_);
   base::UmaHistogramBoolean("Autofill.FilledFieldType.Password",
                             field_classified_as_target_filling_password);
diff --git a/components/password_manager/content/browser/content_password_manager_driver.h b/components/password_manager/content/browser/content_password_manager_driver.h
index 30fddb4..4851236 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.h
+++ b/components/password_manager/content/browser/content_password_manager_driver.h
@@ -72,6 +72,10 @@
   void FillField(const std::u16string& value) override;
   void FillSuggestion(const std::u16string& username,
                       const std::u16string& password) override;
+  void FillSuggestionById(autofill::FieldRendererId username_element_id,
+                          autofill::FieldRendererId password_element_id,
+                          const std::u16string& username,
+                          const std::u16string& password) override;
   void FillIntoFocusedField(bool is_password,
                             const std::u16string& credential) override;
 #if BUILDFLAG(IS_ANDROID)
@@ -83,6 +87,10 @@
                     const std::u16string& value) override;
   void PreviewSuggestion(const std::u16string& username,
                          const std::u16string& password) override;
+  void PreviewSuggestionById(autofill::FieldRendererId username_element_id,
+                             autofill::FieldRendererId password_element_id,
+                             const std::u16string& username,
+                             const std::u16string& password) override;
   void PreviewGenerationSuggestion(const std::u16string& password) override;
   void ClearPreviewedForm() override;
   void SetSuggestionAvailability(autofill::FieldRendererId element_id,
diff --git a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
index ae830b9..82af164 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
@@ -99,6 +99,20 @@
               FillPasswordSuggestion,
               (const std::u16string&, const std::u16string&),
               (override));
+  MOCK_METHOD(void,
+              FillPasswordSuggestionById,
+              (autofill::FieldRendererId,
+               autofill::FieldRendererId,
+               const std::u16string&,
+               const std::u16string&),
+              (override));
+  MOCK_METHOD(void,
+              PreviewPasswordSuggestionById,
+              (autofill::FieldRendererId,
+               autofill::FieldRendererId,
+               const std::u16string&,
+               const std::u16string&),
+              (override));
   MOCK_METHOD(void, InformNoSavedCredentials, (bool), (override));
   MOCK_METHOD(void,
               FillIntoFocusedField,
@@ -170,12 +184,12 @@
  public:
   ~MockPasswordFormCache() override = default;
 
-  MOCK_METHOD(bool,
-              HasPasswordForm,
+  MOCK_METHOD(const PasswordForm*,
+              GetPasswordForm,
               (PasswordManagerDriver*, autofill::FormRendererId),
               (const override));
-  MOCK_METHOD(bool,
-              HasPasswordForm,
+  MOCK_METHOD(const PasswordForm*,
+              GetPasswordForm,
               (PasswordManagerDriver*, autofill::FieldRendererId),
               (const override));
 };
@@ -338,14 +352,15 @@
   base::HistogramTester histogram_tester;
   MockPasswordManager password_manager_{&password_manager_client_};
   MockPasswordFormCache password_form_cache_;
+  PasswordForm form;
   bool field_part_of_password_form = GetParam();
 
   ON_CALL(password_manager_client_, GetPasswordManager())
       .WillByDefault(Return(&password_manager_));
   ON_CALL(password_manager_, GetPasswordFormCache())
       .WillByDefault(Return(&password_form_cache_));
-  ON_CALL(password_form_cache_, HasPasswordForm(_, autofill::FieldRendererId()))
-      .WillByDefault(Return(field_part_of_password_form));
+  ON_CALL(password_form_cache_, GetPasswordForm(_, autofill::FieldRendererId()))
+      .WillByDefault(Return(field_part_of_password_form ? &form : nullptr));
 
   std::unique_ptr<ContentPasswordManagerDriver> driver(
       new ContentPasswordManagerDriver(main_rfh(), &password_manager_client_));
@@ -358,9 +373,15 @@
   histogram_tester.ExpectUniqueSample("Autofill.FilledFieldType.Password",
                                       field_part_of_password_form, 2);
 
-  driver->FillIntoFocusedField(true, u"password");
+  driver->FillSuggestionById(autofill::FieldRendererId(),
+                             autofill::FieldRendererId(), u"username",
+                             u"password");
   histogram_tester.ExpectUniqueSample("Autofill.FilledFieldType.Password",
                                       field_part_of_password_form, 3);
+
+  driver->FillIntoFocusedField(true, u"password");
+  histogram_tester.ExpectUniqueSample("Autofill.FilledFieldType.Password",
+                                      field_part_of_password_form, 4);
 }
 
 INSTANTIATE_TEST_SUITE_P(All,
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 5c22b8182..54819e6 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -213,6 +213,8 @@
 
   if (is_android) {
     sources += [
+      "first_cct_page_load_passwords_ukm_recorder.cc",
+      "first_cct_page_load_passwords_ukm_recorder.h",
       "password_credential_filler.h",
       "password_credential_filler_impl.cc",
       "password_credential_filler_impl.h",
@@ -223,6 +225,10 @@
       ":password_manager_buildflags",
       "//components/webauthn/android",
     ]
+    deps += [
+      "//components/webauthn/android",
+      "//services/metrics/public/cpp:ukm_builders",
+    ]
   }
 
   if (!is_android) {
@@ -494,6 +500,7 @@
   ]
   if (is_android) {
     sources += [
+      "first_cct_page_load_passwords_ukm_recorder_unittest.cc",
       "password_credential_filler_impl_unittest.cc",
       "split_stores_and_local_upm_unittest.cc",
     ]
diff --git a/components/password_manager/core/browser/features/password_features.cc b/components/password_manager/core/browser/features/password_features.cc
index 538a98f..9b54437 100644
--- a/components/password_manager/core/browser/features/password_features.cc
+++ b/components/password_manager/core/browser/features/password_features.cc
@@ -61,7 +61,7 @@
 #if BUILDFLAG(IS_IOS)
 BASE_FEATURE(kIosDetectUsernameInUff,
              "IosSaveUsernameInUff",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kIOSProactivePasswordGenerationBottomSheet,
              "kIOSProactivePasswordGenerationBottomSheet",
diff --git a/components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.cc b/components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.cc
new file mode 100644
index 0000000..8f2f48f
--- /dev/null
+++ b/components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.cc
@@ -0,0 +1,28 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.h"
+
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+
+namespace password_manager {
+
+FirstCctPageLoadPasswordsUkmRecorder::FirstCctPageLoadPasswordsUkmRecorder(
+    ukm::SourceId source_id)
+    : ukm_entry_builder_(
+          std::make_unique<ukm::builders::PasswordManager_FirstCCTPageLoad>(
+              source_id)) {
+  ukm_entry_builder_->SetHasPasswordForm(false);
+}
+
+FirstCctPageLoadPasswordsUkmRecorder::~FirstCctPageLoadPasswordsUkmRecorder() {
+  ukm_entry_builder_->Record(ukm::UkmRecorder::Get());
+}
+
+void FirstCctPageLoadPasswordsUkmRecorder::RecordHasPasswordForm() {
+  ukm_entry_builder_->SetHasPasswordForm(true);
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.h b/components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.h
new file mode 100644
index 0000000..7591a32
--- /dev/null
+++ b/components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.h
@@ -0,0 +1,45 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FIRST_CCT_PAGE_LOAD_PASSWORDS_UKM_RECORDER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FIRST_CCT_PAGE_LOAD_PASSWORDS_UKM_RECORDER_H_
+
+#include "services/metrics/public/cpp/ukm_source_id.h"
+
+namespace ukm {
+namespace builders {
+class PasswordManager_FirstCCTPageLoad;
+}
+}  // namespace ukm
+
+namespace password_manager {
+
+// Reports password manager UKM for CCTs. The metrics are cached during its
+// lifetime and reported on destruction. Created only for custom tabs
+// on Android.
+class FirstCctPageLoadPasswordsUkmRecorder {
+ public:
+  // Records UKM metrics and reports them on destruction.
+  explicit FirstCctPageLoadPasswordsUkmRecorder(ukm::SourceId source_id);
+  FirstCctPageLoadPasswordsUkmRecorder(
+      const FirstCctPageLoadPasswordsUkmRecorder&) = delete;
+  FirstCctPageLoadPasswordsUkmRecorder& operator=(
+      const FirstCctPageLoadPasswordsUkmRecorder&) = delete;
+
+  ~FirstCctPageLoadPasswordsUkmRecorder();
+
+  // Records that the page has a password form. Only used for the first page
+  // load of a CCT on Android.
+  void RecordHasPasswordForm();
+
+ private:
+  // Records URL keyed metrics (UKMs) for the first CCT page load upon its
+  // destruction. Not constructed on subsequent page loads.
+  std::unique_ptr<ukm::builders::PasswordManager_FirstCCTPageLoad>
+      ukm_entry_builder_;
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FIRST_CCT_PAGE_LOAD_PASSWORDS_UKM_RECORDER_H_
diff --git a/components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder_unittest.cc b/components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder_unittest.cc
new file mode 100644
index 0000000..2be56a6
--- /dev/null
+++ b/components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.h"
+
+#include "base/test/task_environment.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+namespace {
+
+const ukm::mojom::UkmEntry* GetMetricEntry(
+    const ukm::TestUkmRecorder& test_ukm_recorder,
+    std::string_view entry) {
+  std::vector<raw_ptr<const ukm::mojom::UkmEntry, VectorExperimental>>
+      ukm_entries = test_ukm_recorder.GetEntriesByName(entry);
+  EXPECT_EQ(1u, ukm_entries.size());
+  return ukm_entries[0];
+}
+
+}  // namespace
+
+class FirstCctPageLoadPasswordsUkmRecorderTest : public testing::Test {
+ public:
+  ~FirstCctPageLoadPasswordsUkmRecorderTest() override = default;
+
+ private:
+  // Needed by `TestUkmRecorder`;
+  base::test::SingleThreadTaskEnvironment task_environment_;
+};
+
+TEST_F(FirstCctPageLoadPasswordsUkmRecorderTest, RecordsHasNoPwdFormIfNotSet) {
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+  auto first_cct_page_recorder =
+      std::make_unique<FirstCctPageLoadPasswordsUkmRecorder>(ukm::SourceId(1));
+  first_cct_page_recorder.reset();
+
+  ukm::TestUkmRecorder::ExpectEntryMetric(
+      GetMetricEntry(
+          test_ukm_recorder,
+          ukm::builders::PasswordManager_FirstCCTPageLoad::kEntryName),
+      ukm::builders::PasswordManager_FirstCCTPageLoad::kHasPasswordFormName, 0);
+}
+
+TEST_F(FirstCctPageLoadPasswordsUkmRecorderTest, RecordsHasPwdFormIfSet) {
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+  auto first_cct_page_recorder =
+      std::make_unique<FirstCctPageLoadPasswordsUkmRecorder>(ukm::SourceId(1));
+  first_cct_page_recorder->RecordHasPasswordForm();
+  first_cct_page_recorder.reset();
+
+  ukm::TestUkmRecorder::ExpectEntryMetric(
+      GetMetricEntry(
+          test_ukm_recorder,
+          ukm::builders::PasswordManager_FirstCCTPageLoad::kEntryName),
+      ukm::builders::PasswordManager_FirstCCTPageLoad::kHasPasswordFormName, 1);
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/mock_password_form_cache.h b/components/password_manager/core/browser/mock_password_form_cache.h
index f31acab..aa19dba 100644
--- a/components/password_manager/core/browser/mock_password_form_cache.h
+++ b/components/password_manager/core/browser/mock_password_form_cache.h
@@ -16,12 +16,12 @@
   ~MockPasswordFormCache() override;
   MockPasswordFormCache(const MockPasswordFormCache&) = delete;
   MockPasswordFormCache& operator=(const MockPasswordFormCache&) = delete;
-  MOCK_METHOD(bool,
-              HasPasswordForm,
+  MOCK_METHOD(const PasswordForm*,
+              GetPasswordForm,
               (PasswordManagerDriver*, autofill::FormRendererId),
               (const override));
-  MOCK_METHOD(bool,
-              HasPasswordForm,
+  MOCK_METHOD(const PasswordForm*,
+              GetPasswordForm,
               (PasswordManagerDriver*, autofill::FieldRendererId),
               (const override));
 };
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index 408c5b21..4f90e952 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -24,6 +24,7 @@
 #include "base/test/task_environment.h"
 #include "base/timer/elapsed_timer.h"
 #include "build/build_config.h"
+#include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/filling_product.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
@@ -73,15 +74,13 @@
 #include "components/webauthn/android/webauthn_cred_man_delegate.h"
 #endif
 
-// The name of the username/password element in the form.
-const char16_t kInvalidUsername[] = u"no-username";
+namespace autofill {
+class AutofillSuggestionDelegate;
+}
 
-const char16_t kAliceUsername[] = u"alice";
-const char16_t kAlicePassword[] = u"password";
-const char16_t kAliceAccountStoredPassword[] = u"account-stored-password";
+namespace password_manager {
 
-constexpr auto kDefaultTriggerSource =
-    autofill::AutofillSuggestionTriggerSource::kFormControlElementClicked;
+namespace {
 
 using autofill::FillingProduct;
 using autofill::Suggestion;
@@ -95,6 +94,7 @@
 using gfx::test::AreImagesEqual;
 using testing::_;
 using testing::AllOf;
+using testing::DoAll;
 using testing::ElementsAre;
 using testing::Eq;
 using testing::Field;
@@ -102,21 +102,20 @@
 using testing::NiceMock;
 using testing::Return;
 using testing::ReturnRef;
+using testing::SaveArg;
 using testing::SizeIs;
 using testing::Unused;
 
+using ReauthSucceeded = PasswordManagerClient::ReauthSucceeded;
 using SuggestionPosition =
     autofill::AutofillSuggestionDelegate::SuggestionPosition;
 using UkmEntry = ukm::builders::PageWithPassword;
 
-namespace autofill {
-class AutofillSuggestionDelegate;
-}
-
-namespace password_manager {
-namespace {
-
-using ReauthSucceeded = PasswordManagerClient::ReauthSucceeded;
+// The name of the username/password element in the form.
+const char16_t kInvalidUsername[] = u"no-username";
+const char16_t kAliceUsername[] = u"alice";
+const char16_t kAlicePassword[] = u"password";
+const char16_t kAliceAccountStoredPassword[] = u"account-stored-password";
 
 constexpr char kMainFrameUrl[] = "https://example.com/";
 constexpr char kDropdownSelectedHistogram[] =
@@ -125,6 +124,10 @@
     "PasswordManager.PasswordDropdownShown";
 constexpr char kCredentialsCountFromAccountStoreAfterUnlockHistogram[] =
     "PasswordManager.CredentialsCountFromAccountStoreAfterUnlock";
+
+constexpr auto kDefaultTriggerSource =
+    autofill::AutofillSuggestionTriggerSource::kFormControlElementClicked;
+
 const gfx::Image kTestFavicon = gfx::test::CreateImage(16, 16);
 
 const Suggestion::Text kSeparatorEntry(u"", Suggestion::Text::IsPrimary(false));
@@ -220,7 +223,7 @@
 class MockAutofillClient : public autofill::TestAutofillClient {
  public:
   MockAutofillClient() = default;
-  MOCK_METHOD(void,
+  MOCK_METHOD(AutofillClient::SuggestionUiSessionId,
               ShowAutofillSuggestions,
               (const autofill::AutofillClient::PopupOpenArgs& open_args,
                base::WeakPtr<autofill::AutofillSuggestionDelegate> delegate),
@@ -289,6 +292,12 @@
   std::vector<Suggestion> autofill_suggestions_;
 };
 
+// Copies the first parameter into `args` and returns a (empty) identifier.
+auto SavePopupOpenArgs(autofill::AutofillClient::PopupOpenArgs& args) {
+  return DoAll(SaveArg<0>(&args),
+               Return(autofill::AutofillClient::SuggestionUiSessionId()));
+}
+
 base::CancelableTaskTracker::TaskId
 RespondWithTestIcon(Unused, FaviconImageCallback callback, Unused) {
   favicon_base::FaviconImageResult image_result;
@@ -438,7 +447,7 @@
   // Show the popup and verify the suggestions.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -498,7 +507,7 @@
   // Show the popup and verify local and account-stored suggestion coexist.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
       std::u16string(), ShowWebAuthnCredentials(false), gfx::RectF());
@@ -545,7 +554,7 @@
   // Show the popup and verify the suggestions.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
       std::u16string(), ShowWebAuthnCredentials(false), gfx::RectF());
@@ -570,7 +579,7 @@
   // Show the popup and verify the suggestions.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   EXPECT_CALL(*client.mock_driver(), CanShowAutofillUi).WillOnce(Return(true));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -592,7 +601,7 @@
   // Show the popup and verify the suggestions.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
       std::u16string(), ShowWebAuthnCredentials(false), gfx::RectF());
@@ -959,7 +968,7 @@
   // should be the user name; the 'label' should be the realm.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
       std::u16string(), ShowWebAuthnCredentials(false), element_bounds);
@@ -980,7 +989,7 @@
 
   // Now simulate displaying suggestions matching "John".
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT, u"John",
       ShowWebAuthnCredentials(false), element_bounds);
@@ -994,7 +1003,7 @@
 
   // Finally, simulate displaying all suggestions, without any prefix matching.
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT, u"",
       ShowWebAuthnCredentials(false), element_bounds);
@@ -1031,7 +1040,7 @@
 
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
       std::u16string(), ShowWebAuthnCredentials(false), gfx::RectF());
@@ -1060,7 +1069,7 @@
 
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
       test_username_, ShowWebAuthnCredentials(false), element_bounds);
@@ -1133,7 +1142,7 @@
 
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1199,7 +1208,7 @@
 
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
       test_username_, ShowWebAuthnCredentials(false), element_bounds);
@@ -1249,7 +1258,7 @@
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_GENERATE_PASSWORD);
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   EXPECT_TRUE(
       password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
           element_bounds, base::i18n::RIGHT_TO_LEFT,
@@ -1305,7 +1314,7 @@
 
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   EXPECT_TRUE(
       password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
@@ -1347,7 +1356,7 @@
   auto regular_generate_id = autofill::SuggestionType::kGeneratePasswordEntry;
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
       gfx::RectF(), base::i18n::RIGHT_TO_LEFT,
@@ -1375,7 +1384,7 @@
 
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
       std::u16string(), ShowWebAuthnCredentials(false), element_bounds);
@@ -1397,7 +1406,7 @@
   // Show the popup
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1434,7 +1443,7 @@
   // Show the popup
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1485,7 +1494,7 @@
   // Show the popup
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1534,7 +1543,7 @@
   // Show the popup
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1579,7 +1588,7 @@
   // Show the popup
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1625,7 +1634,7 @@
   // Show the popup
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
 
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1778,7 +1787,7 @@
   // Show password suggestions including WebAuthn credentials.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   gfx::RectF element_bounds;
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1859,7 +1868,7 @@
   // Show password suggestions including WebAuthn credentials.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   gfx::RectF element_bounds;
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1893,7 +1902,7 @@
   // Show password suggestions including WebAuthn credentials.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   gfx::RectF element_bounds;
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1940,7 +1949,7 @@
   // Show webauthn suggestions with the correct favicon.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   gfx::RectF element_bounds;
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -1979,7 +1988,7 @@
 
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   gfx::RectF element_bounds;
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
@@ -2122,7 +2131,7 @@
   // Show suggestions including WebAuthn credentials.
   autofill::AutofillClient::PopupOpenArgs open_args;
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   password_autofill_manager_->OnShowPasswordSuggestions(
       kElementId, kDefaultTriggerSource, base::i18n::RIGHT_TO_LEFT,
       /*typed_username=*/u"", ShowWebAuthnCredentials(true), gfx::RectF());
@@ -2148,7 +2157,7 @@
 
   // Show suggestions again.
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   EXPECT_CALL(*webauthn_credentials_delegate_, HasPendingPasskeySelection)
       .WillOnce(Return(true));
   password_autofill_manager_->OnShowPasswordSuggestions(
@@ -2166,7 +2175,7 @@
   std::move(hide_callback).Run();
 
   EXPECT_CALL(autofill_client, ShowAutofillSuggestions)
-      .WillOnce(testing::SaveArg<0>(&open_args));
+      .WillOnce(SavePopupOpenArgs(open_args));
   EXPECT_CALL(*webauthn_credentials_delegate_, HasPendingPasskeySelection)
       .WillOnce(Return(false));
   password_autofill_manager_->OnShowPasswordSuggestions(
diff --git a/components/password_manager/core/browser/password_form_cache.h b/components/password_manager/core/browser/password_form_cache.h
index a9bf36c6..f6e4b40 100644
--- a/components/password_manager/core/browser/password_form_cache.h
+++ b/components/password_manager/core/browser/password_form_cache.h
@@ -9,6 +9,7 @@
 
 namespace password_manager {
 class PasswordManagerDriver;
+struct PasswordForm;
 
 // Contains information about password forms detected on a web page.
 class PasswordFormCache {
@@ -18,13 +19,16 @@
   PasswordFormCache(const PasswordFormCache&) = delete;
   PasswordFormCache& operator=(const PasswordFormCache&) = delete;
 
-  // Checks if this cache contains a password form identified by the `form_id`.
-  virtual bool HasPasswordForm(PasswordManagerDriver* driver,
-                               autofill::FormRendererId form_id) const = 0;
-  // Checks if this cache contains a password form with a field identified by
-  // the `field_id`.
-  virtual bool HasPasswordForm(PasswordManagerDriver* driver,
-                               autofill::FieldRendererId field_id) const = 0;
+  // If present, returns a `PasswordForm` for the given `driver` and `form_id`,
+  // and `nullptr` otherwise.
+  virtual const PasswordForm* GetPasswordForm(
+      PasswordManagerDriver* driver,
+      autofill::FormRendererId form_id) const = 0;
+  // If present, returns a `PasswordForm` for the given `driver` and `field_id`,
+  // and `nullptr` otherwise.
+  virtual const PasswordForm* GetPasswordForm(
+      PasswordManagerDriver* driver,
+      autofill::FieldRendererId field_id) const = 0;
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_form_cache_impl.cc b/components/password_manager/core/browser/password_form_cache_impl.cc
index 0d332303..4b5304e 100644
--- a/components/password_manager/core/browser/password_form_cache_impl.cc
+++ b/components/password_manager/core/browser/password_form_cache_impl.cc
@@ -4,6 +4,7 @@
 
 #include "components/password_manager/core/browser/password_form_cache_impl.h"
 
+#include "components/password_manager/core/browser/password_form_manager.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
 #include "components/password_manager/core/browser/password_save_manager_impl.h"
 
@@ -13,16 +14,18 @@
 
 PasswordFormCacheImpl::~PasswordFormCacheImpl() = default;
 
-bool PasswordFormCacheImpl::HasPasswordForm(
+const PasswordForm* PasswordFormCacheImpl::GetPasswordForm(
     PasswordManagerDriver* driver,
     autofill::FormRendererId form_id) const {
-  return GetMatchedManager(driver, form_id) != nullptr;
+  const PasswordFormManager* form_manager = GetMatchedManager(driver, form_id);
+  return form_manager ? form_manager->GetParsedObservedForm() : nullptr;
 }
 
-bool PasswordFormCacheImpl::HasPasswordForm(
+const PasswordForm* PasswordFormCacheImpl::GetPasswordForm(
     PasswordManagerDriver* driver,
     autofill::FieldRendererId field_id) const {
-  return GetMatchedManager(driver, field_id) != nullptr;
+  const PasswordFormManager* form_manager = GetMatchedManager(driver, field_id);
+  return form_manager ? form_manager->GetParsedObservedForm() : nullptr;
 }
 
 PasswordFormManager* PasswordFormCacheImpl::GetMatchedManager(
diff --git a/components/password_manager/core/browser/password_form_cache_impl.h b/components/password_manager/core/browser/password_form_cache_impl.h
index 0201716..d401fea 100644
--- a/components/password_manager/core/browser/password_form_cache_impl.h
+++ b/components/password_manager/core/browser/password_form_cache_impl.h
@@ -42,10 +42,12 @@
 
  private:
   // PasswordFormCache:
-  bool HasPasswordForm(PasswordManagerDriver* driver,
-                       autofill::FormRendererId form_id) const override;
-  bool HasPasswordForm(PasswordManagerDriver* driver,
-                       autofill::FieldRendererId field_id) const override;
+  const PasswordForm* GetPasswordForm(
+      PasswordManagerDriver* driver,
+      autofill::FormRendererId form_id) const override;
+  const PasswordForm* GetPasswordForm(
+      PasswordManagerDriver* driver,
+      autofill::FieldRendererId field_id) const override;
 
   // TODO(b/330313855): Check if `unique_ptr` can be removed here.
   std::vector<std::unique_ptr<PasswordFormManager>> form_managers_;
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 38dd058..85997b2 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -62,6 +62,7 @@
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/build_info.h"
+#include "components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.h"
 #include "components/password_manager/core/browser/password_feature_manager.h"
 #include "components/password_manager/core/browser/password_sync_util.h"
 #endif  // BUILDFLAG(IS_ANDROID)
@@ -1205,6 +1206,13 @@
 void PasswordManager::OnPasswordFormsRendered(
     password_manager::PasswordManagerDriver* driver,
     const std::vector<FormData>& visible_forms_data) {
+#if BUILDFLAG(IS_ANDROID)
+  FirstCctPageLoadPasswordsUkmRecorder* cct_ukm_recorder =
+      client_->GetFirstCctPageLoadUkmRecorder();
+  if (cct_ukm_recorder) {
+    cct_ukm_recorder->RecordHasPasswordForm();
+  }
+#endif
   CreatePendingLoginManagers(driver, visible_forms_data);
   std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
   if (password_manager_util::IsLoggingActive(client_)) {
diff --git a/components/password_manager/core/browser/password_manager_client.h b/components/password_manager/core/browser/password_manager_client.h
index dd520703..78fd8ac3 100644
--- a/components/password_manager/core/browser/password_manager_client.h
+++ b/components/password_manager/core/browser/password_manager_client.h
@@ -37,6 +37,10 @@
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) ||
         // BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(IS_ANDROID)
+#include "components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
 class PrefService;
 
 namespace affiliations {
@@ -92,6 +96,9 @@
 namespace password_manager {
 
 class FieldInfoManager;
+#if BUILDFLAG(IS_ANDROID)
+class FirstCctPageLoadPasswordsUkmRecorder;
+#endif  // BUILDFLAG(IS_ANDROID)
 class PasswordFeatureManager;
 class PasswordFormManagerForUI;
 class PasswordManagerDriver;
@@ -458,6 +465,16 @@
   // does not support metrics recording.
   virtual PasswordManagerMetricsRecorder* GetMetricsRecorder() = 0;
 
+#if BUILDFLAG(IS_ANDROID)
+  // Returns a metrics recorder created specifically for the first CCT page
+  // load. This can return nullptr if the current tab is not a CCT, or if
+  // the user already navigated away from the first page.
+  // It records metrics on destruction, which happens on the first navigation
+  // away from the first loaded page. Callers should  not hold on to the
+  // pointer.
+  virtual FirstCctPageLoadPasswordsUkmRecorder*
+  GetFirstCctPageLoadUkmRecorder() = 0;
+#endif
   // Gets the PasswordRequirementsService associated with the client. It is
   // valid that this method returns a nullptr if the PasswordRequirementsService
   // has not been implemented for a specific platform or the context is an
diff --git a/components/password_manager/core/browser/password_manager_driver.h b/components/password_manager/core/browser/password_manager_driver.h
index e7545ea..a0e1592 100644
--- a/components/password_manager/core/browser/password_manager_driver.h
+++ b/components/password_manager/core/browser/password_manager_driver.h
@@ -89,10 +89,18 @@
   // Tells the renderer to fill the given `value` into the triggering field.
   virtual void FillField(const std::u16string& value) {}
 
-  // Tells the driver to fill the form with the `username` and `password`.
+  // Tells the driver to fill the currently focused form with the `username` and
+  // `password`.
   virtual void FillSuggestion(const std::u16string& username,
                               const std::u16string& password) = 0;
 
+  // Similar to `FillSuggestion` but also passes the FieldRendererIds of the
+  // elements to be filled.
+  virtual void FillSuggestionById(autofill::FieldRendererId username_element_id,
+                                  autofill::FieldRendererId password_element_id,
+                                  const std::u16string& username,
+                                  const std::u16string& password) = 0;
+
   // Tells the renderer to fill the given credential into the focused element.
   // Always calls `completed_callback` with a status indicating success/error.
   virtual void FillIntoFocusedField(
@@ -120,6 +128,14 @@
   virtual void PreviewSuggestion(const std::u16string& username,
                                  const std::u16string& password) = 0;
 
+  // Similar to `PreviewSuggestion` but also passes the FieldRendererIds of the
+  // elements to be previewed.
+  virtual void PreviewSuggestionById(
+      autofill::FieldRendererId username_element_id,
+      autofill::FieldRendererId password_element_id,
+      const std::u16string& username,
+      const std::u16string& password) = 0;
+
   // Tells the driver to preview a password generation suggestion.
   virtual void PreviewGenerationSuggestion(const std::u16string& password) = 0;
 
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index e14e7b2..c66431ea 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -68,11 +68,13 @@
 #include "components/ukm/test_ukm_recorder.h"
 #include "net/cert/cert_status_flags.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/test/test_network_context.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_ANDROID)
+#include "components/password_manager/core/browser/first_cct_page_load_passwords_ukm_recorder.h"
 #include "components/webauthn/android/cred_man_support.h"
 #include "components/webauthn/android/webauthn_cred_man_delegate.h"
 #endif  // BUILDFLAG(IS_ANDROID)
@@ -247,6 +249,12 @@
               GetMetricsRecorder,
               (),
               (override));
+#if BUILDFLAG(IS_ANDROID)
+  MOCK_METHOD(FirstCctPageLoadPasswordsUkmRecorder*,
+              GetFirstCctPageLoadUkmRecorder,
+              (),
+              (override));
+#endif  // BUILDFLAG(IS_ANDROID)
   MOCK_METHOD(bool, IsNewTabPage, (), (const, override));
   MOCK_METHOD(profile_metrics::BrowserProfileType,
               GetProfileType,
@@ -909,6 +917,9 @@
   FieldRendererId username_element = form_data.fields()[0].renderer_id();
   FieldRendererId generation_element = form_data.fields()[1].renderer_id();
 
+  EXPECT_CALL(driver_, GetLastCommittedURL)
+      .WillRepeatedly(ReturnRef(form_data.url()));
+
   // A form is found by PasswordManager.
   manager()->OnPasswordFormsParsed(&driver_, {form_data});
 
@@ -6101,6 +6112,27 @@
   histogram_tester.ExpectTotalCount(
       "PasswordManager.FormSubmissionsVsSavePrompts", 0);
 }
+
+TEST_P(PasswordManagerTest, MarksHasPasswordFormForFirstCctPageLoad) {
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+  auto first_cct_page_recorder =
+      std::make_unique<FirstCctPageLoadPasswordsUkmRecorder>(ukm::SourceId(1));
+
+  ON_CALL(client_, GetFirstCctPageLoadUkmRecorder)
+      .WillByDefault(Return(first_cct_page_recorder.get()));
+
+  FormData form_data(MakeSimpleFormData());
+  std::vector<FormData> observed;
+  observed.push_back(std::move(form_data));
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed);
+  // Destroy the recorder as it records metrics on destruction.
+  first_cct_page_recorder.reset();
+  CheckMetricHasValue(
+      test_ukm_recorder,
+      ukm::builders::PasswordManager_FirstCCTPageLoad::kEntryName,
+      ukm::builders::PasswordManager_FirstCCTPageLoad::kHasPasswordFormName, 1);
+}
 #endif
 
 INSTANTIATE_TEST_SUITE_P(, PasswordManagerTest, ::testing::Bool());
diff --git a/components/password_manager/core/browser/password_manual_fallback_flow.cc b/components/password_manager/core/browser/password_manual_fallback_flow.cc
index 3f75fdb..0d6e104 100644
--- a/components/password_manager/core/browser/password_manual_fallback_flow.cc
+++ b/components/password_manager/core/browser/password_manual_fallback_flow.cc
@@ -7,6 +7,7 @@
 #include <optional>
 
 #include "base/check.h"
+#include "base/check_deref.h"
 #include "base/containers/contains.h"
 #include "base/functional/bind.h"
 #include "base/ranges/algorithm.h"
@@ -184,7 +185,7 @@
 void PasswordManualFallbackFlow::OnSuggestionsShown(
     base::span<const Suggestion> suggestions) {
   manual_fallback_metrics_recorder_->OnDidShowSuggestions(
-      password_form_cache_->HasPasswordForm(password_manager_driver_,
+      password_form_cache_->GetPasswordForm(password_manager_driver_,
                                             field_id_));
 }
 
@@ -197,12 +198,20 @@
     return;
   }
   switch (suggestion.type) {
-    case autofill::SuggestionType::kPasswordEntry:
-      password_manager_driver_->PreviewSuggestion(
+    case autofill::SuggestionType::kPasswordEntry: {
+      const PasswordForm* form = password_form_cache_->GetPasswordForm(
+          password_manager_driver_, field_id_);
+      if (!form) {
+        return;
+      }
+      password_manager_driver_->PreviewSuggestionById(
+          form->username_element_renderer_id,
+          form->password_element_renderer_id,
           GetUsernameFromLabel(suggestion.labels[0][0].value),
           suggestion.GetPayload<Suggestion::PasswordSuggestionDetails>()
               .password);
       break;
+    }
     case autofill::SuggestionType::kPasswordFieldByFieldFilling:
       password_manager_driver_->PreviewField(field_id_,
                                              suggestion.main_text.value);
@@ -225,12 +234,15 @@
   if (!suggestion.is_acceptable) {
     return;
   }
+  const PasswordForm* const form = password_form_cache_->GetPasswordForm(
+      password_manager_driver_, field_id_);
   manual_fallback_metrics_recorder_->OnDidFillSuggestion(
-      password_form_cache_->HasPasswordForm(password_manager_driver_,
-                                            field_id_));
-
+      IsTriggerFieldRelevantInPasswordForm(form));
   switch (suggestion.type) {
     case autofill::SuggestionType::kPasswordEntry: {
+      if (!form) {
+        return;
+      }
       Suggestion::PasswordSuggestionDetails payload =
           suggestion.GetPayload<Suggestion::PasswordSuggestionDetails>();
       EnsureCrossDomainPasswordUsageGetsConsent(
@@ -238,8 +250,10 @@
               &PasswordManualFallbackFlow::MaybeAuthenticateBeforeFilling,
               weak_ptr_factory_.GetWeakPtr(),
               base::BindOnce(
-                  &PasswordManagerDriver::FillSuggestion,
+                  &PasswordManagerDriver::FillSuggestionById,
                   base::Unretained(password_manager_driver_),
+                  form->username_element_renderer_id,
+                  form->password_element_renderer_id,
                   GetUsernameFromLabel(suggestion.labels[0][0].value),
                   payload.password)));
       break;
@@ -262,12 +276,12 @@
     case autofill::SuggestionType::kViewPasswordDetails: {
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || \
     BUILDFLAG(IS_CHROMEOS)
-      std::optional<password_manager::PasswordForm> form =
+      std::optional<password_manager::PasswordForm> credentials =
           GetCorrespondingPasswordForm(
               suggestion.GetPayload<Suggestion::PasswordSuggestionDetails>(),
               *passwords_presenter_);
-      if (form) {
-        password_client_->OpenPasswordDetailsBubble(*form);
+      if (credentials) {
+        password_client_->OpenPasswordDetailsBubble(*credentials);
       }
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) ||
         // BUILDFLAG(IS_CHROMEOS)
@@ -314,16 +328,20 @@
 void PasswordManualFallbackFlow::RunFlowImpl(
     const gfx::RectF& bounds,
     base::i18n::TextDirection text_direction) {
-  IsTriggeredOnPasswordForm on_password_form(
-      password_form_cache_->HasPasswordForm(password_manager_driver_,
-                                            field_id_));
-  // TODO(b/331409076): Fetch suggested passwords and pass them to the
-  // suggestion generator.
+  const PasswordForm* const password_form =
+      password_form_cache_->GetPasswordForm(password_manager_driver_,
+                                            field_id_);
+  // Generate suggestions for the given context. IsTriggeredOnPasswordForm is
+  // targeting contexts where the focused field is a relevant field in the
+  // parsed password form and the form contains at most one password field.
   std::vector<Suggestion> suggestions =
       suggestion_generator_.GetManualFallbackSuggestions(
           form_fetcher_->GetBestMatches(),
           base::make_span(passwords_presenter_->GetSavedPasswords()),
-          on_password_form);
+          IsTriggeredOnPasswordForm(
+              password_form &&
+              IsTriggerFieldRelevantInPasswordForm(password_form) &&
+              !password_form->HasNewPasswordElement()));
   // TODO(crbug.com/41474723): Set the right `form_control_ax_id`.
   autofill::AutofillClient::PopupOpenArgs open_args(
       bounds, text_direction, std::move(suggestions),
@@ -404,4 +422,11 @@
   std::move(on_allowed).Run();
 }
 
+bool PasswordManualFallbackFlow::IsTriggerFieldRelevantInPasswordForm(
+    const PasswordForm* password_form) const {
+  return password_form &&
+         (password_form->username_element_renderer_id == field_id_ ||
+          password_form->password_element_renderer_id == field_id_);
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_manual_fallback_flow.h b/components/password_manager/core/browser/password_manual_fallback_flow.h
index 193e8de..1c1ff9ae 100644
--- a/components/password_manager/core/browser/password_manual_fallback_flow.h
+++ b/components/password_manager/core/browser/password_manual_fallback_flow.h
@@ -131,6 +131,12 @@
       const autofill::Suggestion::PasswordSuggestionDetails& payload,
       base::OnceClosure on_allowed);
 
+  // Returns whether `field_id_` represents a username or password field in
+  // `password_form_`. This only considers the username and password fields
+  // stored in the `PasswordForm` object.
+  bool IsTriggerFieldRelevantInPasswordForm(
+      const PasswordForm* password_form) const;
+
   const PasswordSuggestionGenerator suggestion_generator_;
   const raw_ptr<PasswordManagerDriver> password_manager_driver_;
   const raw_ptr<autofill::AutofillClient> autofill_client_;
diff --git a/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc b/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
index 768a528..ab8011e 100644
--- a/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
+++ b/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
@@ -34,6 +34,8 @@
 
 namespace password_manager {
 
+namespace {
+
 using autofill::AutofillClient;
 using autofill::AutofillSuggestionDelegate;
 using autofill::AutofillSuggestionTriggerSource;
@@ -51,6 +53,7 @@
 using testing::ByMove;
 using testing::ElementsAre;
 using testing::Field;
+using testing::Matcher;
 using testing::NiceMock;
 using testing::Return;
 using testing::ReturnRef;
@@ -64,6 +67,13 @@
 constexpr char kShowSuggestionLatency[] =
     "PasswordManager.ManualFallback.ShowSuggestions.Latency";
 
+Matcher<Suggestion> EqualsManualFallbackSuggestion(SuggestionType type,
+                                                   bool is_acceptable) {
+  return AllOf(
+      Field("type", &Suggestion::type, type),
+      Field("is_acceptable", &Suggestion::is_acceptable, is_acceptable));
+}
+
 Suggestion::PasswordSuggestionDetails CreateTestPasswordDetails() {
   return Suggestion::PasswordSuggestionDetails(
       u"username", u"password", "https://google.com/", u"google.com",
@@ -74,7 +84,7 @@
  public:
   MockAutofillClient() = default;
   ~MockAutofillClient() override = default;
-  MOCK_METHOD(void,
+  MOCK_METHOD(autofill::AutofillClient::SuggestionUiSessionId,
               ShowAutofillSuggestions,
               (const AutofillClient::PopupOpenArgs&,
                base::WeakPtr<AutofillSuggestionDelegate>),
@@ -101,6 +111,20 @@
               FillSuggestion,
               (const std::u16string&, const std::u16string&),
               (override));
+  MOCK_METHOD(void,
+              FillSuggestionById,
+              (FieldRendererId,
+               FieldRendererId,
+               const std::u16string&,
+               const std::u16string&),
+              (override));
+  MOCK_METHOD(void,
+              PreviewSuggestionById,
+              (FieldRendererId,
+               FieldRendererId,
+               const std::u16string&,
+               const std::u16string&),
+              (override));
   MOCK_METHOD(void, FillField, (const std::u16string&), (override));
   MOCK_METHOD(const GURL&, GetLastCommittedURL, (), (const override));
 };
@@ -603,11 +627,21 @@
   InitializeFlow();
   ProcessPasswordStoreUpdates();
 
-  flow().RunFlow(MakeFieldRendererId(), gfx::RectF{},
+  PasswordForm form;
+  form.username_element_renderer_id = MakeFieldRendererId();
+  form.password_element_renderer_id = MakeFieldRendererId();
+  // Simulate that the field is/isn't classified as target filling password.
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.username_element_renderer_id))
+      .WillRepeatedly(Return(&form));
+
+  flow().RunFlow(form.username_element_renderer_id, gfx::RectF{},
                  TextDirection::LEFT_TO_RIGHT);
 
-  EXPECT_CALL(driver(), PreviewSuggestion(std::u16string(u"username"),
-                                          std::u16string(u"password")));
+  EXPECT_CALL(driver(), PreviewSuggestionById(form.username_element_renderer_id,
+                                              form.password_element_renderer_id,
+                                              std::u16string(u"username"),
+                                              std::u16string(u"password")));
   Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
       SuggestionType::kPasswordEntry, u"google.com",
       CreateTestPasswordDetails());
@@ -625,11 +659,20 @@
   InitializeFlow();
   ProcessPasswordStoreUpdates();
 
-  flow().RunFlow(MakeFieldRendererId(), gfx::RectF{},
+  PasswordForm form;
+  form.password_element_renderer_id = MakeFieldRendererId();
+  // Simulate that the field is/isn't classified as target filling password.
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.password_element_renderer_id))
+      .WillRepeatedly(Return(&form));
+
+  flow().RunFlow(form.password_element_renderer_id, gfx::RectF{},
                  TextDirection::LEFT_TO_RIGHT);
 
-  EXPECT_CALL(driver(),
-              PreviewSuggestion(std::u16string(), std::u16string(u"password")));
+  EXPECT_CALL(driver(), PreviewSuggestionById(FieldRendererId(),
+                                              form.password_element_renderer_id,
+                                              std::u16string(),
+                                              std::u16string(u"password")));
   Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
       SuggestionType::kPasswordEntry, u"google.com",
       CreateTestPasswordDetails());
@@ -670,13 +713,23 @@
   InitializeFlow();
   ProcessPasswordStoreUpdates();
 
-  flow().RunFlow(MakeFieldRendererId(), gfx::RectF{},
+  PasswordForm form;
+  form.username_element_renderer_id = MakeFieldRendererId();
+  form.password_element_renderer_id = MakeFieldRendererId();
+  // Simulate that the field is/isn't classified as target filling password.
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.username_element_renderer_id))
+      .WillRepeatedly(Return(&form));
+
+  flow().RunFlow(form.username_element_renderer_id, gfx::RectF{},
                  TextDirection::LEFT_TO_RIGHT);
 
   EXPECT_CALL(password_manager_client(), IsReauthBeforeFillingRequired)
       .WillOnce(Return(false));
-  EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"username"),
-                                       std::u16string(u"password")));
+  EXPECT_CALL(driver(), FillSuggestionById(form.username_element_renderer_id,
+                                           form.password_element_renderer_id,
+                                           std::u16string(u"username"),
+                                           std::u16string(u"password")));
   Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
       SuggestionType::kPasswordEntry, u"google.com",
       CreateTestPasswordDetails());
@@ -696,7 +749,15 @@
   InitializeFlow();
   ProcessPasswordStoreUpdates();
 
-  flow().RunFlow(MakeFieldRendererId(), gfx::RectF{},
+  PasswordForm form;
+  form.username_element_renderer_id = MakeFieldRendererId();
+  form.password_element_renderer_id = MakeFieldRendererId();
+  // Simulate that the field is/isn't classified as target filling password.
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.username_element_renderer_id))
+      .WillRepeatedly(Return(&form));
+
+  flow().RunFlow(form.username_element_renderer_id, gfx::RectF{},
                  TextDirection::LEFT_TO_RIGHT);
 
   auto authenticator =
@@ -709,7 +770,7 @@
   EXPECT_CALL(password_manager_client(), GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(authenticator))));
 
-  EXPECT_CALL(driver(), FillSuggestion).Times(0);
+  EXPECT_CALL(driver(), FillSuggestionById).Times(0);
 
   base::HistogramTester histograms;
   base::ScopedMockElapsedTimersForTest mock_elapsed_timers_;
@@ -739,7 +800,15 @@
   InitializeFlow();
   ProcessPasswordStoreUpdates();
 
-  flow().RunFlow(MakeFieldRendererId(), gfx::RectF{},
+  PasswordForm form;
+  form.username_element_renderer_id = MakeFieldRendererId();
+  form.password_element_renderer_id = MakeFieldRendererId();
+  // Simulate that the field is/isn't classified as target filling password.
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.username_element_renderer_id))
+      .WillRepeatedly(Return(&form));
+
+  flow().RunFlow(form.username_element_renderer_id, gfx::RectF{},
                  TextDirection::LEFT_TO_RIGHT);
 
   auto authenticator =
@@ -752,8 +821,10 @@
   EXPECT_CALL(password_manager_client(), GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(authenticator))));
 
-  EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"username"),
-                                       std::u16string(u"password")));
+  EXPECT_CALL(driver(), FillSuggestionById(form.username_element_renderer_id,
+                                           form.password_element_renderer_id,
+                                           std::u16string(u"username"),
+                                           std::u16string(u"password")));
 
   base::HistogramTester histograms;
   base::ScopedMockElapsedTimersForTest mock_elapsed_timers_;
@@ -783,11 +854,20 @@
   InitializeFlow();
   ProcessPasswordStoreUpdates();
 
-  flow().RunFlow(MakeFieldRendererId(), gfx::RectF{},
+  PasswordForm form;
+  form.password_element_renderer_id = MakeFieldRendererId();
+  // Simulate that the field is/isn't classified as target filling password.
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.password_element_renderer_id))
+      .WillRepeatedly(Return(&form));
+
+  flow().RunFlow(form.password_element_renderer_id, gfx::RectF{},
                  TextDirection::LEFT_TO_RIGHT);
 
-  EXPECT_CALL(driver(),
-              FillSuggestion(std::u16string(), std::u16string(u"password")));
+  EXPECT_CALL(
+      driver(),
+      FillSuggestionById(FieldRendererId(), form.password_element_renderer_id,
+                         std::u16string(), std::u16string(u"password")));
   Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
       SuggestionType::kPasswordEntry, u"google.com",
       CreateTestPasswordDetails());
@@ -811,7 +891,7 @@
   flow().RunFlow(MakeFieldRendererId(), gfx::RectF{},
                  TextDirection::LEFT_TO_RIGHT);
 
-  EXPECT_CALL(driver(), FillSuggestion).Times(0);
+  EXPECT_CALL(driver(), FillSuggestionById).Times(0);
   Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
       SuggestionType::kPasswordEntry, u"google.com",
       CreateTestPasswordDetails());
@@ -911,7 +991,16 @@
   const GURL domain = driver().GetLastCommittedURL();
   const std::string password_origin = "password_origin";
 
-  flow().RunFlow(MakeFieldRendererId(), element_bounds, text_direction);
+  PasswordForm form;
+  form.username_element_renderer_id = MakeFieldRendererId();
+  form.password_element_renderer_id = MakeFieldRendererId();
+  // Simulate that the field is/isn't classified as target filling password.
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.username_element_renderer_id))
+      .WillRepeatedly(Return(&form));
+
+  flow().RunFlow(form.username_element_renderer_id, element_bounds,
+                 text_direction);
 
   EXPECT_CALL(
       password_manager_client(),
@@ -1151,17 +1240,123 @@
   }
 };
 
+// Tests that top level suggestions are acceptable when suggestions are
+// triggered on a login form.
+TEST_F(PasswordManualFallbackFlowTest, Acceptability_OnLoginForm) {
+  InitializeFlow();
+  ProcessPasswordStoreUpdates();
+
+  PasswordForm form;
+  form.username_element_renderer_id = MakeFieldRendererId();
+  form.password_element_renderer_id = MakeFieldRendererId();
+  // Simulate that the field is classified as target filling password.
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.username_element_renderer_id))
+      .WillRepeatedly(Return(&form));
+
+  EXPECT_CALL(
+      autofill_client(),
+      ShowAutofillSuggestions(
+          AllOf(Field(
+              "suggestions", &AutofillClient::PopupOpenArgs::suggestions,
+              ElementsAre(
+                  EqualsSuggestion(SuggestionType::kTitle),
+                  EqualsManualFallbackSuggestion(SuggestionType::kPasswordEntry,
+                                                 /*is_acceptable=*/true),
+                  EqualsSuggestion(SuggestionType::kTitle),
+                  EqualsManualFallbackSuggestion(SuggestionType::kPasswordEntry,
+                                                 /*is_acceptable=*/true),
+                  EqualsSuggestion(SuggestionType::kSeparator),
+                  EqualsSuggestion(SuggestionType::kAllSavedPasswordsEntry)))),
+          _));
+
+  flow().RunFlow(form.username_element_renderer_id, gfx::RectF{},
+                 TextDirection::LEFT_TO_RIGHT);
+}
+
+// Tests that top level suggestions are not acceptable when suggestions are
+// triggered on a signup form.
+TEST_F(PasswordManualFallbackFlowTest, Acceptability_OnSignupForm) {
+  InitializeFlow();
+  ProcessPasswordStoreUpdates();
+
+  PasswordForm form;
+  form.username_element_renderer_id = MakeFieldRendererId();
+  form.new_password_element_renderer_id = MakeFieldRendererId();
+  // Simulate that the field is classified as target filling password.
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.username_element_renderer_id))
+      .WillRepeatedly(Return(&form));
+
+  EXPECT_CALL(
+      autofill_client(),
+      ShowAutofillSuggestions(
+          AllOf(Field(
+              "suggestions", &AutofillClient::PopupOpenArgs::suggestions,
+              ElementsAre(
+                  EqualsSuggestion(SuggestionType::kTitle),
+                  EqualsManualFallbackSuggestion(SuggestionType::kPasswordEntry,
+                                                 /*is_acceptable=*/false),
+                  EqualsSuggestion(SuggestionType::kTitle),
+                  EqualsManualFallbackSuggestion(SuggestionType::kPasswordEntry,
+                                                 /*is_acceptable=*/false),
+                  EqualsSuggestion(SuggestionType::kSeparator),
+                  EqualsSuggestion(SuggestionType::kAllSavedPasswordsEntry)))),
+          _));
+
+  flow().RunFlow(form.username_element_renderer_id, gfx::RectF{},
+                 TextDirection::LEFT_TO_RIGHT);
+}
+
+// Tests that top level suggestions are not acceptable when suggestions are
+// triggered from a field that is not the main username or password field.
+TEST_F(PasswordManualFallbackFlowTest, Acceptability_IrrelevantFocusedElement) {
+  InitializeFlow();
+  ProcessPasswordStoreUpdates();
+
+  PasswordForm form;
+  form.username_element_renderer_id = MakeFieldRendererId();
+  form.password_element_renderer_id = MakeFieldRendererId();
+  FieldRendererId other_field_id = MakeFieldRendererId();
+  // Simulate that the field is classified as target filling password.
+  EXPECT_CALL(password_form_cache(), GetPasswordForm(_, other_field_id))
+      .WillRepeatedly(Return(&form));
+
+  EXPECT_CALL(
+      autofill_client(),
+      ShowAutofillSuggestions(
+          AllOf(Field(
+              "suggestions", &AutofillClient::PopupOpenArgs::suggestions,
+              ElementsAre(
+                  EqualsSuggestion(SuggestionType::kTitle),
+                  EqualsManualFallbackSuggestion(SuggestionType::kPasswordEntry,
+                                                 /*is_acceptable=*/false),
+                  EqualsSuggestion(SuggestionType::kTitle),
+                  EqualsManualFallbackSuggestion(SuggestionType::kPasswordEntry,
+                                                 /*is_acceptable=*/false),
+                  EqualsSuggestion(SuggestionType::kSeparator),
+                  EqualsSuggestion(SuggestionType::kAllSavedPasswordsEntry)))),
+          _));
+
+  flow().RunFlow(other_field_id, gfx::RectF{}, TextDirection::LEFT_TO_RIGHT);
+}
+
 TEST_P(PasswordManualFallbackFlowFillAfterSuggestionMetricsTest,
        MetricsAreRecorded) {
   InitializeFlow();
   ProcessPasswordStoreUpdates();
 
-  const FieldRendererId field_id = MakeFieldRendererId();
+  PasswordForm form;
+  form.username_element_renderer_id = MakeFieldRendererId();
+  form.password_element_renderer_id = MakeFieldRendererId();
   // Simulate that the field is/isn't classified as target filling password.
-  EXPECT_CALL(password_form_cache(), HasPasswordForm(_, field_id))
-      .WillRepeatedly(Return(IsClassifiedAsTargetFillingPassword()));
+  EXPECT_CALL(password_form_cache(),
+              GetPasswordForm(_, form.username_element_renderer_id))
+      .WillRepeatedly(
+          Return(IsClassifiedAsTargetFillingPassword() ? &form : nullptr));
 
-  flow().RunFlow(field_id, gfx::RectF{}, TextDirection::LEFT_TO_RIGHT);
+  flow().RunFlow(form.username_element_renderer_id, gfx::RectF{},
+                 TextDirection::LEFT_TO_RIGHT);
 
   base::HistogramTester histograms;
   autofill::Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
@@ -1190,4 +1385,6 @@
                                    : "_NotClassifiedAsTargetFilling"});
     });
 
+}  // namespace
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/stub_password_manager_client.cc b/components/password_manager/core/browser/stub_password_manager_client.cc
index 88368fd..2807566 100644
--- a/components/password_manager/core/browser/stub_password_manager_client.cc
+++ b/components/password_manager/core/browser/stub_password_manager_client.cc
@@ -153,6 +153,13 @@
   return base::OptionalToPtr(metrics_recorder_);
 }
 
+#if BUILDFLAG(IS_ANDROID)
+FirstCctPageLoadPasswordsUkmRecorder*
+StubPasswordManagerClient::GetFirstCctPageLoadUkmRecorder() {
+  return nullptr;
+}
+#endif
+
 signin::IdentityManager* StubPasswordManagerClient::GetIdentityManager() {
   return nullptr;
 }
diff --git a/components/password_manager/core/browser/stub_password_manager_client.h b/components/password_manager/core/browser/stub_password_manager_client.h
index d5cc2a7..e6aa630 100644
--- a/components/password_manager/core/browser/stub_password_manager_client.h
+++ b/components/password_manager/core/browser/stub_password_manager_client.h
@@ -99,6 +99,10 @@
 
   ukm::SourceId GetUkmSourceId() override;
   PasswordManagerMetricsRecorder* GetMetricsRecorder() override;
+#if BUILDFLAG(IS_ANDROID)
+  FirstCctPageLoadPasswordsUkmRecorder* GetFirstCctPageLoadUkmRecorder()
+      override;
+#endif
   signin::IdentityManager* GetIdentityManager() override;
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   network::mojom::NetworkContext* GetNetworkContext() const override;
diff --git a/components/password_manager/core/browser/stub_password_manager_driver.cc b/components/password_manager/core/browser/stub_password_manager_driver.cc
index 2b70dd4..e80971c 100644
--- a/components/password_manager/core/browser/stub_password_manager_driver.cc
+++ b/components/password_manager/core/browser/stub_password_manager_driver.cc
@@ -27,6 +27,12 @@
                                                const std::u16string& password) {
 }
 
+void StubPasswordManagerDriver::FillSuggestionById(
+    autofill::FieldRendererId username_element_id,
+    autofill::FieldRendererId password_element_id,
+    const std::u16string& username,
+    const std::u16string& password) {}
+
 #if BUILDFLAG(IS_ANDROID)
 void StubPasswordManagerDriver::TriggerFormSubmission() {}
 #endif
@@ -35,6 +41,12 @@
     const std::u16string& username,
     const std::u16string& password) {}
 
+void StubPasswordManagerDriver::PreviewSuggestionById(
+    autofill::FieldRendererId username_element_id,
+    autofill::FieldRendererId password_element_id,
+    const std::u16string& username,
+    const std::u16string& password) {}
+
 void StubPasswordManagerDriver::PreviewGenerationSuggestion(
     const std::u16string& password) {}
 
diff --git a/components/password_manager/core/browser/stub_password_manager_driver.h b/components/password_manager/core/browser/stub_password_manager_driver.h
index 8e6a3099..6a07ea4 100644
--- a/components/password_manager/core/browser/stub_password_manager_driver.h
+++ b/components/password_manager/core/browser/stub_password_manager_driver.h
@@ -31,11 +31,19 @@
   void FocusNextFieldAfterPasswords() override;
   void FillSuggestion(const std::u16string& username,
                       const std::u16string& password) override;
+  void FillSuggestionById(autofill::FieldRendererId username_element_id,
+                          autofill::FieldRendererId password_element_id,
+                          const std::u16string& username,
+                          const std::u16string& password) override;
 #if BUILDFLAG(IS_ANDROID)
   void TriggerFormSubmission() override;
 #endif
   void PreviewSuggestion(const std::u16string& username,
                          const std::u16string& password) override;
+  void PreviewSuggestionById(autofill::FieldRendererId username_element_id,
+                             autofill::FieldRendererId password_element_id,
+                             const std::u16string& username,
+                             const std::u16string& password) override;
   void PreviewGenerationSuggestion(const std::u16string& password) override;
   void ClearPreviewedForm() override;
   void SetSuggestionAvailability(autofill::FieldRendererId element_id,
diff --git a/components/password_manager/ios/ios_password_manager_driver.h b/components/password_manager/ios/ios_password_manager_driver.h
index 7a3185ec..d95139e 100644
--- a/components/password_manager/ios/ios_password_manager_driver.h
+++ b/components/password_manager/ios/ios_password_manager_driver.h
@@ -48,8 +48,16 @@
   void GeneratedPasswordAccepted(const std::u16string& password) override;
   void FillSuggestion(const std::u16string& username,
                       const std::u16string& password) override;
+  void FillSuggestionById(autofill::FieldRendererId username_element_id,
+                          autofill::FieldRendererId password_element_id,
+                          const std::u16string& username,
+                          const std::u16string& password) override;
   void PreviewSuggestion(const std::u16string& username,
                          const std::u16string& password) override;
+  void PreviewSuggestionById(autofill::FieldRendererId username_element_id,
+                             autofill::FieldRendererId password_element_id,
+                             const std::u16string& username,
+                             const std::u16string& password) override;
   void PreviewGenerationSuggestion(const std::u16string& password) override;
   void ClearPreviewedForm() override;
   void SetSuggestionAvailability(
diff --git a/components/password_manager/ios/ios_password_manager_driver.mm b/components/password_manager/ios/ios_password_manager_driver.mm
index 252cd74..82c5f50 100644
--- a/components/password_manager/ios/ios_password_manager_driver.mm
+++ b/components/password_manager/ios/ios_password_manager_driver.mm
@@ -80,12 +80,28 @@
   NOTIMPLEMENTED();
 }
 
+void IOSPasswordManagerDriver::FillSuggestionById(
+    autofill::FieldRendererId username_element_id,
+    autofill::FieldRendererId password_element_id,
+    const std::u16string& username,
+    const std::u16string& password) {
+  NOTIMPLEMENTED() << "This function is used for non-iOS manual fallback";
+}
+
 void IOSPasswordManagerDriver::PreviewSuggestion(
     const std::u16string& username,
     const std::u16string& password) {
   NOTIMPLEMENTED();
 }
 
+void IOSPasswordManagerDriver::PreviewSuggestionById(
+    autofill::FieldRendererId username_element_id,
+    autofill::FieldRendererId password_element_id,
+    const std::u16string& username,
+    const std::u16string& password) {
+  NOTIMPLEMENTED() << "This function is used for non-iOS manual fallback";
+}
+
 void IOSPasswordManagerDriver::PreviewGenerationSuggestion(
     const std::u16string& password) {
   NOTIMPLEMENTED();
diff --git a/components/plus_addresses/fake_plus_address_service.cc b/components/plus_addresses/fake_plus_address_service.cc
index 1adcbc2..aad152c 100644
--- a/components/plus_addresses/fake_plus_address_service.cc
+++ b/components/plus_addresses/fake_plus_address_service.cc
@@ -54,9 +54,13 @@
 void FakePlusAddressService::GetAffiliatedPlusProfiles(
     const url::Origin& origin,
     GetPlusProfilesCallback callback) {
-  std::move(callback).Run(std::vector<PlusProfile>{
-      PlusProfile(kFakeProfileId, FacetURI::FromCanonicalSpec(kFacet),
-                  PlusAddress(kFakePlusAddress), true)});
+  if (should_return_no_affiliated_plus_profiles_) {
+    std::move(callback).Run({});
+  } else {
+    std::move(callback).Run(std::vector<PlusProfile>{
+        PlusProfile(kFakeProfileId, FacetURI::FromCanonicalSpec(kFacet),
+                    PlusAddress(kFakePlusAddress), true)});
+  }
 }
 
 void FakePlusAddressService::ReservePlusAddress(
diff --git a/components/plus_addresses/fake_plus_address_service.h b/components/plus_addresses/fake_plus_address_service.h
index 7b00586..10f8b92 100644
--- a/components/plus_addresses/fake_plus_address_service.h
+++ b/components/plus_addresses/fake_plus_address_service.h
@@ -92,6 +92,12 @@
     should_offer_creation_ = should_offer_creation;
   }
 
+  void set_should_return_no_affiliated_plus_profiles(
+      bool should_return_no_affiliated_plus_profiles) {
+    should_return_no_affiliated_plus_profiles_ =
+        should_return_no_affiliated_plus_profiles;
+  }
+
  private:
   PlusAddressRequestCallback on_confirmed_;
   testing::NiceMock<affiliations::MockAffiliationService>
@@ -103,6 +109,7 @@
   bool should_fail_to_refresh_ = false;
   bool is_plus_address_filling_enabled_ = false;
   bool should_offer_creation_ = false;
+  bool should_return_no_affiliated_plus_profiles_ = false;
 };
 
 }  // namespace plus_addresses
diff --git a/components/plus_addresses/features.cc b/components/plus_addresses/features.cc
index 5688b3475..44d475d 100644
--- a/components/plus_addresses/features.cc
+++ b/components/plus_addresses/features.cc
@@ -22,6 +22,12 @@
 }  // namespace
 
 #if BUILDFLAG(IS_ANDROID)
+// When enabled, mobile plus address creation bottom sheet shows enhanced UI for
+// different error states.
+BASE_FEATURE(kPlusAddressAndroidErrorStatesEnabled,
+             "PlusAddressAndroidErrorStatesEnabled",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // When enabled, mobile manual fallbacks for addresses and passwords show plus
 // address filling information.
 BASE_FEATURE(kPlusAddressAndroidManualFallbackEnabled,
diff --git a/components/plus_addresses/features.h b/components/plus_addresses/features.h
index 746d7e6c..a8478d9 100644
--- a/components/plus_addresses/features.h
+++ b/components/plus_addresses/features.h
@@ -13,6 +13,9 @@
 
 #if BUILDFLAG(IS_ANDROID)
 COMPONENT_EXPORT(PLUS_ADDRESSES_FEATURES)
+BASE_DECLARE_FEATURE(kPlusAddressAndroidErrorStatesEnabled);
+
+COMPONENT_EXPORT(PLUS_ADDRESSES_FEATURES)
 BASE_DECLARE_FEATURE(kPlusAddressAndroidManualFallbackEnabled);
 #endif  // BUILDFLAG(IS_ANDROID)
 
diff --git a/components/plus_addresses_strings.grdp b/components/plus_addresses_strings.grdp
index 5de85cd..6e4cec6 100644
--- a/components/plus_addresses_strings.grdp
+++ b/components/plus_addresses_strings.grdp
@@ -48,6 +48,18 @@
     <message name="IDS_PLUS_ADDRESS_BOTTOMSHEET_NOTICE_ANDROID" desc="The notice text on the creation dialog for plus addresses on Android when the dialog is shown for the first time." translateable="false">
       First time usage notice placeholder (<ph name="GAIA_EMAIL">$1<ex>foo@gmail.com</ex></ph>), which includes the redirection link <ph name="BEGIN_LINK">&lt;link&gt;</ph>link placeholder<ph name="END_LINK">&lt;/link&gt;</ph>.
     </message>
+    <message name="IDS_PLUS_ADDRESS_BOTTOMSHEET_RESERVE_ERROR_TITLE_ANDROID" desc="Title shown in the plus address creation bottom sheet when the backend fails to reserve the plus address after the user click the confirm button" translateable="false">
+      Reserve error title placeholder
+    </message>
+    <message name="IDS_PLUS_ADDRESS_BOTTOMSHEET_RESERVE_ERROR_DESCRIPTION_ANDROID" desc="Description message shown in the plus address creation bottom sheet when the backend fails to reserve the plus address after the user click the confirm button" translateable="false">
+      Reserve error description placeholder.
+    </message>
+    <message name="IDS_PLUS_ADDRESS_BOTTOMSHEET_RESERVE_ERROR_OK_BUTTON_ANDROID" desc="The user clicked confirm on the plus address creation bottom sheet and received and error. The text of the button that restarts the process." translateable="false">
+      Reserve ok button placeholder
+    </message>
+    <message name="IDS_PLUS_ADDRESS_BOTTOMSHEET_RESERVE_ERROR_CANCEL_BUTTON_ANDROID" desc="The user clicked confirm on the plus address creation bottom sheet and received and error. The text of the button that closes the current error message." translateable="false">
+      Reserver cancel button placeholder
+    </message>
 
     <!-- iOS strings -->
     <message name="IDS_PLUS_ADDRESS_BOTTOMSHEET_TITLE_IOS" desc="Title for the experimental enterprise plus addresses feature modal on iOS." translateable="false">
@@ -88,6 +100,9 @@
     <message name="IDS_PLUS_ADDRESS_MANUAL_FALLBACK_MANAGE_ACTION_TEXT_IOS" desc="The action title shown in the manual fallbacks view to manage the plus addresses" translateable="false">
 	Manage Plus Address...
     </message>
+    <message name="IDS_PLUS_ADDRESS_MANUAL_FALLBACK_CREATE_ACTION_TEXT_IOS" desc="The action title shown in the manual fallbacks view to create a plus addresss" translateable="false">
+        Create Plus Address...
+    </message>
 
     <!-- Desktop strings -->
     <message name="IDS_PLUS_ADDRESS_MODAL_TITLE" desc="Title for the experimental enterprise plus addresses feature modal" translateable="false">
diff --git a/components/policy/content/safe_search_service.cc b/components/policy/content/safe_search_service.cc
index d255f83..10d3dbe 100644
--- a/components/policy/content/safe_search_service.cc
+++ b/components/policy/content/safe_search_service.cc
@@ -22,7 +22,7 @@
 void OnCheckURLDone(SafeSearchService::CheckSafeSearchCallback callback,
                     const GURL& /* url */,
                     safe_search_api::Classification classification,
-                    bool /* uncertain */) {
+                    safe_search_api::ClassificationDetails details) {
   std::move(callback).Run(classification ==
                           safe_search_api::Classification::SAFE);
 }
diff --git a/components/policy/core/common/cloud/cloud_policy_core.cc b/components/policy/core/common/cloud/cloud_policy_core.cc
index 57d79d2..8cefc66c 100644
--- a/components/policy/core/common/cloud/cloud_policy_core.cc
+++ b/components/policy/core/common/cloud/cloud_policy_core.cc
@@ -96,7 +96,7 @@
   if (!refresh_scheduler_) {
     refresh_scheduler_ = std::make_unique<CloudPolicyRefreshScheduler>(
         client_.get(), store_, service_.get(), task_runner_,
-        network_connection_tracker_getter_);
+        network_connection_tracker_getter_, skip_first_policy_fetch());
     UpdateRefreshDelayFromPref();
     for (auto& observer : observers_)
       observer.OnRefreshSchedulerStarted(this);
@@ -122,6 +122,12 @@
   observers_.RemoveObserver(observer);
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+void CloudPolicyCore::SetSkipFirstPolicyFetch(bool skip_first_policy_fetch) {
+  skip_first_policy_fetch_ = skip_first_policy_fetch;
+}
+#endif
+
 void CloudPolicyCore::ConnectForTesting(
     std::unique_ptr<CloudPolicyService> service,
     std::unique_ptr<CloudPolicyClient> client) {
diff --git a/components/policy/core/common/cloud/cloud_policy_core.h b/components/policy/core/common/cloud/cloud_policy_core.h
index 33f0e57..a0ea65df 100644
--- a/components/policy/core/common/cloud/cloud_policy_core.h
+++ b/components/policy/core/common/cloud/cloud_policy_core.h
@@ -130,6 +130,14 @@
   // Removes the specified observer.
   void RemoveObserver(Observer* observer);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Set if the refresh_scheduler needs the first policy fetch.
+  void SetSkipFirstPolicyFetch(bool skip_first_policy_fetch);
+  bool skip_first_policy_fetch() { return skip_first_policy_fetch_; }
+#else
+  bool skip_first_policy_fetch() { return false; }
+#endif
+
   // Initializes the cloud connection using injected |service| and |client|.
   void ConnectForTesting(std::unique_ptr<CloudPolicyService> service,
                          std::unique_ptr<CloudPolicyClient> client);
@@ -149,6 +157,9 @@
   std::unique_ptr<RemoteCommandsService> remote_commands_service_;
   std::unique_ptr<IntegerPrefMember> refresh_delay_;
   base::ObserverList<Observer, true>::Unchecked observers_;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  bool skip_first_policy_fetch_ = false;
+#endif
 };
 
 }  // namespace policy
diff --git a/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.cc b/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.cc
index a3e1acb..99a97d29 100644
--- a/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.cc
+++ b/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.cc
@@ -96,7 +96,8 @@
     CloudPolicyStore* store,
     CloudPolicyService* service,
     const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-    network::NetworkConnectionTrackerGetter network_connection_tracker_getter)
+    network::NetworkConnectionTrackerGetter network_connection_tracker_getter,
+    bool skip_first_policy_fetch)
     : client_(client),
       store_(store),
       service_(service),
@@ -111,6 +112,7 @@
   store_->AddObserver(this);
   network_connection_tracker_->AddNetworkConnectionObserver(this);
 
+  skip_first_policy_fetch_ = skip_first_policy_fetch;
   UpdateLastRefreshFromPolicy();
   ScheduleRefresh();
 }
@@ -435,6 +437,20 @@
   if (!delay.is_zero())
     delay += base::Milliseconds(refresh_delay_salt_ms_);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // On browser start the `last_refresh_` is not set yet, and the calculated
+  // delay ends up being zero. If we need to skip the initial policy fetch,
+  // we reset the delay here to the input value received.
+  if (skip_first_policy_fetch_) {
+    skip_first_policy_fetch_ = false;
+    delay = delta;
+
+    // We have to refresh the last_refresh_ values here to enforce the delay.
+    last_refresh_ = GetClock()->Now();
+    last_refresh_ticks_ = GetTickClock()->NowTicks();
+  }
+#endif
+
   refresh_callback_.Reset(
       base::BindOnce(&CloudPolicyRefreshScheduler::PerformRefresh,
                      base::Unretained(this), reason));
diff --git a/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h b/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h
index 543019c6..b3e0a20 100644
--- a/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h
+++ b/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h
@@ -78,8 +78,8 @@
       CloudPolicyStore* store,
       CloudPolicyService* service,
       const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-      network::NetworkConnectionTrackerGetter
-          network_connection_tracker_getter);
+      network::NetworkConnectionTrackerGetter network_connection_tracker_getter,
+      bool skip_first_policy_fetch = false);
   CloudPolicyRefreshScheduler(const CloudPolicyRefreshScheduler&) = delete;
   CloudPolicyRefreshScheduler& operator=(const CloudPolicyRefreshScheduler&) =
       delete;
@@ -215,6 +215,10 @@
   // Whether we have retried with key reset or not.
   bool has_retried_with_key_reset_ = false;
 
+  // Whether the immediate policy fetch should be skipped. Applied to device
+  // local accounts when it's not the first time Chrome starts on device boot.
+  bool skip_first_policy_fetch_ = false;
+
   base::ObserverList<CloudPolicyRefreshSchedulerObserver, true> observers_;
 };
 
diff --git a/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_unittest.cc b/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_unittest.cc
index 2e94da2..835b43e 100644
--- a/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_unittest.cc
@@ -296,6 +296,17 @@
   Mock::VerifyAndClearExpectations(&client_);
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+TEST_F(CloudPolicyRefreshSchedulerTest, SkipFirstFetch) {
+  CloudPolicyRefreshScheduler scheduler(
+      &client_, &store_, service_.get(), task_runner_,
+      network::TestNetworkConnectionTracker::CreateGetter(), true);
+  base::TimeDelta delay = task_runner_->NextPendingTaskDelay();
+  EXPECT_EQ(CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs,
+            delay.InMilliseconds());
+}
+#endif
+
 TEST_F(CloudPolicyRefreshSchedulerTest, RefreshSoonOverriding) {
   auto scheduler = base::WrapUnique(CreateRefreshScheduler());
 
diff --git a/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json b/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json
index faedf4d..3f9180ac 100644
--- a/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json
+++ b/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json
@@ -25,6 +25,9 @@
           },
           "optimization_guide.model_execution.history_search_enterprise_policy_allowed": {
             "value": 0
+          },
+          "optimization_guide.model_execution.tab_compare_settings_enterprise_policy": {
+            "value": 0
           }
         }
       },
@@ -47,6 +50,9 @@
           },
           "optimization_guide.model_execution.history_search_enterprise_policy_allowed": {
             "value": 1
+          },
+          "optimization_guide.model_execution.tab_compare_settings_enterprise_policy": {
+            "value": 1
           }
         }
       },
@@ -69,6 +75,9 @@
           },
           "optimization_guide.model_execution.history_search_enterprise_policy_allowed": {
             "value": 2
+          },
+          "optimization_guide.model_execution.tab_compare_settings_enterprise_policy": {
+            "value": 2
           }
         }
       },
@@ -89,6 +98,9 @@
           },
           "optimization_guide.model_execution.history_search_enterprise_policy_allowed": {
             "default_value": 0
+          },
+          "optimization_guide.model_execution.tab_compare_settings_enterprise_policy": {
+            "default_value": 0
           }
         }
       },
@@ -99,7 +111,8 @@
           "TabOrganizerSettings": 2,
           "CreateThemesSettings": 2,
           "DevToolsGenAiSettings": 2,
-          "HistorySearchSettings": 2
+          "HistorySearchSettings": 2,
+          "TabCompareSettings": 2
         },
         "prefs": {
           "optimization_guide.model_execution.compose_enterprise_policy_allowed": {
@@ -116,6 +129,9 @@
           },
           "optimization_guide.model_execution.history_search_enterprise_policy_allowed": {
             "value": 2
+          },
+          "optimization_guide.model_execution.tab_compare_settings_enterprise_policy": {
+            "value": 2
           }
         }
       }
@@ -147,6 +163,9 @@
           },
           "optimization_guide.model_execution.history_search_enterprise_policy_allowed": {
             "value": 1
+          },
+          "optimization_guide.model_execution.tab_compare_settings_enterprise_policy": {
+            "value": 1
           }
         }
       },
@@ -167,6 +186,9 @@
           },
           "optimization_guide.model_execution.history_search_enterprise_policy_allowed": {
             "value": 1
+          },
+          "optimization_guide.model_execution.tab_compare_settings_enterprise_policy": {
+            "value": 1
           }
         }
       }
diff --git a/components/power_monitor/power_monitor_device_source_linux.cc b/components/power_monitor/power_monitor_device_source_linux.cc
index 14facdc5..25f04545 100644
--- a/components/power_monitor/power_monitor_device_source_linux.cc
+++ b/components/power_monitor/power_monitor_device_source_linux.cc
@@ -47,7 +47,7 @@
     ShutdownBus();
 }
 
-bool PowerMonitorDeviceSourceLinux::IsOnBatteryPower() {
+bool PowerMonitorDeviceSourceLinux::IsOnBatteryPower() const {
   // TODO(crbug.com/40836663): Use org.freedesktop.UPower to check for
   // OnBattery. One possibility is to connect to the DeviceService's
   // BatteryMonitor.
diff --git a/components/power_monitor/power_monitor_device_source_linux.h b/components/power_monitor/power_monitor_device_source_linux.h
index 8da7e72..d21d5cdd 100644
--- a/components/power_monitor/power_monitor_device_source_linux.h
+++ b/components/power_monitor/power_monitor_device_source_linux.h
@@ -27,7 +27,7 @@
   ~PowerMonitorDeviceSourceLinux() override;
 
   // base::PowerMonitorSource:
-  bool IsOnBatteryPower() override;
+  bool IsOnBatteryPower() const override;
 
  private:
   void ShutdownBus();
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc
index 04e7d968..629224d 100644
--- a/components/safe_browsing/core/common/features.cc
+++ b/components/safe_browsing/core/common/features.cc
@@ -254,7 +254,7 @@
 
 BASE_FEATURE(kSafetyHubAbusiveNotificationRevocation,
              "SafetyHubAbusiveNotificationRevocation",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kSimplifiedUrlDisplay,
              "SimplifiedUrlDisplay",
diff --git a/components/safe_search_api/url_checker.cc b/components/safe_search_api/url_checker.cc
index 47c6918..2de0550c 100644
--- a/components/safe_search_api/url_checker.cc
+++ b/components/safe_search_api/url_checker.cc
@@ -40,8 +40,7 @@
 URLChecker::Check::~Check() = default;
 
 URLChecker::CheckResult::CheckResult(Classification classification)
-    : classification(classification),
-      timestamp(base::TimeTicks::Now()) {}
+    : classification(classification), timestamp(base::TimeTicks::Now()) {}
 
 URLChecker::URLChecker(std::unique_ptr<URLCheckerClient> async_checker)
     : URLChecker(std::move(async_checker), kDefaultCacheSize) {}
@@ -82,7 +81,10 @@
       DVLOG(1) << "Cache hit! " << url.spec() << " is "
                << (result.classification == Classification::UNSAFE ? "NOT" : "")
                << " safe";
-      std::move(callback).Run(url, result.classification, /*uncertain=*/false);
+      std::move(callback).Run(
+          url, result.classification,
+          ClassificationDetails{
+              .reason = ClassificationDetails::Reason::kCachedResponse});
 
       base::UmaHistogramEnumeration(kCacheHitMetricKey,
                                     CacheAccessStatus::kHit);
@@ -122,7 +124,13 @@
   }
 
   for (CheckCallback& callback : callbacks) {
-    std::move(callback).Run(url, classification, uncertain);
+    std::move(callback).Run(
+        url, classification,
+        ClassificationDetails{
+            .reason =
+                uncertain
+                    ? ClassificationDetails::Reason::kFailedUseDefault
+                    : ClassificationDetails::Reason::kFreshServerResponse});
   }
 }
 
diff --git a/components/safe_search_api/url_checker.h b/components/safe_search_api/url_checker.h
index 4eefe6c..1166638 100644
--- a/components/safe_search_api/url_checker.h
+++ b/components/safe_search_api/url_checker.h
@@ -20,6 +20,24 @@
 // The SafeSearch API classification of a URL.
 enum class Classification { SAFE, UNSAFE };
 
+// Additional details regarding how the `Classification` result was determined.
+struct ClassificationDetails {
+  enum class Reason {
+    // Chrome sent a fresh external request to the server, and received a valid
+    // response.
+    kFreshServerResponse,
+
+    // Chrome used a cached response, stored from a previous successful request
+    // to the server.
+    kCachedResponse,
+
+    // Chrome tried but failed to query the server, and defaulted to
+    // `Classification::SAFE`.
+    kFailedUseDefault,
+  };
+  Reason reason;
+};
+
 // These values are sent to Uma to understand Cache utilization. Must not be
 // renumbered.
 enum class CacheAccessStatus {
@@ -40,8 +58,9 @@
 class URLChecker {
  public:
   // Used to report whether |url| should be blocked. Called from CheckURL.
-  using CheckCallback = base::OnceCallback<
-      void(const GURL&, Classification classification, bool /* uncertain */)>;
+  using CheckCallback = base::OnceCallback<void(const GURL&,
+                                                Classification classification,
+                                                ClassificationDetails details)>;
 
   explicit URLChecker(std::unique_ptr<URLCheckerClient> async_checker);
 
diff --git a/components/safe_search_api/url_checker_unittest.cc b/components/safe_search_api/url_checker_unittest.cc
index 6bc4a90..7e75163 100644
--- a/components/safe_search_api/url_checker_unittest.cc
+++ b/components/safe_search_api/url_checker_unittest.cc
@@ -67,6 +67,12 @@
   return base::BucketsInclude(buckets_array);
 }
 
+// A matcher which checks that the provided |ClassificationDetails| has the
+// expected |reason| value.
+MATCHER_P(ReasonEq, reason, "") {
+  return arg.reason == reason;
+}
+
 }  // namespace
 
 class SafeSearchURLCheckerTest : public testing::Test {
@@ -81,7 +87,7 @@
   MOCK_METHOD3(OnCheckDone,
                void(const GURL& url,
                     Classification classification,
-                    bool uncertain));
+                    ClassificationDetails details));
 
  protected:
   GURL GetNewURL() {
@@ -121,21 +127,29 @@
 TEST_F(SafeSearchURLCheckerTest, Simple) {
   {
     GURL url(GetNewURL());
-    EXPECT_CALL(*this,
-                OnCheckDone(url, Classification::SAFE, /*uncertain=*/false));
+    EXPECT_CALL(
+        *this,
+        OnCheckDone(
+            url, Classification::SAFE,
+            ReasonEq(ClassificationDetails::Reason::kFreshServerResponse)));
     ASSERT_FALSE(SendResponse(url, Classification::SAFE, /*uncertain=*/false));
   }
   {
     GURL url(GetNewURL());
-    EXPECT_CALL(*this,
-                OnCheckDone(url, Classification::UNSAFE, /*uncertain=*/false));
+    EXPECT_CALL(
+        *this,
+        OnCheckDone(
+            url, Classification::UNSAFE,
+            ReasonEq(ClassificationDetails::Reason::kFreshServerResponse)));
     ASSERT_FALSE(
         SendResponse(url, Classification::UNSAFE, /*uncertain=*/false));
   }
   {
     GURL url(GetNewURL());
-    EXPECT_CALL(*this,
-                OnCheckDone(url, Classification::SAFE, /*uncertain=*/true));
+    EXPECT_CALL(
+        *this, OnCheckDone(
+                   url, Classification::SAFE,
+                   ReasonEq(ClassificationDetails::Reason::kFailedUseDefault)));
     ASSERT_FALSE(SendResponse(url, Classification::SAFE, /*uncertain=*/true));
   }
 
@@ -151,28 +165,44 @@
   GURL url3(GetNewURL());
 
   // Populate the cache.
-  EXPECT_CALL(*this,
-              OnCheckDone(url1, Classification::SAFE, /*uncertain=*/false));
+  EXPECT_CALL(
+      *this,
+      OnCheckDone(
+          url1, Classification::SAFE,
+          ReasonEq(ClassificationDetails::Reason::kFreshServerResponse)));
   ASSERT_FALSE(SendResponse(url1, Classification::SAFE, /*uncertain=*/false));
-  EXPECT_CALL(*this,
-              OnCheckDone(url2, Classification::SAFE, /*uncertain=*/false));
+  EXPECT_CALL(
+      *this,
+      OnCheckDone(
+          url2, Classification::SAFE,
+          ReasonEq(ClassificationDetails::Reason::kFreshServerResponse)));
   ASSERT_FALSE(SendResponse(url2, Classification::SAFE, /*uncertain=*/false));
 
   // Now we should get results synchronously, without a request to the api.
-  EXPECT_CALL(*this,
-              OnCheckDone(url2, Classification::SAFE, /*uncertain=*/false));
+  EXPECT_CALL(
+      *this,
+      OnCheckDone(url2, Classification::SAFE,
+                  ReasonEq(ClassificationDetails::Reason::kCachedResponse)));
   ASSERT_TRUE(CheckURL(url2));
-  EXPECT_CALL(*this,
-              OnCheckDone(url1, Classification::SAFE, /*uncertain=*/false));
+  EXPECT_CALL(
+      *this,
+      OnCheckDone(url1, Classification::SAFE,
+                  ReasonEq(ClassificationDetails::Reason::kCachedResponse)));
   ASSERT_TRUE(CheckURL(url1));
 
   // Now |url2| is the LRU and should be evicted on the next check.
-  EXPECT_CALL(*this,
-              OnCheckDone(url3, Classification::SAFE, /*uncertain=*/false));
+  EXPECT_CALL(
+      *this,
+      OnCheckDone(
+          url3, Classification::SAFE,
+          ReasonEq(ClassificationDetails::Reason::kFreshServerResponse)));
   ASSERT_FALSE(SendResponse(url3, Classification::SAFE, /*uncertain=*/false));
 
-  EXPECT_CALL(*this,
-              OnCheckDone(url2, Classification::SAFE, /*uncertain=*/false));
+  EXPECT_CALL(
+      *this,
+      OnCheckDone(
+          url2, Classification::SAFE,
+          ReasonEq(ClassificationDetails::Reason::kFreshServerResponse)));
   ASSERT_FALSE(SendResponse(url2, Classification::SAFE, /*uncertain=*/false));
 
   EXPECT_THAT(CacheHitMetric(), Recorded({{CacheAccessStatus::kHit, 2},
@@ -185,7 +215,11 @@
   ASSERT_FALSE(CheckURL(url));
   ASSERT_FALSE(CheckURL(url));
   // A single response should answer both of those checks
-  EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, false)).Times(2);
+  EXPECT_CALL(
+      *this, OnCheckDone(
+                 url, Classification::SAFE,
+                 ReasonEq(ClassificationDetails::Reason::kFreshServerResponse)))
+      .Times(2);
   fake_client_->RunCallback(ToAPIClassification(Classification::SAFE, false));
 
   EXPECT_THAT(CacheHitMetric(), Recorded({{CacheAccessStatus::kHit, 0},
@@ -197,14 +231,20 @@
 
   checker_->SetCacheTimeoutForTesting(base::Seconds(0));
 
-  EXPECT_CALL(*this,
-              OnCheckDone(url, Classification::SAFE, /*uncertain=*/false));
+  EXPECT_CALL(
+      *this,
+      OnCheckDone(
+          url, Classification::SAFE,
+          ReasonEq(ClassificationDetails::Reason::kFreshServerResponse)));
   ASSERT_FALSE(SendResponse(url, Classification::SAFE, /*uncertain=*/false));
 
   // Since the cache timeout is zero, the cache entry should be invalidated
   // immediately.
-  EXPECT_CALL(*this,
-              OnCheckDone(url, Classification::UNSAFE, /*uncertain=*/false));
+  EXPECT_CALL(
+      *this,
+      OnCheckDone(
+          url, Classification::UNSAFE,
+          ReasonEq(ClassificationDetails::Reason::kFreshServerResponse)));
   ASSERT_FALSE(SendResponse(url, Classification::UNSAFE, /*uncertain=*/false));
 
   EXPECT_THAT(CacheHitMetric(), Recorded({{CacheAccessStatus::kHit, 0},
diff --git a/components/sensitive_content/sensitive_content_manager_unittest.cc b/components/sensitive_content/sensitive_content_manager_unittest.cc
index 4fc0d08..7147301 100644
--- a/components/sensitive_content/sensitive_content_manager_unittest.cc
+++ b/components/sensitive_content/sensitive_content_manager_unittest.cc
@@ -50,6 +50,14 @@
         web_contents(), &sensitive_content_client_);
   }
 
+  void TearDown() override {
+    // The destruction of the frame at the end of a test triggers a state
+    // change, which can result in content sensitivity being changed and in
+    // unexpected calls to the client.
+    testing::Mock::VerifyAndClearExpectations(&sensitive_content_client_);
+    content::RenderViewHostTestHarness::TearDown();
+  }
+
   autofill::ContentAutofillDriver* autofill_driver() {
     return autofill_driver_injector_[web_contents()];
   }
diff --git a/components/supervised_user/core/browser/supervised_user_error_page_unittest.cc b/components/supervised_user/core/browser/supervised_user_error_page_unittest.cc
index e16159b..5fa0ddf6 100644
--- a/components/supervised_user/core/browser/supervised_user_error_page_unittest.cc
+++ b/components/supervised_user/core/browser/supervised_user_error_page_unittest.cc
@@ -32,7 +32,7 @@
   BlockMessageIDTestParameter param = GetParam();
   EXPECT_EQ(param.expected_result,
             GetBlockMessageID(param.reason, param.single_parent))
-      << "reason = " << FilteringBehaviorReasonToString(param.reason)
+      << "reason = " << int(param.reason)
       << " single parent = " << param.single_parent;
 }
 
diff --git a/components/supervised_user/core/browser/supervised_user_url_filter.cc b/components/supervised_user/core/browser/supervised_user_url_filter.cc
index 88a3944..7243c7ee 100644
--- a/components/supervised_user/core/browser/supervised_user_url_filter.cc
+++ b/components/supervised_user/core/browser/supervised_user_url_filter.cc
@@ -20,6 +20,7 @@
 #include "base/notreached.h"
 #include "base/strings/string_util.h"
 #include "base/task/thread_pool.h"
+#include "components/safe_search_api/url_checker.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/supervised_user/core/browser/kids_chrome_management_url_checker_client.h"
 #include "components/supervised_user/core/browser/supervised_user_capabilities.h"
@@ -567,10 +568,12 @@
       reason != supervised_user::FilteringBehaviorReason::DEFAULT) {
     std::move(callback).Run(behavior, reason, false);
     for (Observer& observer : observers_) {
-      observer.OnURLChecked(url, behavior, reason, false);
+      observer.OnURLChecked(
+          url, behavior,
+          FilteringBehaviorDetails{.reason = reason});
     }
     return true;
-  }
+    }
 
   if (!skip_manual_parent_filter) {
     // Any non-default reason trumps the async checker.
@@ -579,7 +582,9 @@
         behavior == FilteringBehavior::kBlock) {
       std::move(callback).Run(behavior, reason, false);
       for (Observer& observer : observers_) {
-        observer.OnURLChecked(url, behavior, reason, false);
+        observer.OnURLChecked(
+            url, behavior,
+            FilteringBehaviorDetails{.reason = reason});
       }
       return true;
     }
@@ -601,7 +606,9 @@
   if (reason != supervised_user::FilteringBehaviorReason::DEFAULT) {
     std::move(callback).Run(behavior, reason, false);
     for (Observer& observer : observers_) {
-      observer.OnURLChecked(url, behavior, reason, false);
+      observer.OnURLChecked(
+          url, behavior,
+          FilteringBehaviorDetails{.reason = reason});
     }
     return true;
   }
@@ -613,7 +620,9 @@
     // It is not in the same domain and is blocked.
     std::move(callback).Run(behavior, reason, false);
     for (Observer& observer : observers_) {
-      observer.OnURLChecked(url, behavior, reason, false);
+      observer.OnURLChecked(
+          url, behavior,
+          FilteringBehaviorDetails{.reason = reason});
     }
     return true;
   }
@@ -782,16 +791,19 @@
     FilteringBehaviorCallback callback,
     const GURL& url,
     safe_search_api::Classification classification,
-    bool uncertain) const {
+    safe_search_api::ClassificationDetails details) const {
   FilteringBehavior behavior =
       GetBehaviorFromSafeSearchClassification(classification);
   std::move(callback).Run(
       behavior, supervised_user::FilteringBehaviorReason::ASYNC_CHECKER,
-      uncertain);
+      details.reason ==
+          safe_search_api::ClassificationDetails::Reason::kFailedUseDefault);
   for (Observer& observer : observers_) {
     observer.OnURLChecked(
-        url, behavior, supervised_user::FilteringBehaviorReason::ASYNC_CHECKER,
-        uncertain);
+        url, behavior,
+        supervised_user::FilteringBehaviorDetails{
+            .reason = supervised_user::FilteringBehaviorReason::ASYNC_CHECKER,
+            .classification_details = details});
   }
 }
 
diff --git a/components/supervised_user/core/browser/supervised_user_url_filter.h b/components/supervised_user/core/browser/supervised_user_url_filter.h
index a9735db..4794f569 100644
--- a/components/supervised_user/core/browser/supervised_user_url_filter.h
+++ b/components/supervised_user/core/browser/supervised_user_url_filter.h
@@ -103,10 +103,10 @@
    public:
     // Called whenever a check started via
     // GetFilteringBehaviorForURLWithAsyncChecks completes.
-    virtual void OnURLChecked(const GURL& url,
-                              FilteringBehavior behavior,
-                              supervised_user::FilteringBehaviorReason reason,
-                              bool uncertain) {}
+    virtual void OnURLChecked(
+        const GURL& url,
+        FilteringBehavior behavior,
+        supervised_user::FilteringBehaviorDetails details) {}
   };
 
   SupervisedUserURLFilter(PrefService& user_prefs,
@@ -252,7 +252,7 @@
   void CheckCallback(FilteringBehaviorCallback callback,
                      const GURL& url,
                      safe_search_api::Classification classification,
-                     bool uncertain) const;
+                     safe_search_api::ClassificationDetails details) const;
 
   base::ObserverList<Observer>::Unchecked observers_;
 
diff --git a/components/supervised_user/core/browser/supervised_user_url_filter_unittest.cc b/components/supervised_user/core/browser/supervised_user_url_filter_unittest.cc
index 15f7891..fef7cd3 100644
--- a/components/supervised_user/core/browser/supervised_user_url_filter_unittest.cc
+++ b/components/supervised_user/core/browser/supervised_user_url_filter_unittest.cc
@@ -41,10 +41,9 @@
   // SupervisedUserURLFilter::Observer:
   void OnURLChecked(const GURL& url,
                     supervised_user::FilteringBehavior behavior,
-                    supervised_user::FilteringBehaviorReason reason,
-                    bool uncertain) override {
+                    supervised_user::FilteringBehaviorDetails details) override {
     behavior_ = behavior;
-    reason_ = reason;
+    reason_ = details.reason;
   }
 
  protected:
diff --git a/components/supervised_user/core/browser/supervised_user_utils.cc b/components/supervised_user/core/browser/supervised_user_utils.cc
index b50a1ed..ba59982 100644
--- a/components/supervised_user/core/browser/supervised_user_utils.cc
+++ b/components/supervised_user/core/browser/supervised_user_utils.cc
@@ -107,18 +107,6 @@
   }
 }
 
-std::string FilteringBehaviorReasonToString(FilteringBehaviorReason reason) {
-  switch (reason) {
-    case FilteringBehaviorReason::DEFAULT:
-      return "Default";
-    case FilteringBehaviorReason::ASYNC_CHECKER:
-      return "AsyncChecker";
-    case FilteringBehaviorReason::MANUAL:
-      return "Manual";
-  }
-  return "Unknown";
-}
-
 GURL NormalizeUrl(const GURL& url) {
   GURL effective_url = url_matcher::util::GetEmbeddedURL(url);
   if (!effective_url.is_valid()) {
diff --git a/components/supervised_user/core/browser/supervised_user_utils.h b/components/supervised_user/core/browser/supervised_user_utils.h
index e4e6837..3d83fe8e 100644
--- a/components/supervised_user/core/browser/supervised_user_utils.h
+++ b/components/supervised_user/core/browser/supervised_user_utils.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/memory/raw_ref.h"
+#include "components/safe_search_api/url_checker.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/supervised_user/core/browser/family_link_user_log_record.h"
 #include "components/supervised_user/core/browser/proto/families_common.pb.h"
@@ -25,6 +26,14 @@
   MANUAL = 2,
 };
 
+// Details degarding how a particular filtering classification was arrived at.
+struct FilteringBehaviorDetails {
+  FilteringBehaviorReason reason;
+
+  // The following field only applies if `reason` is `ASYNC_CHECKER`.
+  safe_search_api::ClassificationDetails classification_details;
+};
+
 // A Java counterpart will be generated for this enum.
 // Values are stored in prefs under kDefaultSupervisedUserFilteringBehavior.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.superviseduser
@@ -61,9 +70,6 @@
 // Converts FamilyRole enum to string format.
 std::string FamilyRoleToString(kidsmanagement::FamilyRole role);
 
-// Converts FilteringBehaviorReason enum to string format.
-std::string FilteringBehaviorReasonToString(FilteringBehaviorReason reason);
-
 // Strips user-specific tokens in a URL to generalize it.
 GURL NormalizeUrl(const GURL& url);
 
diff --git a/components/translate/core/browser/BUILD.gn b/components/translate/core/browser/BUILD.gn
index fb33679..b17e7bf91 100644
--- a/components/translate/core/browser/BUILD.gn
+++ b/components/translate/core/browser/BUILD.gn
@@ -93,7 +93,6 @@
     "translate_model_service.h",
   ]
   deps = [
-    ":browser",
     "//base",
     "//components/keyed_service/core",
     "//components/optimization_guide/core",
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index fc3c45b..558961b 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -38,6 +38,10 @@
              "AndroidBcivWithSuppression",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kAndroidBcivZeroBrowserFrames,
+             "AndroidBcivZeroBrowserFrames",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 #endif  // BUILDFLAG(IS_ANDROID)
 
 BASE_FEATURE(kBackdropFilterMirrorEdgeMode,
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index 5eaee51..2d57b22 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -20,8 +20,9 @@
 namespace features {
 
 #if BUILDFLAG(IS_ANDROID)
-VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAndroidBrowserControlsInViz);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAndroidBcivWithSuppression);
+VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAndroidBcivZeroBrowserFrames);
+VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAndroidBrowserControlsInViz);
 #endif  // BUILDFLAG(IS_ANDROID)
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kBackdropFilterMirrorEdgeMode);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kDelegatedCompositing);
diff --git a/components/viz/common/gpu/context_cache_controller.cc b/components/viz/common/gpu/context_cache_controller.cc
index 4be029e..09e0687e 100644
--- a/components/viz/common/gpu/context_cache_controller.cc
+++ b/components/viz/common/gpu/context_cache_controller.cc
@@ -13,7 +13,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "gpu/command_buffer/client/context_support.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 namespace viz {
 namespace {
diff --git a/components/viz/common/gpu/context_cache_controller_unittest.cc b/components/viz/common/gpu/context_cache_controller_unittest.cc
index 8141922..6819425 100644
--- a/components/viz/common/gpu/context_cache_controller_unittest.cc
+++ b/components/viz/common/gpu/context_cache_controller_unittest.cc
@@ -14,7 +14,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkPixmap.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 
 using ::testing::Mock;
diff --git a/components/viz/common/gpu/vulkan_in_process_context_provider.cc b/components/viz/common/gpu/vulkan_in_process_context_provider.cc
index b637bf7..d3833f4 100644
--- a/components/viz/common/gpu/vulkan_in_process_context_provider.cc
+++ b/components/viz/common/gpu/vulkan_in_process_context_provider.cc
@@ -15,7 +15,7 @@
 #include "gpu/vulkan/vulkan_implementation.h"
 #include "gpu/vulkan/vulkan_instance.h"
 #include "gpu/vulkan/vulkan_util.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h"
 #include "third_party/skia/include/gpu/vk/VulkanBackendContext.h"
 #include "third_party/skia/include/gpu/vk/VulkanExtensions.h"
diff --git a/components/viz/common/gpu/vulkan_in_process_context_provider.h b/components/viz/common/gpu/vulkan_in_process_context_provider.h
index 8b259be..c4035b1 100644
--- a/components/viz/common/gpu/vulkan_in_process_context_provider.h
+++ b/components/viz/common/gpu/vulkan_in_process_context_provider.h
@@ -14,7 +14,7 @@
 #include "components/viz/common/gpu/vulkan_context_provider.h"
 #include "components/viz/common/viz_vulkan_context_provider_export.h"
 #include "gpu/vulkan/buildflags.h"
-#include "third_party/skia/include/gpu/GrContextOptions.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextOptions.h"
 
 namespace gpu {
 class VulkanImplementation;
diff --git a/components/viz/service/display/dc_layer_overlay.cc b/components/viz/service/display/dc_layer_overlay.cc
index 9e27d35..df10901 100644
--- a/components/viz/service/display/dc_layer_overlay.cc
+++ b/components/viz/service/display/dc_layer_overlay.cc
@@ -813,8 +813,8 @@
     : has_overlay_support_(skip_initialization_for_testing),
       allowed_yuv_overlay_count_(allowed_yuv_overlay_count),
       is_on_battery_power_(
-          base::PowerMonitor::AddPowerStateObserverAndReturnOnBatteryState(
-              this)),
+          base::PowerMonitor::GetInstance()
+              ->AddPowerStateObserverAndReturnOnBatteryState(this)),
       no_undamaged_overlay_promotion_(base::FeatureList::IsEnabled(
           features::kNoUndamagedOverlayPromotion)) {
   if (!skip_initialization_for_testing) {
@@ -829,7 +829,7 @@
 
 DCLayerOverlayProcessor::~DCLayerOverlayProcessor() {
   gl::DirectCompositionOverlayCapsMonitor::GetInstance()->RemoveObserver(this);
-  base::PowerMonitor::RemovePowerStateObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerStateObserver(this);
 }
 
 void DCLayerOverlayProcessor::UpdateHasHwOverlaySupport() {
diff --git a/components/viz/service/display/external_use_client.h b/components/viz/service/display/external_use_client.h
index 0d9728e..4610db17 100644
--- a/components/viz/service/display/external_use_client.h
+++ b/components/viz/service/display/external_use_client.h
@@ -19,8 +19,8 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/graphite/TextureInfo.h"
 #include "ui/gfx/geometry/size.h"
 
diff --git a/components/viz/service/display/readback_pixeltest.cc b/components/viz/service/display/readback_pixeltest.cc
index 57b5278..d4b64140 100644
--- a/components/viz/service/display/readback_pixeltest.cc
+++ b/components/viz/service/display/readback_pixeltest.cc
@@ -52,7 +52,7 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/color_transform.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 5e421a0..9f1b2d2 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -83,8 +83,8 @@
 #include "third_party/skia/include/effects/SkOverdrawColorFilter.h"
 #include "third_party/skia/include/effects/SkRuntimeEffect.h"
 #include "third_party/skia/include/effects/SkShaderMaskFilter.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/private/chromium/GrDeferredDisplayList.h"
 #include "third_party/skia/modules/skcms/skcms.h"
 #include "third_party/skia/src/core/SkCanvasPriv.h"
diff --git a/components/viz/service/display_embedder/image_context_impl.cc b/components/viz/service/display_embedder/image_context_impl.cc
index 750745b..9346fc70 100644
--- a/components/viz/service/display_embedder/image_context_impl.cc
+++ b/components/viz/service/display_embedder/image_context_impl.cc
@@ -20,7 +20,7 @@
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrContextThreadSafeProxy.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextThreadSafeProxy.h"
 #include "third_party/skia/include/gpu/graphite/Recorder.h"
 #include "third_party/skia/include/gpu/graphite/Surface.h"
 #include "third_party/skia/include/gpu/graphite/dawn/DawnTypes.h"
diff --git a/components/viz/service/display_embedder/image_context_impl.h b/components/viz/service/display_embedder/image_context_impl.h
index fa6e15a..898e6ae8 100644
--- a/components/viz/service/display_embedder/image_context_impl.h
+++ b/components/viz/service/display_embedder/image_context_impl.h
@@ -20,8 +20,8 @@
 #include "gpu/ipc/common/vulkan_ycbcr_info.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/graphite/BackendTexture.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gfx/geometry/size.h"
diff --git a/components/viz/service/display_embedder/skia_output_device.cc b/components/viz/service/display_embedder/skia_output_device.cc
index aef2e5ba..f6949ebe 100644
--- a/components/viz/service/display_embedder/skia_output_device.cc
+++ b/components/viz/service/display_embedder/skia_output_device.cc
@@ -13,8 +13,8 @@
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "services/tracing/public/cpp/perfetto/flow_event_utils.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/graphite/Context.h"
 #include "third_party/skia/include/gpu/graphite/Recording.h"
 #include "third_party/skia/include/private/chromium/GrDeferredDisplayList.h"
diff --git a/components/viz/service/display_embedder/skia_output_device.h b/components/viz/service/display_embedder/skia_output_device.h
index fc39dc74..b754e08b 100644
--- a/components/viz/service/display_embedder/skia_output_device.h
+++ b/components/viz/service/display_embedder/skia_output_device.h
@@ -21,7 +21,7 @@
 #include "components/viz/service/viz_service_export.h"
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "ui/gfx/swap_result.h"
 
 class GrDirectContext;
diff --git a/components/viz/service/display_embedder/skia_output_device_dawn.h b/components/viz/service/display_embedder/skia_output_device_dawn.h
index 5ac84f5..85d54259 100644
--- a/components/viz/service/display_embedder/skia_output_device_dawn.h
+++ b/components/viz/service/display_embedder/skia_output_device_dawn.h
@@ -15,7 +15,7 @@
 #include "third_party/dawn/include/dawn/webgpu.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "ui/gfx/native_widget_types.h"
 
 #if BUILDFLAG(IS_WIN)
diff --git a/components/viz/service/display_embedder/skia_output_device_dcomp.cc b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
index 95266da7..34fe6199 100644
--- a/components/viz/service/display_embedder/skia_output_device_dcomp.cc
+++ b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
@@ -23,7 +23,7 @@
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gl/dc_layer_overlay_image.h"
 #include "ui/gl/dc_layer_overlay_params.h"
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.cc b/components/viz/service/display_embedder/skia_output_device_gl.cc
index 3c8e6eb2..4cfc1e23 100644
--- a/components/viz/service/display_embedder/skia_output_device_gl.cc
+++ b/components/viz/service/display_embedder/skia_output_device_gl.cc
@@ -20,11 +20,11 @@
 #include "third_party/skia/include/core/SkColorType.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkSurfaceProps.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_features.h"
diff --git a/components/viz/service/display_embedder/skia_output_device_vulkan.cc b/components/viz/service/display_embedder/skia_output_device_vulkan.cc
index 765f8bc..034af41c 100644
--- a/components/viz/service/display_embedder/skia_output_device_vulkan.cc
+++ b/components/viz/service/display_embedder/skia_output_device_vulkan.cc
@@ -22,16 +22,16 @@
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkSurfaceProps.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrRecordingContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
 #include "third_party/skia/include/gpu/MutableTextureState.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrRecordingContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
-#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
 #include "third_party/skia/include/gpu/vk/VulkanMutableTextureState.h"
 #include "ui/gfx/presentation_feedback.h"
 
diff --git a/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.cc b/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.cc
index 5981e5d..78ffeac6 100644
--- a/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.cc
+++ b/components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.cc
@@ -7,9 +7,9 @@
 #include <utility>
 
 #include "components/viz/common/gpu/vulkan_context_provider.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
 #include "third_party/skia/include/private/chromium/GrDeferredDisplayList.h"
diff --git a/components/viz/service/display_embedder/skia_output_device_webview.cc b/components/viz/service/display_embedder/skia_output_device_webview.cc
index 352e1de7..610ef5e 100644
--- a/components/viz/service/display_embedder/skia_output_device_webview.cc
+++ b/components/viz/service/display_embedder/skia_output_device_webview.cc
@@ -13,7 +13,7 @@
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_surface.h"
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 67446f3..f12bafa 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -66,10 +66,10 @@
 #include "skia/ext/skia_trace_memory_dump_impl.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrYUVABackendTextures.h"
+#include "third_party/skia/include/gpu/ganesh/GrYUVABackendTextures.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "third_party/skia/include/gpu/graphite/Image.h"
 #include "third_party/skia/include/gpu/graphite/Recorder.h"
 #include "third_party/skia/include/gpu/graphite/YUVABackendTextures.h"
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index fed4808..4a96e31 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -83,7 +83,7 @@
 #include "third_party/skia/include/core/SkSwizzle.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
 #include "third_party/skia/include/gpu/graphite/Context.h"
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index b82de404..879d87f 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -40,8 +40,8 @@
 #include "media/gpu/buildflags.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/private/chromium/GrDeferredDisplayList.h"
 #include "ui/gfx/gpu_fence_handle.h"
 
diff --git a/components/viz/service/display_embedder/skia_render_copy_results.h b/components/viz/service/display_embedder/skia_render_copy_results.h
index 7217ecfa..ac64bcd9 100644
--- a/components/viz/service/display_embedder/skia_render_copy_results.h
+++ b/components/viz/service/display_embedder/skia_render_copy_results.h
@@ -16,7 +16,7 @@
 #include "third_party/libyuv/include/libyuv/planar_functions.h"
 #include "third_party/skia/include/core/SkPixelRef.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/color_space.h"
 
 namespace viz {
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 1e1ad8a9..e540912 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -68,9 +68,9 @@
 #include "media/mojo/services/mojo_video_encode_accelerator_provider.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "skia/buildflags.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLAssembleInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 #include "ui/base/ozone_buildflags.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/gl/gl_context.h"
diff --git a/components/viz/service/main/viz_main_impl.cc b/components/viz/service/main/viz_main_impl.cc
index 4c23fcfa..6671f01 100644
--- a/components/viz/service/main/viz_main_impl.cc
+++ b/components/viz/service/main/viz_main_impl.cc
@@ -88,7 +88,7 @@
   // split into separate processes. Until then this is necessary to be able to
   // run Mushrome (chrome with mus) with Mus running in the browser process.
   if (dependencies_.power_monitor_source) {
-    base::PowerMonitor::Initialize(
+    base::PowerMonitor::GetInstance()->Initialize(
         std::move(dependencies_.power_monitor_source));
   }
 
diff --git a/components/viz/service/main/viz_main_impl_unittest.cc b/components/viz/service/main/viz_main_impl_unittest.cc
index 5ef2c8c..f4446cc3 100644
--- a/components/viz/service/main/viz_main_impl_unittest.cc
+++ b/components/viz/service/main/viz_main_impl_unittest.cc
@@ -74,7 +74,7 @@
 
   ~MockPowerMonitorSource() override { *leak_guard_ = false; }
 
-  bool IsOnBatteryPower() override { return false; }
+  bool IsOnBatteryPower() const override { return false; }
 
  private:
   // An external flag to signal as to whether or not this object is still
@@ -129,8 +129,9 @@
   builder.Record(recorder);
 
   // Need to shutdown the |PowerMonitor| infrastructure.
-  EXPECT_TRUE(base::PowerMonitor::IsInitialized());
-  base::PowerMonitor::ShutdownForTesting();
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  EXPECT_TRUE(power_monitor->IsInitialized());
+  power_monitor->ShutdownForTesting();
   // Double-check that we're not leaking the MockPowerMonitorSource
   // instance.
   ASSERT_FALSE(mock_source_is_alive);
diff --git a/components/viz/test/fake_skia_output_surface.cc b/components/viz/test/fake_skia_output_surface.cc
index 584f9de..3a7e05f2 100644
--- a/components/viz/test/fake_skia_output_surface.cc
+++ b/components/viz/test/fake_skia_output_surface.cc
@@ -30,12 +30,12 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkPixelRef.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gfx/gpu_fence_handle.h"
 #include "ui/gfx/presentation_feedback.h"
 #include "ui/gfx/swap_result.h"
diff --git a/components/viz/test/test_context_provider.cc b/components/viz/test/test_context_provider.cc
index d95a710..c398ea7 100644
--- a/components/viz/test/test_context_provider.cc
+++ b/components/viz/test/test_context_provider.cc
@@ -29,8 +29,8 @@
 #include "gpu/command_buffer/common/shared_image_capabilities.h"
 #include "gpu/config/skia_limits.h"
 #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 
 namespace viz {
 
diff --git a/components/viz/test/test_in_process_context_provider.cc b/components/viz/test/test_in_process_context_provider.cc
index 5b05fc0..401eb41f 100644
--- a/components/viz/test/test_in_process_context_provider.cc
+++ b/components/viz/test/test_in_process_context_provider.cc
@@ -22,7 +22,7 @@
 #include "gpu/ipc/gl_in_process_context.h"
 #include "gpu/ipc/raster_in_process_context.h"
 #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 namespace viz {
 
diff --git a/components/webdata/README.md b/components/webdata/README.md
index 88c6fb1..2a0be0f 100644
--- a/components/webdata/README.md
+++ b/components/webdata/README.md
@@ -1,6 +1,12 @@
 The webdata component manages the "web database", a SQLite database stored in
 the user's profile containing various webpage-related metadata such as autofill
-and web search engine data.
+and web search engine data. A single database (per profile) is shared by many
+unrelated features.
+
+**If you are adding data storage for a [new] feature and want to use SQLite, you
+should probably default to creating a unique database using the compat layer in
+[`//sql`](https://source.chromium.org/chromium/chromium/src/+/main:sql/).**
+See [SQLite is a local optimum for Chromium](https://bit.ly/3Xc3EcH).
 
 This component is not allowed to depend on content/, because it is used by iOS.
 If dependencies on content/ need to be added, this component will have to be
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index c0d3fc6..c33d5c01 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -1248,7 +1248,8 @@
 
     // PowerMonitor is needed in reduced mode. BrowserMainLoop will safely skip
     // initializing it again if it has already been initialized.
-    base::PowerMonitor::Initialize(MakePowerMonitorDeviceSource());
+    base::PowerMonitor::GetInstance()->Initialize(
+        MakePowerMonitorDeviceSource());
 
     // Ensure the visibility tracker is created on the main thread.
     ProcessVisibilityTracker::GetInstance();
diff --git a/content/browser/ai/echo_ai_manager_impl.h b/content/browser/ai/echo_ai_manager_impl.h
index 3cb6e108..53bef73 100644
--- a/content/browser/ai/echo_ai_manager_impl.h
+++ b/content/browser/ai/echo_ai_manager_impl.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_AI_ECHO_AI_MANAGER_IMPL_H_
 #define CONTENT_BROWSER_AI_ECHO_AI_MANAGER_IMPL_H_
 
+#include <variant>
+
 #include "base/no_destructor.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_frame_host.h"
diff --git a/content/browser/android/battery_metrics.cc b/content/browser/android/battery_metrics.cc
index 79d5149d..bec2f0b 100644
--- a/content/browser/android/battery_metrics.cc
+++ b/content/browser/android/battery_metrics.cc
@@ -182,9 +182,10 @@
 
 void AndroidBatteryMetrics::InitializeOnSequence() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto* power_monitor = base::PowerMonitor::GetInstance();
   on_battery_power_ =
-      base::PowerMonitor::AddPowerStateObserverAndReturnOnBatteryState(this);
-  base::PowerMonitor::AddPowerThermalObserver(this);
+      power_monitor->AddPowerStateObserverAndReturnOnBatteryState(this);
+  power_monitor->AddPowerThermalObserver(this);
   content::ProcessVisibilityTracker::GetInstance()->AddObserver(this);
   // TODO(b/339859756): Update this call to take into account the unknown battery
   // status.
@@ -234,7 +235,7 @@
   if (should_be_enabled && !metrics_timer_.IsRunning()) {
     // Capture first capacity measurement and enable the repeating timer.
     last_remaining_capacity_uah_ =
-        base::PowerMonitor::GetRemainingBatteryCapacity();
+        base::PowerMonitor::GetInstance()->GetRemainingBatteryCapacity();
     skipped_timers_ = 0;
     observed_capacity_drops_ = 0;
 
@@ -252,7 +253,7 @@
 
 void AndroidBatteryMetrics::CaptureAndReportMetrics(bool disabling) {
   int remaining_capacity_uah =
-      base::PowerMonitor::GetRemainingBatteryCapacity();
+      base::PowerMonitor::GetInstance()->GetRemainingBatteryCapacity();
 
   if (remaining_capacity_uah >= last_remaining_capacity_uah_) {
     // No change in battery capacity, or it increased. The latter could happen
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 7fd2e314..b7844a4 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -668,8 +668,10 @@
   }
   {
     TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:PowerMonitor");
-    if (!base::PowerMonitor::IsInitialized())
-      base::PowerMonitor::Initialize(MakePowerMonitorDeviceSource());
+    if (auto* power_monitor = base::PowerMonitor::GetInstance();
+        !power_monitor->IsInitialized()) {
+      power_monitor->Initialize(MakePowerMonitorDeviceSource());
+    }
   }
   {
     TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:HighResTimerManager");
diff --git a/content/browser/file_system_access/file_system_access_observer_browsertest.cc b/content/browser/file_system_access/file_system_access_observer_browsertest.cc
index d58999af..bbfe8ee9 100644
--- a/content/browser/file_system_access/file_system_access_observer_browsertest.cc
+++ b/content/browser/file_system_access/file_system_access_observer_browsertest.cc
@@ -4,13 +4,10 @@
 
 #include <memory>
 
-#include "base/base_paths.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/json/values_util.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_timeouts.h"
-#include "base/win/windows_version.h"
 #include "build/buildflag.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/render_frame_host.h"
@@ -34,26 +31,12 @@
 constexpr int kBFCacheTestTimeoutMs = 3000;
 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
         // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_MAC)
-constexpr char kAttemptToObserveSymlinkHistogram[] =
-    "Storage.FileSystemAccess.AttemptToObserveSymlinkOrJunction";
 
 enum class TestFileSystemType {
   kBucket,
   kLocal,
 };
 
-enum class CreateSymbolicLinkResult {
-  // The symbolic link creation failed because the platform does not support it.
-  // On Windows, that may be due to the lack of the required privilege.
-  kUnsupported = -1,
-
-  // The symbolic link creation failed.
-  kFailed,
-
-  // The symbolic link was created successfully.
-  kSucceeded,
-};
-
 }  // namespace
 
 // Helpful macros to reduce the boilerplate script in the tests below.
@@ -175,88 +158,6 @@
     ui::SelectFileDialog::SetFactory(nullptr);
   }
 
-#if BUILDFLAG(IS_WIN)
-  CreateSymbolicLinkResult CreateWinSymbolicLink(const base::FilePath& target,
-                                                 const base::FilePath& symlink,
-                                                 bool is_directory = false) {
-    // Creating symbolic links on Windows requires Administrator privileges.
-    // However, recent versions of Windows introduced the
-    // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE flag, which allows the
-    // creation of symbolic links by processes with lower privileges, provided
-    // that Developer Mode is enabled.
-    //
-    // On older versions of Windows where the
-    // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE flag does not exist, the OS
-    // will return the error code ERROR_INVALID_PARAMETER when attempting to
-    // create a symbolic link without sufficient privileges.
-    if (base::win::GetVersion() < base::win::Version::WIN10_RS3) {
-      return CreateSymbolicLinkResult::kUnsupported;
-    }
-
-    DWORD flags = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
-
-    if (!::CreateSymbolicLink(
-            symlink.value().c_str(), target.value().c_str(),
-            flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
-      // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE works only if Developer
-      // Mode is enabled.
-      if (::GetLastError() == ERROR_PRIVILEGE_NOT_HELD) {
-        return CreateSymbolicLinkResult::kUnsupported;
-      }
-      return CreateSymbolicLinkResult::kFailed;
-    }
-
-    return CreateSymbolicLinkResult::kSucceeded;
-  }
-#endif  // BUILDFLAG(IS_WIN)
-
-  CreateSymbolicLinkResult CreateSymbolicLinkForTesting(
-      const base::FilePath& target,
-      const base::FilePath& symlink) {
-    // base::ScopedAllowBlockingForTesting allow_blocking;
-#if BUILDFLAG(IS_WIN)
-    return CreateWinSymbolicLink(target, symlink);
-#elif BUILDFLAG(IS_POSIX)
-    if (!base::CreateSymbolicLink(target, symlink)) {
-      return CreateSymbolicLinkResult::kFailed;
-    }
-    return CreateSymbolicLinkResult::kSucceeded;
-#else
-    return CreateSymbolicLinkResult::kUnsupported;
-#endif  // BUILDFLAG(IS_WIN)
-  }
-
-  std::optional<base::FilePath> CreateSymlinkToBePicked() {
-    base::FilePath file_path;
-    base::FilePath symlink_path = temp_dir_.GetPath().AppendASCII("symlink1");
-
-    {
-      base::ScopedAllowBlockingForTesting allow_blocking;
-      // Create the temporary file in the temp_dir_
-      EXPECT_TRUE(
-          base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path));
-      EXPECT_TRUE(base::WriteFile(file_path, "observe me"));
-
-      // Create a symbolic link to the temporary file
-      CreateSymbolicLinkResult result =
-          CreateSymbolicLinkForTesting(file_path, symlink_path);
-      if (result == CreateSymbolicLinkResult::kUnsupported) {
-        return std::nullopt;
-      }
-      EXPECT_EQ(result, CreateSymbolicLinkResult::kSucceeded);
-    }
-
-    // Set up the file dialog factory with the symlink path
-    ui::SelectFileDialog::SetFactory(
-        std::make_unique<FakeSelectFileDialogFactory>(
-            std::vector<base::FilePath>{symlink_path}));
-
-    // Navigate to the test URL
-    EXPECT_TRUE(NavigateToURL(shell(), test_url_));
-
-    return symlink_path;
-  }
-
   base::FilePath CreateFileToBePicked() {
     base::FilePath file_path;
     {
@@ -479,36 +380,6 @@
   EXPECT_THAT(records.GetList(), testing::IsEmpty());
 }
 
-// Local file system access - including the open*Picker() methods used here
-// - is not supported on Android or iOS. Fuchsia does not support symlinks.
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_FUCHSIA)
-IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
-                       SymlinkCannotBeObserved) {
-  base::HistogramTester histogram_tester;
-  std::optional<base::FilePath> symlink_path = CreateSymlinkToBePicked();
-  if (!symlink_path.has_value()) {
-    GTEST_SKIP() << "Platform does not support symlinks.";
-  }
-  const std::string script =
-      // clang-format off
-      "(async () => {"
-         CREATE_PROMISE_AND_RESOLVERS
-         START_OBSERVING_FILE(TestFileSystemType::kLocal)
-         SET_CHANGE_TIMEOUT
-      "})()";
-  // clang-format on
-  auto result = EvalJs(shell(), script);
-
-  // Check if a JavaScript error occurred.
-  EXPECT_TRUE(result.error.find("InvalidModificationError") !=
-              std::string::npos)
-      << "Unexpected result: " << result.error;
-  histogram_tester.ExpectUniqueSample(kAttemptToObserveSymlinkHistogram,
-                                      /*sample=*/true, 1);
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
-        // !BUILDFLAG(IS_FUCHSIA)
-
 class FileSystemAccessObserveWithUnobserveFlagBrowserTest
     : public FileSystemAccessObserveWithFlagBrowserTest {
  public:
@@ -611,7 +482,6 @@
 // TODO(b/360153904): Disabled on Mac due to flakiness.
 #if !BUILDFLAG(IS_MAC)
 IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest, ObserveFile) {
-  base::HistogramTester histogram_tester;
   base::FilePath file_path = CreateFileToBePicked();
 
   const std::string script =
@@ -625,8 +495,6 @@
   // clang-format on
   auto records = EvalJs(shell(), script).ExtractList();
   EXPECT_THAT(records.GetList(), testing::Not(testing::IsEmpty()));
-  histogram_tester.ExpectUniqueSample(kAttemptToObserveSymlinkHistogram,
-                                      /*sample=*/false, 1);
 }
 #endif  // !BUILDFLAG(IS_MAC)
 
@@ -689,7 +557,7 @@
   // clang-format on
   auto result = EvalJs(shell(), script);
 
-  // Check if a JavaScript error occurred and contains "NotFoundError".
+  // Check if a JavaScript error occurred and contains "NotFoundError"
   EXPECT_TRUE(result.error.find("NotFoundError") != std::string::npos)
       << "Unexpected result: " << result.error;
 }
@@ -712,7 +580,7 @@
   // clang-format on
   auto result = EvalJs(shell(), script);
 
-  // Check if a JavaScript error occurred and contains "NotFoundError".
+  // Check if a JavaScript error occurred and contains "NotFoundError"
   EXPECT_TRUE(result.error.find("NotFoundError") != std::string::npos)
       << "Unexpected result: " << result.error;
 }
diff --git a/content/browser/file_system_access/file_system_access_observer_host.cc b/content/browser/file_system_access/file_system_access_observer_host.cc
index c142176..a03a2d18 100644
--- a/content/browser/file_system_access/file_system_access_observer_host.cc
+++ b/content/browser/file_system_access/file_system_access_observer_host.cc
@@ -6,8 +6,6 @@
 
 #include <memory>
 
-#include "base/files/file_util.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/ranges/algorithm.h"
 #include "content/browser/file_system_access/file_system_access_directory_handle_impl.h"
 #include "content/browser/file_system_access/file_system_access_error.h"
@@ -102,53 +100,6 @@
       break;
   }
 
-  // Observing symlink and junction is not supported for Origin Trial.
-  // TODO(crbug.com/363195541): Add support for symlinks and junctions for
-  // feature launch.
-  base::FilePath path = resolved_token->url().path();
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(
-          [](base::FilePath path) -> bool {
-            base::FilePath check_path;
-            // `base::NormalizeFilePath()` resolves any file path elements like
-            // symbolic links or junctions by returning the target file path.
-            if (!base::NormalizeFilePath(path, &check_path)) {
-              check_path = path;
-            }
-            DCHECK(path.empty() == check_path.empty());
-            return check_path != path;
-          },
-          std::move(path)),
-      base::BindOnce(&FileSystemAccessObserverHost::DidCheckIfSymlinkOrJunction,
-                     weak_factory_.GetWeakPtr(), std::move(handle),
-                     std::move(callback), resolved_token->url(), is_recursive,
-                     handle_type));
-}
-
-void FileSystemAccessObserverHost::DidCheckIfSymlinkOrJunction(
-    absl::variant<std::unique_ptr<FileSystemAccessDirectoryHandleImpl>,
-                  std::unique_ptr<FileSystemAccessFileHandleImpl>> handle,
-    ObserveCallback callback,
-    storage::FileSystemURL url,
-    bool is_recursive,
-    FileSystemAccessPermissionContext::HandleType handle_type,
-    bool is_symlink_or_junction) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  base::UmaHistogramBoolean(
-      "Storage.FileSystemAccess.AttemptToObserveSymlinkOrJunction",
-      is_symlink_or_junction);
-  if (is_symlink_or_junction) {
-    std::move(callback).Run(
-        blink::mojom::FileSystemAccessError::New(
-            blink::mojom::FileSystemAccessStatus::kFileError,
-            base::File::FILE_ERROR_INVALID_OPERATION,
-            "Symlinks or junctions cannot be observed"),
-        mojo::NullReceiver());
-    return;
-  }
-
   manager_->DoFileSystemOperation(
       FROM_HERE,
       handle_type == FileSystemAccessPermissionContext::HandleType::kDirectory
@@ -156,8 +107,8 @@
           : &storage::FileSystemOperationRunner::FileExists,
       base::BindOnce(&FileSystemAccessObserverHost::DidCheckItemExists,
                      weak_factory_.GetWeakPtr(), std::move(handle),
-                     std::move(callback), url, is_recursive),
-      url);
+                     std::move(callback), resolved_token->url(), is_recursive),
+      resolved_token->url());
 }
 
 void FileSystemAccessObserverHost::DidCheckItemExists(
diff --git a/content/browser/file_system_access/file_system_access_observer_host.h b/content/browser/file_system_access/file_system_access_observer_host.h
index ff90992..3490f3d 100644
--- a/content/browser/file_system_access/file_system_access_observer_host.h
+++ b/content/browser/file_system_access/file_system_access_observer_host.h
@@ -65,15 +65,6 @@
       ObserveCallback callback,
       FileSystemAccessTransferTokenImpl* resolved_token);
 
-  void DidCheckIfSymlinkOrJunction(
-      absl::variant<std::unique_ptr<FileSystemAccessDirectoryHandleImpl>,
-                    std::unique_ptr<FileSystemAccessFileHandleImpl>> handle,
-      ObserveCallback callback,
-      storage::FileSystemURL url,
-      bool is_recursive,
-      FileSystemAccessPermissionContext::HandleType handle_type,
-      bool is_symlink_or_junction);
-
   void DidCheckItemExists(
       absl::variant<std::unique_ptr<FileSystemAccessDirectoryHandleImpl>,
                     std::unique_ptr<FileSystemAccessFileHandleImpl>> handle,
diff --git a/content/browser/indexed_db/indexed_db_bucket_context.cc b/content/browser/indexed_db/indexed_db_bucket_context.cc
index 4846531..b4c87e5 100644
--- a/content/browser/indexed_db/indexed_db_bucket_context.cc
+++ b/content/browser/indexed_db/indexed_db_bucket_context.cc
@@ -917,7 +917,8 @@
     int64_t version,
     mojo::PendingAssociatedReceiver<blink::mojom::IDBTransaction>
         transaction_receiver,
-    int64_t transaction_id) {
+    int64_t transaction_id,
+    int scheduling_priority) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("IndexedDB", "IndexedDBBucketContext::Open");
   // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
@@ -944,6 +945,7 @@
       transaction_id, version, std::move(transaction_receiver));
   connection->was_cold_open = was_cold_open;
   connection->data_loss_info = data_loss_info;
+  connection->scheduling_priority = scheduling_priority;
   ReceiverContext& client = receivers_.current_context();
   // `IndexedDBConnection` only needs an opaque token to uniquely identify the
   // document or worker that owns the other side of the connection.
diff --git a/content/browser/indexed_db/indexed_db_bucket_context.h b/content/browser/indexed_db/indexed_db_bucket_context.h
index 1d4127d..b13b571 100644
--- a/content/browser/indexed_db/indexed_db_bucket_context.h
+++ b/content/browser/indexed_db/indexed_db_bucket_context.h
@@ -304,7 +304,8 @@
             int64_t version,
             mojo::PendingAssociatedReceiver<blink::mojom::IDBTransaction>
                 transaction_receiver,
-            int64_t transaction_id) override;
+            int64_t transaction_id,
+            int scheduling_priority) override;
   void DeleteDatabase(mojo::PendingAssociatedRemote<
                           blink::mojom::IDBFactoryClient> factory_client,
                       const std::u16string& name,
diff --git a/content/browser/indexed_db/indexed_db_connection.cc b/content/browser/indexed_db/indexed_db_connection.cc
index 835a8b8..a47fedb 100644
--- a/content/browser/indexed_db/indexed_db_connection.cc
+++ b/content/browser/indexed_db/indexed_db_connection.cc
@@ -15,7 +15,9 @@
 #include "content/browser/indexed_db/indexed_db_callback_helpers.h"
 #include "content/browser/indexed_db/indexed_db_cursor.h"
 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
+#include "content/browser/indexed_db/indexed_db_lock_request_data.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
+#include "content/public/common/content_features.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
@@ -60,7 +62,8 @@
     std::unique_ptr<IndexedDBDatabaseCallbacks> callbacks,
     mojo::Remote<storage::mojom::IndexedDBClientStateChecker>
         client_state_checker,
-    base::UnguessableToken client_token)
+    base::UnguessableToken client_token,
+    int scheduling_priority)
     : id_(g_next_indexed_db_connection_id++),
       bucket_context_handle_(bucket_context),
       database_(std::move(database)),
@@ -68,7 +71,8 @@
       on_close_(std::move(on_close)),
       callbacks_(std::move(callbacks)),
       client_state_checker_(std::move(client_state_checker)),
-      client_token_(client_token) {
+      client_token_(client_token),
+      scheduling_priority_(scheduling_priority) {
   bucket_context_handle_->quota_manager()->NotifyBucketAccessed(
       bucket_context_handle_->bucket_locator(), base::Time::Now());
 }
@@ -743,6 +747,18 @@
   }
 }
 
+void IndexedDBConnection::UpdatePriority(int new_priority) {
+  scheduling_priority_ = new_priority;
+
+  for (const auto& [_, transaction] : transactions_) {
+    transaction->OnSchedulingPriorityUpdated(new_priority);
+  }
+
+  // TODO(crbug.com/359623664): consider reordering transactions already in the
+  // queue. For now the priority change will only impact where new transactions
+  // are placed (whether they skip past the existing ones).
+}
+
 const storage::BucketInfo& IndexedDBConnection::GetBucketInfo() {
   CHECK(bucket_context());
   return bucket_context()->bucket_info();
@@ -832,4 +848,34 @@
   return leveldb::Status::OK();
 }
 
+// static
+bool IndexedDBConnection::HasHigherPriorityThan(
+    const PartitionedLockHolder* this_one,
+    const PartitionedLockHolder& other) {
+  if (!base::FeatureList::IsEnabled(
+          features::kIdbPrioritizeForegroundClients)) {
+    return false;
+  }
+
+  auto* this_lock_request_data = static_cast<IndexedDBLockRequestData*>(
+      this_one->GetUserData(IndexedDBLockRequestData::kKey));
+  if (!this_lock_request_data) {
+    return false;
+  }
+
+  auto* other_lock_request_data = static_cast<IndexedDBLockRequestData*>(
+      other.GetUserData(IndexedDBLockRequestData::kKey));
+  if (!other_lock_request_data) {
+    return false;
+  }
+
+  if (this_lock_request_data->client_token ==
+      other_lock_request_data->client_token) {
+    return false;
+  }
+
+  return this_lock_request_data->scheduling_priority <
+         other_lock_request_data->scheduling_priority;
+}
+
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_connection.h b/content/browser/indexed_db/indexed_db_connection.h
index 0511b660..62b592e 100644
--- a/content/browser/indexed_db/indexed_db_connection.h
+++ b/content/browser/indexed_db/indexed_db_connection.h
@@ -56,7 +56,8 @@
                       std::unique_ptr<IndexedDBDatabaseCallbacks> callbacks,
                       mojo::Remote<storage::mojom::IndexedDBClientStateChecker>
                           client_state_checker,
-                      base::UnguessableToken client_token);
+                      base::UnguessableToken client_token,
+                      int scheduling_priority);
 
   IndexedDBConnection(const IndexedDBConnection&) = delete;
   IndexedDBConnection& operator=(const IndexedDBConnection&) = delete;
@@ -107,6 +108,15 @@
                                           const IndexedDBDatabaseError& error);
   void CloseAndReportForceClose();
 
+  int scheduling_priority() const { return scheduling_priority_; }
+
+  // Returns true if `this_one` should skip ahead of `other` when being added to
+  // the lock manager/scheduler. Two lock requests (which can be associated with
+  // transactions or new connection requests) will never be reordered if they
+  // come from the same client (window/worker context).
+  static bool HasHigherPriorityThan(const PartitionedLockHolder* this_one,
+                                    const PartitionedLockHolder& other);
+
  private:
   friend class IndexedDBTransactionTest;
   FRIEND_TEST_ALL_PREFIXES(IndexedDBDatabaseTest, ForcedClose);
@@ -188,6 +198,7 @@
                    const std::u16string& new_name) override;
   void Abort(int64_t transaction_id) override;
   void DidBecomeInactive() override;
+  void UpdatePriority(int new_priority) override;
 
   // It is an error to call either of these after `IsConnected()`
   // is no longer true.
@@ -249,6 +260,11 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  // The priority for transactions made on this connection. This corresponds to
+  // the renderer's scheduler throttling state. See `HasHigherPriorityThan()`
+  // for prioritization logic.
+  int scheduling_priority_;
+
   bool is_shutting_down_ = false;
 
   base::WeakPtrFactory<IndexedDBConnection> weak_factory_{this};
diff --git a/content/browser/indexed_db/indexed_db_connection_coordinator.cc b/content/browser/indexed_db/indexed_db_connection_coordinator.cc
index f71f37f1..01c7cda 100644
--- a/content/browser/indexed_db/indexed_db_connection_coordinator.cc
+++ b/content/browser/indexed_db/indexed_db_connection_coordinator.cc
@@ -45,6 +45,7 @@
 #include "content/browser/indexed_db/indexed_db_database_error.h"
 #include "content/browser/indexed_db/indexed_db_factory_client.h"
 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+#include "content/browser/indexed_db/indexed_db_lock_request_data.h"
 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
 #include "content/browser/indexed_db/indexed_db_reporting.h"
 #include "content/browser/indexed_db/indexed_db_task_helper.h"
@@ -138,8 +139,11 @@
         {{GetDatabaseLockId(db_->metadata().name),
           PartitionedLockManager::LockType::kExclusive}};
     state_ = RequestState::kPendingLocks;
-    db_->lock_manager().AcquireLocks(std::move(lock_requests), lock_receiver_,
-                                     std::move(next_step));
+
+    db_->lock_manager().AcquireLocks(
+        std::move(lock_requests), lock_receiver_, std::move(next_step),
+        base::BindRepeating(&IndexedDBConnection::HasHigherPriorityThan,
+                            &lock_receiver_));
   }
 
   RequestState state_ = RequestState::kNotStarted;
@@ -168,6 +172,15 @@
       : ConnectionRequest(bucket_context, db, connection_coordinator),
         pending_(std::move(pending_connection)) {
     db_->metadata_.was_cold_open = pending_->was_cold_open;
+
+    // Note that the `scheduling_priority` on this lock receiver isn't very
+    // important because locks are only acquired when upgrading the version, and
+    // that requires that all other connections be closed. So there shouldn't be
+    // a queue of outstanding lock requests to contend with.
+    lock_receiver_.SetUserData(
+        IndexedDBLockRequestData::kKey,
+        std::make_unique<IndexedDBLockRequestData>(pending_->client_token,
+                                                   /*scheduling_priority=*/0));
   }
 
   OpenRequest(const OpenRequest&) = delete;
@@ -237,7 +250,8 @@
       pending_->factory_client->OnOpenSuccess(
           db_->CreateConnection(std::move(pending_->database_callbacks),
                                 std::move(pending_->client_state_checker),
-                                pending_->client_token),
+                                pending_->client_token,
+                                pending_->scheduling_priority),
           db_->metadata_);
       bucket_context_handle_.Release();
       state_ = RequestState::kDone;
@@ -250,7 +264,8 @@
       pending_->factory_client->OnOpenSuccess(
           db_->CreateConnection(std::move(pending_->database_callbacks),
                                 std::move(pending_->client_state_checker),
-                                pending_->client_token),
+                                pending_->client_token,
+                                pending_->scheduling_priority),
           db_->metadata_);
       state_ = RequestState::kDone;
       bucket_context_handle_.Release();
@@ -330,7 +345,8 @@
     DCHECK(!lock_receiver_.locks.empty());
     connection_ = db_->CreateConnection(
         std::move(pending_->database_callbacks),
-        std::move(pending_->client_state_checker), pending_->client_token);
+        std::move(pending_->client_state_checker), pending_->client_token,
+        pending_->scheduling_priority);
     bucket_context_handle_.Release();
     DCHECK(!connection_ptr_for_close_comparision_);
     connection_ptr_for_close_comparision_ = connection_.get();
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 160587b..a09e377 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -124,7 +124,8 @@
             int64_t version,
             mojo::PendingAssociatedReceiver<blink::mojom::IDBTransaction>
                 transaction_receiver,
-            int64_t transaction_id) override {
+            int64_t transaction_id,
+            int scheduling_priority) override {
     IndexedDBFactoryClient(std::move(factory_client))
         .OnError(IndexedDBDatabaseError(
             blink::mojom::IDBException::kUnknownError, u"Internal error."));
diff --git a/content/browser/indexed_db/indexed_db_context_unittest.cc b/content/browser/indexed_db/indexed_db_context_unittest.cc
index 45a70de..6eb2be2 100644
--- a/content/browser/indexed_db/indexed_db_context_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_context_unittest.cc
@@ -195,7 +195,7 @@
                        database_callbacks->CreateInterfacePtrAndBind(),
                        u"database_name", /*version=*/1,
                        transaction_remote.BindNewEndpointAndPassReceiver(),
-                       /*transaction_id=*/0);
+                       /*transaction_id=*/0, /*priority=*/0);
   loop_2.Run();
 
   // IDBFactory::DeleteDatabase
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index fb4b4c6..7daa883e 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -214,7 +214,9 @@
 
   lock_manager().AcquireLocks(
       std::move(lock_requests), *transaction->mutable_locks_receiver(),
-      base::BindOnce(&IndexedDBTransaction::Start, transaction->AsWeakPtr()));
+      base::BindOnce(&IndexedDBTransaction::Start, transaction->AsWeakPtr()),
+      base::BindRepeating(&IndexedDBConnection::HasHigherPriorityThan,
+                          transaction->mutable_locks_receiver()));
 }
 
 std::tuple<IndexedDBDatabase::RunTasksResult, leveldb::Status>
@@ -1565,7 +1567,8 @@
     std::unique_ptr<IndexedDBDatabaseCallbacks> database_callbacks,
     mojo::Remote<storage::mojom::IndexedDBClientStateChecker>
         client_state_checker,
-    base::UnguessableToken client_token) {
+    base::UnguessableToken client_token,
+    int scheduling_priority) {
   auto connection = std::make_unique<IndexedDBConnection>(
       *bucket_context_, weak_factory_.GetWeakPtr(),
       base::BindRepeating(&IndexedDBDatabase::VersionChangeIgnored,
@@ -1573,7 +1576,7 @@
       base::BindOnce(&IndexedDBDatabase::ConnectionClosed,
                      weak_factory_.GetWeakPtr()),
       std::move(database_callbacks), std::move(client_state_checker),
-      client_token);
+      client_token, scheduling_priority);
   connections_.insert(connection.get());
   return connection;
 }
diff --git a/content/browser/indexed_db/indexed_db_database.h b/content/browser/indexed_db/indexed_db_database.h
index 95849b2..3890bcae 100644
--- a/content/browser/indexed_db/indexed_db_database.h
+++ b/content/browser/indexed_db/indexed_db_database.h
@@ -352,7 +352,8 @@
       std::unique_ptr<IndexedDBDatabaseCallbacks> database_callbacks,
       mojo::Remote<storage::mojom::IndexedDBClientStateChecker>
           client_state_checker,
-      base::UnguessableToken client_token);
+      base::UnguessableToken client_token,
+      int scheduling_priority);
 
   // Ack that one of the connections notified with a "versionchange" event did
   // not promptly close. Therefore a "blocked" event should be fired at the
diff --git a/content/browser/indexed_db/indexed_db_lock_request_data.cc b/content/browser/indexed_db/indexed_db_lock_request_data.cc
index 4f5bad9..249608bb 100644
--- a/content/browser/indexed_db/indexed_db_lock_request_data.cc
+++ b/content/browser/indexed_db/indexed_db_lock_request_data.cc
@@ -10,8 +10,9 @@
     &IndexedDBLockRequestData::kKey;
 
 IndexedDBLockRequestData::IndexedDBLockRequestData(
-    const base::UnguessableToken& client_token)
-    : client_token(client_token) {}
+    const base::UnguessableToken& client_token,
+    int scheduling_priority)
+    : client_token(client_token), scheduling_priority(scheduling_priority) {}
 
 IndexedDBLockRequestData::~IndexedDBLockRequestData() = default;
 
diff --git a/content/browser/indexed_db/indexed_db_lock_request_data.h b/content/browser/indexed_db/indexed_db_lock_request_data.h
index 05a33b3..e73d4ef 100644
--- a/content/browser/indexed_db/indexed_db_lock_request_data.h
+++ b/content/browser/indexed_db/indexed_db_lock_request_data.h
@@ -15,10 +15,12 @@
 struct IndexedDBLockRequestData : public base::SupportsUserData::Data {
   static const void* const kKey;
 
-  IndexedDBLockRequestData(const base::UnguessableToken& client_token);
+  IndexedDBLockRequestData(const base::UnguessableToken& client_token,
+                           int scheduling_priority);
   ~IndexedDBLockRequestData() override;
 
   base::UnguessableToken client_token;
+  int scheduling_priority;
 };
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_pending_connection.h b/content/browser/indexed_db/indexed_db_pending_connection.h
index 6dd6cbe..0c605f5 100644
--- a/content/browser/indexed_db/indexed_db_pending_connection.h
+++ b/content/browser/indexed_db/indexed_db_pending_connection.h
@@ -37,6 +37,7 @@
   std::unique_ptr<IndexedDBDatabaseCallbacks> database_callbacks;
   int64_t transaction_id;
   int64_t version;
+  int scheduling_priority = 0;
   IndexedDBDataLossInfo data_loss_info;
   // The versionchange operation, if any.
   base::WeakPtr<IndexedDBTransaction> transaction;
diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
index bf32ad4..641e344 100644
--- a/content/browser/indexed_db/indexed_db_transaction.cc
+++ b/content/browser/indexed_db/indexed_db_transaction.cc
@@ -142,7 +142,8 @@
 
   locks_receiver_.SetUserData(
       IndexedDBLockRequestData::kKey,
-      std::make_unique<IndexedDBLockRequestData>(connection->client_token()));
+      std::make_unique<IndexedDBLockRequestData>(
+          connection->client_token(), connection->scheduling_priority()));
 
   database_ = connection_->database();
   if (database_) {
@@ -898,4 +899,11 @@
   return lock_requests;
 }
 
+void IndexedDBTransaction::OnSchedulingPriorityUpdated(int new_priority) {
+  auto* lock_request_data = static_cast<IndexedDBLockRequestData*>(
+      locks_receiver_.GetUserData(IndexedDBLockRequestData::kKey));
+  DCHECK(lock_request_data);
+  lock_request_data->scheduling_priority = new_priority;
+}
+
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_transaction.h b/content/browser/indexed_db/indexed_db_transaction.h
index a1eba44b0..5d3fcf9 100644
--- a/content/browser/indexed_db/indexed_db_transaction.h
+++ b/content/browser/indexed_db/indexed_db_transaction.h
@@ -106,6 +106,8 @@
   std::vector<PartitionedLockManager::PartitionedLockRequest>
   BuildLockRequests() const;
 
+  void OnSchedulingPriorityUpdated(int new_priority);
+
   blink::mojom::IDBTransactionMode mode() const { return mode_; }
   const std::set<int64_t>& scope() const { return object_store_ids_; }
 
diff --git a/content/browser/indexed_db/indexed_db_transaction_unittest.cc b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
index 6f14c0ea..cb5ebf4 100644
--- a/content/browser/indexed_db/indexed_db_transaction_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
@@ -15,6 +15,8 @@
 #include "base/run_loop.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/test/run_until.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h"
@@ -24,6 +26,7 @@
 #include "content/browser/indexed_db/indexed_db_database_error.h"
 #include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+#include "content/public/common/content_features.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/browser/test/mock_quota_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -115,14 +118,14 @@
     return leveldb::Status::OK();
   }
 
-  std::unique_ptr<IndexedDBConnection> CreateConnection() {
+  std::unique_ptr<IndexedDBConnection> CreateConnection(int priority = 0) {
     mojo::Remote<storage::mojom::IndexedDBClientStateChecker> remote;
     auto connection = std::make_unique<IndexedDBConnection>(
         *bucket_context_, db_->AsWeakPtr(), base::DoNothing(),
         base::DoNothing(),
         std::make_unique<IndexedDBDatabaseCallbacks>(
             mojo::NullAssociatedRemote()),
-        std::move(remote), base::UnguessableToken::Create());
+        std::move(remote), base::UnguessableToken::Create(), priority);
     db_->AddConnectionForTesting(connection.get());
     return connection;
   }
@@ -313,6 +316,109 @@
   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
 }
 
+TEST_F(IndexedDBTransactionTest, WithoutPrioritization) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      features::kIdbPrioritizeForegroundClients);
+
+  const std::vector<int64_t> object_store_ids{1};
+  std::unique_ptr<IndexedDBConnection> low_pri_connection = CreateConnection(1);
+  std::unique_ptr<IndexedDBConnection> high_pri_connection =
+      CreateConnection(0);
+
+  // Create transaction that is incidentally low priority.
+  // No conflicting transactions, so coordinator will start it immediately:
+  IndexedDBTransaction* low_pri_transaction =
+      CreateTransaction(low_pri_connection.get(), /*id=*/0, object_store_ids,
+                        blink::mojom::IDBTransactionMode::ReadWrite);
+  EXPECT_EQ(IndexedDBTransaction::STARTED, low_pri_transaction->state());
+
+  // Create second transaction, which is blocked.
+  IndexedDBTransaction* low_pri_transaction2 =
+      CreateTransaction(low_pri_connection.get(), /*id=*/1, object_store_ids,
+                        blink::mojom::IDBTransactionMode::ReadWrite);
+  EXPECT_EQ(IndexedDBTransaction::CREATED, low_pri_transaction2->state());
+
+  // Create a high priority transaction, which also queues up.
+  IndexedDBTransaction* high_pri_transaction =
+      CreateTransaction(high_pri_connection.get(), /*id=*/2, object_store_ids,
+                        blink::mojom::IDBTransactionMode::ReadWrite);
+  EXPECT_EQ(IndexedDBTransaction::CREATED, high_pri_transaction->state());
+
+  // Finish the first low priority transaction. Verify the order of queueing of
+  // other transactions.
+  low_pri_transaction->Abort(IndexedDBDatabaseError(
+      blink::mojom::IDBException::kAbortError, "Transaction aborted by user."));
+  EXPECT_EQ(IndexedDBTransaction::FINISHED, low_pri_transaction->state());
+
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return IndexedDBTransaction::STARTED == low_pri_transaction2->state();
+  }));
+  EXPECT_EQ(IndexedDBTransaction::CREATED, high_pri_transaction->state());
+}
+
+TEST_F(IndexedDBTransactionTest, WithPrioritization) {
+  base::test::ScopedFeatureList scoped_feature_list{
+      features::kIdbPrioritizeForegroundClients};
+
+  const std::vector<int64_t> object_store_ids{1};
+  std::unique_ptr<IndexedDBConnection> low_pri_connection = CreateConnection(1);
+  std::unique_ptr<IndexedDBConnection> high_pri_connection =
+      CreateConnection(0);
+
+  // Create transaction that is incidentally low priority.
+  // No conflicting transactions, so coordinator will start it immediately:
+  IndexedDBTransaction* low_pri_transaction =
+      CreateTransaction(low_pri_connection.get(), /*id=*/0, object_store_ids,
+                        blink::mojom::IDBTransactionMode::ReadWrite);
+  EXPECT_EQ(IndexedDBTransaction::STARTED, low_pri_transaction->state());
+
+  // Create second transaction, which is blocked.
+  IndexedDBTransaction* low_pri_transaction2 =
+      CreateTransaction(low_pri_connection.get(), /*id=*/1, object_store_ids,
+                        blink::mojom::IDBTransactionMode::ReadWrite);
+  EXPECT_EQ(IndexedDBTransaction::CREATED, low_pri_transaction2->state());
+
+  // Create a couple high priority transactions, which skip ahead in the queue.
+  IndexedDBTransaction* high_pri_transaction =
+      CreateTransaction(high_pri_connection.get(), /*id=*/2, object_store_ids,
+                        blink::mojom::IDBTransactionMode::ReadWrite);
+  EXPECT_EQ(IndexedDBTransaction::CREATED, high_pri_transaction->state());
+  IndexedDBTransaction* high_pri_transaction2 =
+      CreateTransaction(high_pri_connection.get(), /*id=*/3, object_store_ids,
+                        blink::mojom::IDBTransactionMode::ReadWrite);
+  EXPECT_EQ(IndexedDBTransaction::CREATED, high_pri_transaction2->state());
+
+  // Finish the first low priority transaction. Verify the order of queueing of
+  // other transactions.
+  low_pri_transaction->Abort(IndexedDBDatabaseError(
+      blink::mojom::IDBException::kAbortError, "Transaction aborted by user."));
+  EXPECT_EQ(IndexedDBTransaction::FINISHED, low_pri_transaction->state());
+
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return IndexedDBTransaction::STARTED == high_pri_transaction->state();
+  }));
+  EXPECT_EQ(IndexedDBTransaction::CREATED, high_pri_transaction2->state());
+  EXPECT_EQ(IndexedDBTransaction::CREATED, low_pri_transaction2->state());
+
+  high_pri_transaction->Abort(IndexedDBDatabaseError(
+      blink::mojom::IDBException::kAbortError, "Transaction aborted by user."));
+  EXPECT_EQ(IndexedDBTransaction::FINISHED, high_pri_transaction->state());
+
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return IndexedDBTransaction::STARTED == high_pri_transaction2->state();
+  }));
+  EXPECT_EQ(IndexedDBTransaction::CREATED, low_pri_transaction2->state());
+
+  high_pri_transaction2->Abort(IndexedDBDatabaseError(
+      blink::mojom::IDBException::kAbortError, "Transaction aborted by user."));
+  EXPECT_EQ(IndexedDBTransaction::FINISHED, high_pri_transaction2->state());
+
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return IndexedDBTransaction::STARTED == low_pri_transaction2->state();
+  }));
+}
+
 TEST_P(IndexedDBTransactionTestMode, ScheduleNormalTask) {
   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
   IndexedDBTransaction* transaction =
diff --git a/content/browser/indexed_db/indexed_db_unittest.cc b/content/browser/indexed_db/indexed_db_unittest.cc
index ee5b077..3fe46c2 100644
--- a/content/browser/indexed_db/indexed_db_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_unittest.cc
@@ -177,7 +177,7 @@
         open_callbacks->CreateInterfacePtrAndBind(),
         connection_callbacks->CreateInterfacePtrAndBind(), db_name, version,
         version_change_transaction.BindNewEndpointAndPassReceiver(task_runner),
-        upgrade_txn_id);
+        upgrade_txn_id, /*priority=*/0);
     // ForcedClose is called on shutdown and depending on ordering and timing
     // may or may not happen, which is fine.
     EXPECT_CALL(*connection_callbacks, ForcedClose())
@@ -466,7 +466,7 @@
                           database_callbacks.CreateInterfacePtrAndBind(),
                           u"opendb", /*version=*/0,
                           transaction_remote.BindNewEndpointAndPassReceiver(),
-                          /*host_transaction_id=*/0);
+                          /*host_transaction_id=*/0, /*priority=*/0);
     run_loop.Run();
     EXPECT_TRUE(base::DirectoryExists(test_path));
 
@@ -2055,7 +2055,7 @@
                        database_callbacks.CreateInterfacePtrAndBind(), u"db",
                        /*version=*/1,
                        transaction_remote.BindNewEndpointAndPassReceiver(),
-                       /*transaction_id=*/1);
+                       /*transaction_id=*/1, /*priority=*/0);
   run_loop.Run();
 
   ASSERT_TRUE(context_->BucketContextExists(bucket_locator.id));
@@ -2113,7 +2113,7 @@
                          database_callbacks.CreateInterfacePtrAndBind(), u"db",
                          /*version=*/0,
                          transaction_remote.BindNewEndpointAndPassReceiver(),
-                         /*transaction_id=*/1);
+                         /*transaction_id=*/1, /*priority=*/0);
     run_loop.Run();
   }
 
@@ -2177,7 +2177,7 @@
                        database_callbacks.CreateInterfacePtrAndBind(), u"db",
                        /*version=*/0,
                        transaction_remote.BindNewEndpointAndPassReceiver(),
-                       /*transaction_id=*/1);
+                       /*transaction_id=*/1, /*priority=*/0);
   run_loop.Run();
   // GetDatabaseInfo didn't create the factory, so it shouldn't close it.
   {
@@ -2225,7 +2225,7 @@
                        database_callbacks.CreateInterfacePtrAndBind(), u"db",
                        /*version=*/1,
                        transaction_remote.BindNewEndpointAndPassReceiver(),
-                       /*transaction_id=*/1);
+                       /*transaction_id=*/1, /*priority=*/0);
   run_loop.Run();
 
   // A disk full error results in an error reported to the quota system.
@@ -2266,7 +2266,7 @@
                          database_callbacks.CreateInterfacePtrAndBind(),
                          db_name, db_version,
                          transaction_remote.BindNewEndpointAndPassReceiver(),
-                         /*transaction_id=*/1);
+                         /*transaction_id=*/1, /*priority=*/0);
     run_loop.Run();
   }
 
@@ -2283,7 +2283,7 @@
                          database_callbacks.CreateInterfacePtrAndBind(),
                          db_name, db_version,
                          transaction_remote.BindNewEndpointAndPassReceiver(),
-                         /*transaction_id=*/2);
+                         /*transaction_id=*/2, /*priority=*/0);
     run_loop.Run();
     IndexedDBBucketContext* bucket_context =
         GetBucketContext(bucket_locator.id);
@@ -2326,7 +2326,7 @@
                          database_callbacks.CreateInterfacePtrAndBind(),
                          db_name, /*version=*/1,
                          transaction_remote.BindNewEndpointAndPassReceiver(),
-                         /*transaction_id=*/1);
+                         /*transaction_id=*/1, /*priority=*/0);
     run_loop.Run();
 
     // This step is necessary to make sure the backing store is closed so that
@@ -2360,7 +2360,7 @@
                          database_callbacks.CreateInterfacePtrAndBind(),
                          db_name, /*version=*/1,
                          transaction_remote.BindNewEndpointAndPassReceiver(),
-                         /*transaction_id=*/2);
+                         /*transaction_id=*/2, /*priority=*/0);
     run_loop.Run();
   }
 }
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index 4890e31..3390384 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -15942,6 +15942,72 @@
 }
 
 TEST_F(AuctionRunnerTest,
+       PrivateAggregationRequestForEventFilteringIdNonKAnon) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      /*enabled_features=*/{blink::features::kPrivateAggregationApiFilteringIds,
+                            kPrivacySandboxAggregationServiceFilteringIds,
+                            blink::features::kFledgeConsiderKAnonymity,
+                            blink::features::kFledgeEnforceKAnonymity},
+      /*disabled_features=*/{});
+
+  // Only one bidder participating the auction, to keep things simple.
+  interest_group_buyers_ = {{kBidder1}};
+
+  const char kBidScript[] = R"(
+    const bid = %d;
+
+    function generateBid(
+        interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals,
+        browserSignals) {
+      privateAggregation.contributeToHistogramOnEvent('reserved.always', {
+        bucket: {baseValue: "bid-reject-reason"},
+        value: 1,
+        filteringId: 1n,
+      });
+      return {bid: bid, render: interestGroup.ads[0].renderURL};
+    }
+
+    function reportWin(
+        auctionSignals, perBuyerSignals, sellerSignals, browserSignals) {
+    }
+  )";
+
+  const std::string kSellerScript = R"(
+    function scoreAd(adMetadata, bid, auctionConfig, browserSignals) {
+      return bid;
+    }
+
+    function reportResult(auctionConfig, browserSignals) {
+    }
+  )";
+
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kBidder1Url,
+                                         base::StringPrintf(kBidScript, 1));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         kSellerScript);
+
+  // Nothing is authorized for k-anon.
+  RunStandardAuction(/*request_trusted_bidding_signals=*/false);
+  EXPECT_THAT(result_.errors, testing::UnorderedElementsAre());
+  EXPECT_FALSE(result_.aborted_by_script);
+  EXPECT_EQ(result_.winning_group_id, std::nullopt);
+  EXPECT_EQ(result_.ad_descriptor, std::nullopt);
+
+  EXPECT_THAT(private_aggregation_manager_.TakePrivateAggregationRequests(),
+              testing::UnorderedElementsAre(testing::Pair(
+                  kBidder1, ElementsAreRequests(
+                                // generateBid().
+                                BuildPrivateAggregationRequest(
+                                    /*bucket=*/static_cast<int>(
+                                        auction_worklet::mojom::RejectReason::
+                                            kBelowKAnonThreshold),
+                                    /*value=*/1, /*debug_mode_details=*/
+                                    blink::mojom::DebugModeDetails::New(),
+                                    /*filtering_id=*/1)))));
+}
+
+TEST_F(AuctionRunnerTest,
        PrivateAggregationReportGenerateBidInvalidReservedEventType) {
   StartStandardAuctionWithMockService();
 
diff --git a/content/browser/media/browser_feature_provider.cc b/content/browser/media/browser_feature_provider.cc
index 8dd1090..16a3308 100644
--- a/content/browser/media/browser_feature_provider.cc
+++ b/content/browser/media/browser_feature_provider.cc
@@ -51,8 +51,10 @@
           static_cast<int>(net::NetworkChangeNotifier::GetConnectionType()));
     } else if (desc.name == FeatureLibrary::BatteryPower().name) {
       bool is_battery = false;
-      if (base::PowerMonitor::IsInitialized())
-        is_battery = base::PowerMonitor::IsOnBatteryPower();
+      if (auto* power_monitor = base::PowerMonitor::GetInstance();
+          power_monitor->IsInitialized()) {
+        is_battery = power_monitor->IsOnBatteryPower();
+      }
       features[i] = FeatureValue(is_battery);
     }
   }
diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc
index 77c6f5d..a5db92c 100644
--- a/content/browser/notifications/platform_notification_context_impl.cc
+++ b/content/browser/notifications/platform_notification_context_impl.cc
@@ -276,6 +276,8 @@
   // |service_worker_context_| may be NULL in tests.
   if (service_worker_context_)
     service_worker_context_->RemoveObserver(this);
+
+  browser_context_ = nullptr;
 }
 
 void PlatformNotificationContextImpl::CreateService(
diff --git a/content/browser/notifications/platform_notification_context_impl.h b/content/browser/notifications/platform_notification_context_impl.h
index 46b07160..05d3a12d 100644
--- a/content/browser/notifications/platform_notification_context_impl.h
+++ b/content/browser/notifications/platform_notification_context_impl.h
@@ -345,7 +345,7 @@
   void LogClose(const NotificationDatabaseData& data);
 
   base::FilePath path_;
-  raw_ptr<BrowserContext, AcrossTasksDanglingUntriaged> browser_context_;
+  raw_ptr<BrowserContext> browser_context_;
 
   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
 
diff --git a/content/browser/notifications/platform_notification_service_proxy.h b/content/browser/notifications/platform_notification_service_proxy.h
index 9bbda73..bd4dab8 100644
--- a/content/browser/notifications/platform_notification_service_proxy.h
+++ b/content/browser/notifications/platform_notification_service_proxy.h
@@ -96,9 +96,8 @@
       scoped_refptr<ServiceWorkerRegistration> registration);
 
   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
-  raw_ptr<BrowserContext, AcrossTasksDanglingUntriaged> browser_context_;
-  raw_ptr<PlatformNotificationService, AcrossTasksDanglingUntriaged>
-      notification_service_;
+  raw_ptr<BrowserContext> browser_context_;
+  raw_ptr<PlatformNotificationService> notification_service_;
   base::WeakPtrFactory<PlatformNotificationServiceProxy> weak_ptr_factory_{
       this};
 };
diff --git a/content/browser/preloading/anchor_element_interaction_host_impl.cc b/content/browser/preloading/anchor_element_interaction_host_impl.cc
index c954f515..f06fb7c 100644
--- a/content/browser/preloading/anchor_element_interaction_host_impl.cc
+++ b/content/browser/preloading/anchor_element_interaction_host_impl.cc
@@ -6,8 +6,11 @@
 
 #include "content/browser/preloading/preloading_decider.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/service_worker_context.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/origin_util.h"
 #include "third_party/blink/public/common/features.h"
 
 namespace content {
@@ -20,10 +23,12 @@
 
 void MaybePrewarmHttpDiskCache(const GURL& url,
                                RenderFrameHost& render_frame_host) {
-  if (!base::FeatureList::IsEnabled(
-          blink::features::kHttpDiskCachePrewarming) ||
-      !blink::features::kHttpDiskCachePrewarmingTriggerOnPointerDownOrHover
-           .Get()) {
+  static const bool enabled =
+      base::FeatureList::IsEnabled(blink::features::kHttpDiskCachePrewarming) &&
+      blink::features::kHttpDiskCachePrewarmingTriggerOnPointerDownOrHover
+          .Get();
+
+  if (!enabled) {
     return;
   }
 
@@ -35,11 +40,84 @@
     return;
   }
 
+  // Disallow PrewarmHttpDiskCache when there are other windows that
+  // might script with this frame to mitigate security and privacy
+  // concerns.
+  if (render_frame_host.GetSiteInstance()->GetRelatedActiveContentsCount() >
+      1u) {
+    return;
+  }
+
   GetContentClient()->browser()->MaybePrewarmHttpDiskCache(
       *render_frame_host.GetBrowserContext(),
       render_frame_host.GetLastCommittedOrigin(), url);
 }
 
+void MaybeWarmUpServiceWorker(const GURL& url,
+                              RenderFrameHost& render_frame_host) {
+  if (!IsOutermostMainFrame(render_frame_host)) {
+    return;
+  }
+
+  // Disallow service worker warm-up when there are other windows that
+  // might script with this frame to mitigate security and privacy
+  // concerns.
+  if (render_frame_host.GetSiteInstance()->GetRelatedActiveContentsCount() >
+      1u) {
+    return;
+  }
+
+  content::StoragePartition* storage_partition =
+      render_frame_host.GetStoragePartition();
+
+  if (!storage_partition) {
+    return;
+  }
+
+  content::ServiceWorkerContext* service_worker_context =
+      storage_partition->GetServiceWorkerContext();
+
+  if (!service_worker_context) {
+    return;
+  }
+
+  if (!content::OriginCanAccessServiceWorkers(url)) {
+    return;
+  }
+
+  const blink::StorageKey key =
+      blink::StorageKey::CreateFirstParty(url::Origin::Create(url));
+
+  if (!service_worker_context->MaybeHasRegistrationForStorageKey(key)) {
+    return;
+  }
+
+  service_worker_context->WarmUpServiceWorker(url, key, base::DoNothing());
+}
+
+void MaybeWarmUpServiceWorkerOnPointerDown(const GURL& url,
+                                           RenderFrameHost& render_frame_host) {
+  static const bool enabled =
+      base::FeatureList::IsEnabled(
+          blink::features::kSpeculativeServiceWorkerWarmUp) &&
+      blink::features::kSpeculativeServiceWorkerWarmUpOnPointerdown.Get();
+  if (enabled) {
+    MaybeWarmUpServiceWorker(url, render_frame_host);
+  }
+}
+
+void MaybeWarmUpServiceWorkerOnPointerHover(
+    const GURL& url,
+    RenderFrameHost& render_frame_host) {
+  static const bool enabled =
+      base::FeatureList::IsEnabled(
+          blink::features::kSpeculativeServiceWorkerWarmUp) &&
+      blink::features::kSpeculativeServiceWorkerWarmUpOnPointerover.Get();
+  if (enabled) {
+    MaybeWarmUpServiceWorker(url, render_frame_host);
+  }
+}
+
 }  // namespace
 
 AnchorElementInteractionHostImpl::AnchorElementInteractionHostImpl(
@@ -62,6 +140,7 @@
       PreloadingDecider::GetOrCreateForCurrentDocument(&render_frame_host());
   preloading_decider->OnPointerDown(url);
   MaybePrewarmHttpDiskCache(url, render_frame_host());
+  MaybeWarmUpServiceWorkerOnPointerDown(url, render_frame_host());
 }
 
 void AnchorElementInteractionHostImpl::OnPointerHover(
@@ -71,6 +150,7 @@
       PreloadingDecider::GetOrCreateForCurrentDocument(&render_frame_host());
   preloading_decider->OnPointerHover(url, std::move(mouse_data));
   MaybePrewarmHttpDiskCache(url, render_frame_host());
+  MaybeWarmUpServiceWorkerOnPointerHover(url, render_frame_host());
 }
 
 }  // namespace content
diff --git a/content/browser/preloading/prefetch/prefetch_container.cc b/content/browser/preloading/prefetch/prefetch_container.cc
index 6d9c4ec..3b62989 100644
--- a/content/browser/preloading/prefetch/prefetch_container.cc
+++ b/content/browser/preloading/prefetch/prefetch_container.cc
@@ -16,6 +16,7 @@
 #include "content/browser/preloading/prefetch/prefetch_cookie_listener.h"
 #include "content/browser/preloading/prefetch/prefetch_document_manager.h"
 #include "content/browser/preloading/prefetch/prefetch_features.h"
+#include "content/browser/preloading/prefetch/prefetch_match_resolver.h"
 #include "content/browser/preloading/prefetch/prefetch_network_context.h"
 #include "content/browser/preloading/prefetch/prefetch_params.h"
 #include "content/browser/preloading/prefetch/prefetch_probe_result.h"
@@ -30,6 +31,7 @@
 #include "content/browser/preloading/preloading_attempt_impl.h"
 #include "content/browser/preloading/preloading_data_impl.h"
 #include "content/browser/preloading/preloading_trigger_type_impl.h"
+#include "content/browser/preloading/prerender/prerender_features.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -294,6 +296,8 @@
 void RecordWasBlockedUntilHeadWhenServingHistogram(
     const PrefetchType& prefetch_type,
     bool blocked_until_head) {
+  CHECK(!UseNewWaitLoop());
+
   if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
     base::UmaHistogramBoolean(
         base::StringPrintf(
@@ -307,10 +311,31 @@
   }
 }
 
+void RecordPrefetchMatchingBlockedNavigationWithPrefetchHistogram(
+    const PrefetchType& prefetch_type,
+    bool blocked_until_head) {
+  CHECK(UseNewWaitLoop());
+
+  if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
+    base::UmaHistogramBoolean(
+        base::StringPrintf(
+            "PrefetchProxy.AfterClick."
+            "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
+            GetPrefetchEagernessHistogramSuffix(prefetch_type.GetEagerness())
+                .c_str()),
+        blocked_until_head);
+  } else {
+    // TODO(crbug.com/40946257, crbug.com/40898833): Extend the metrics for
+    // embedder triggers.
+  }
+}
+
 void RecordBlockUntilHeadDurationHistogram(
     const PrefetchType& prefetch_type,
     const base::TimeDelta& block_until_head_duration,
     bool served) {
+  CHECK(!UseNewWaitLoop());
+
   if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
     base::UmaHistogramTimes(
         base::StringPrintf(
@@ -325,6 +350,26 @@
   }
 }
 
+void RecordBlockUntilHeadDuration2Histogram(
+    const PrefetchType& prefetch_type,
+    const base::TimeDelta block_until_head_duration,
+    bool served) {
+  CHECK(UseNewWaitLoop());
+
+  if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
+    base::UmaHistogramTimes(
+        base::StringPrintf(
+            "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.%s.%s",
+            served ? "Served" : "NotServed",
+            GetPrefetchEagernessHistogramSuffix(prefetch_type.GetEagerness())
+                .c_str()),
+        block_until_head_duration);
+  } else {
+    // TODO(crbug.com/40946257, crbug.com/40898833): Extend the metrics for
+    // embedder triggers.
+  }
+}
+
 ukm::SourceId GetUkmSourceId(RenderFrameHostImpl& rfhi) {
   // Prerendering page should not trigger prefetches.
   CHECK(
@@ -503,6 +548,14 @@
 PrefetchContainer::~PrefetchContainer() {
   is_in_dtor_ = true;
 
+  // Ideally, this method should be called just before dtor.
+  // https://chromium-review.googlesource.com/c/chromium/src/+/5657659/comments/0cfb14c0_3050963e
+  //
+  // TODO(crbug.com/356314759): Do it.
+  if (UseNewWaitLoop()) {
+    OnWillBeDestroyed();
+  }
+
   CancelStreamingURLLoaderIfNotServing();
 
   ukm::builders::PrefetchProxy_PrefetchedResource builder(ukm_source_id_);
@@ -534,7 +587,17 @@
     prefetch_document_manager_->PrefetchWillBeDestroyed(this);
   }
 
-  UnblockPrefetchMatchResolver();
+  if (!UseNewWaitLoop()) {
+    UnblockPrefetchMatchResolver();
+  }
+}
+
+void PrefetchContainer::OnWillBeDestroyed() {
+  CHECK(UseNewWaitLoop());
+
+  for (auto& observer : observers_) {
+    observer.OnWillBeDestroyed(*this);
+  }
 }
 
 PrefetchContainer::Key::Key(
@@ -1040,6 +1103,8 @@
     base::OnceCallback<void(PrefetchContainer&)>
         on_maybe_determined_head_callback,
     base::TimeDelta timeout) {
+  CHECK(!UseNewWaitLoop());
+
   on_maybe_determined_head_callback_ =
       std::move(on_maybe_determined_head_callback);
 
@@ -1055,6 +1120,8 @@
 }
 
 void PrefetchContainer::OnDeterminedHead() {
+  CHECK(!UseNewWaitLoop());
+
   // Propagates the header to `no_vary_search_data_` if a non-redirect response
   // header is got.
   //
@@ -1069,8 +1136,27 @@
   UnblockPrefetchMatchResolver();
 }
 
+void PrefetchContainer::OnDeterminedHead2() {
+  CHECK(UseNewWaitLoop());
+
+  // Propagates the header to `no_vary_search_data_` if a non-redirect response
+  // header is got.
+  //
+  // TODO(crbug.com/40946257): Current code doesn't support NVS for
+  // browser-initated triggers.
+  if (IsRendererInitiated()) {
+    auto* rfhi_can_be_null =
+        RenderFrameHostImpl::FromID(referring_render_frame_host_id_);
+    MaybeSetNoVarySearchData(rfhi_can_be_null);
+  }
+
+  for (auto& observer : observers_) {
+    observer.OnDeterminedHead(*this);
+  }
+}
+
 void PrefetchContainer::MaybeSetNoVarySearchData(RenderFrameHost* rfh) {
-  CHECK(!no_vary_search_data_);
+  CHECK(!no_vary_search_data_.has_value());
 
   if (!GetNonRedirectHead()) {
     return;
@@ -1081,6 +1167,8 @@
 }
 
 void PrefetchContainer::UnblockPrefetchMatchResolver() {
+  CHECK(!UseNewWaitLoop());
+
   block_until_head_timer_.reset();
 
   if (on_maybe_determined_head_callback_) {
@@ -1292,13 +1380,34 @@
   SetPrefetchStatus(PrefetchStatus::kPrefetchSuccessful);
 }
 
-void PrefetchContainer::OnCookiesChanged() {
+void PrefetchContainer::OnDetectedCookiesChange() {
   CHECK_NE(GetPrefetchStatus(), PrefetchStatus::kPrefetchNotUsedCookiesChanged);
   SetPrefetchStatus(PrefetchStatus::kPrefetchNotUsedCookiesChanged);
+  on_detected_cookies_change_called_ = true;
   UpdateServingPageMetrics();
   CancelStreamingURLLoaderIfNotServing();
 }
 
+void PrefetchContainer::OnDetectedCookiesChange2() {
+  CHECK(UseNewWaitLoop());
+
+  // If `kPrefetchNewWaitLoop` is enabled, multiple `PrefetchMatchResolver2` can
+  // wait the same `PrefetchContainer`. So, `OnDetectedCookiesChange2()` can be
+  // called multiple times, unlike `OnDetectedCookiesChange()`.
+  //
+  // TODO(crbug.com/353490734): Remove this comment and merge
+  // `OnDetectedCookiesChange()` to it when removing `kPrefetchNewWaitLoop` as
+  // this comment is just a note about the difference to the old path.
+  //
+  // Do not call `OnDetectedCookiesChange()` multiple times even if
+  // `OnDetectedCookiesChange2()` is called multiple times.
+  if (on_detected_cookies_change_called_) {
+    return;
+  }
+
+  OnDetectedCookiesChange();
+}
+
 // TODO(crbug.com/40274818): We might be waiting on PrefetchContainer's head
 // from multiple navigations.
 // E.g. We might wait from one navigation but not use the prefetch, and
@@ -1306,6 +1415,8 @@
 // for the head. We need to keep track of blocked_until_head_start_time_ per
 // each navigation for this PrefetchContainer.
 void PrefetchContainer::OnGetPrefetchToServe(bool blocked_until_head) {
+  CHECK(!UseNewWaitLoop());
+
   // OnGetPrefetchToServe is called before we start waiting for head, and
   // when the prefetch is used from `prefetches_ready_to_serve_`.
   // If the prefetch had to wait for head, `blocked_until_head_start_time_`
@@ -1322,6 +1433,8 @@
 
 void PrefetchContainer::OnReturnPrefetchToServe(bool served,
                                                 const GURL& navigated_url) {
+  CHECK(!UseNewWaitLoop());
+
   if (served) {
     RecordAfterClickRedirectChainSize(redirect_chain_.size());
     navigated_to_ = true;
@@ -1679,6 +1792,18 @@
   }
 }
 
+void PrefetchContainer::AddObserver(Observer* observer) {
+  CHECK(UseNewWaitLoop());
+
+  observers_.AddObserver(observer);
+}
+
+void PrefetchContainer::RemoveObserver(Observer* observer) {
+  CHECK(UseNewWaitLoop());
+
+  observers_.RemoveObserver(observer);
+}
+
 bool PrefetchContainer::IsExactMatch(const GURL& url) const {
   return url == GetURL();
 }
@@ -1690,4 +1815,35 @@
          no_vary_search_data->AreEquivalent(url, GetURL());
 }
 
+void PrefetchContainer::OnUnregisterCandidate(
+    const GURL& navigated_url,
+    bool is_served,
+    std::optional<base::TimeDelta> blocked_duration) {
+  // Note that this method can be called with `is_in_dtor_` true.
+  //
+  // TODO(crbug.com/356314759): Avoid calling this with `is_in_dtor_`
+  // true.
+
+  CHECK(UseNewWaitLoop());
+
+  if (is_served) {
+    navigated_to_ = true;
+    RecordAfterClickRedirectChainSize(redirect_chain_.size());
+  }
+
+  RecordPrefetchMatchingBlockedNavigationWithPrefetchHistogram(
+      prefetch_type_, blocked_duration.has_value());
+
+  if (blocked_duration.has_value()) {
+    RecordBlockUntilHeadDuration2Histogram(prefetch_type_,
+                                           blocked_duration.value(), is_served);
+  }
+
+  // See the comment in `PrefetchContainer::OnReturnPrefetchToServe()`.
+  if (auto attempt = preloading_attempt()) {
+    static_cast<PreloadingAttemptImpl*>(attempt.get())
+        ->SetIsAccurateTriggering(navigated_url);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/preloading/prefetch/prefetch_container.h b/content/browser/preloading/prefetch/prefetch_container.h
index b584e23a..1d768b5 100644
--- a/content/browser/preloading/prefetch/prefetch_container.h
+++ b/content/browser/preloading/prefetch/prefetch_container.h
@@ -10,6 +10,8 @@
 
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
 #include "base/time/time.h"
 #include "content/browser/preloading/prefetch/prefetch_probe_result.h"
 #include "content/browser/preloading/prefetch/prefetch_status.h"
@@ -171,6 +173,30 @@
     const GURL prefetch_url_;
   };
 
+  // Observer interface to listen to lifecycle events of `PrefetchContainer`.
+  //
+  // Each callback is called at most once in the lifecycle of a container.
+  //
+  // Be careful about using this. This is designed only for
+  // `PrefetchMatchResolver2`.
+  //
+  // These callback are called only if `kPrefetchNewWaitLoop` is enabled.
+  // Observer interface to listen to lifecycle events of `PrefetchContainer`.
+  class Observer : public base::CheckedObserver {
+   public:
+    // Called at the head of dtor.
+    //
+    // TODO(crbug.com/356314759): Update the description to "Called just
+    // before dtor is called."
+    virtual void OnWillBeDestroyed(PrefetchContainer& prefetch_container) = 0;
+    // Called if non-redirect header of prefetch response is determined, i.e.
+    // successfully received or fetch requests including redirects failed.
+    // Callers can check success/failure by `GetNonRedirectHead()`.
+    virtual void OnDeterminedHead(PrefetchContainer& prefetch_container) = 0;
+  };
+
+  void OnWillBeDestroyed();
+
   const Key& GetPrefetchContainerKey() const { return key_; }
 
   // The ID of the RenderFrameHost that triggered the prefetch.
@@ -403,6 +429,7 @@
   // This method must be called at most once in the lifecycle of
   // `PrefetchContainer`.
   void OnDeterminedHead();
+  void OnDeterminedHead2();
   // Unblocks waiting `PrefetchMatchResolver`.
   //
   // This method can be called multiple times.
@@ -464,10 +491,18 @@
   // message to DevTools console and can be null.
   void MaybeSetNoVarySearchData(RenderFrameHost* rfh);
 
-  // Called when cookies changes are detected via
-  // `HaveDefaultContextCookiesChanged()`, either for `this` or other
-  // `PrefetchContainer`s under the same `PrefetchMatchResolver`.
-  void OnCookiesChanged();
+  // Called upon detecting a change to cookies within the redirect chain.
+  //
+  // Note that there are two paths:
+  //
+  // - Roughly speaking, when non-redirect header received and
+  //   `PrefetchService`/`PrefetchContainer` detected cookies change of the head
+  //   of redirect chain. `PrefetchMatchResolver`/`PrefetchMatchResolver2`
+  //   propagates it to other waiting prefetches as they share domain.
+  // - When `PrefetchURLLoaderInterceptor::MaybeCreateLoader()` handles
+  //   redirects in the serving prefetch.
+  void OnDetectedCookiesChange();
+  void OnDetectedCookiesChange2();
 
   class SinglePrefetch;
 
@@ -578,9 +613,25 @@
 
   Reader CreateReader();
 
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   bool IsExactMatch(const GURL& url) const;
   bool IsNoVarySearchHeaderMatch(const GURL& url) const;
 
+  // Records metrics when serving result is determined.
+  //
+  // This is eventually called once for every `PrefetchContainer` put in
+  // `PrefetchMatchResolver2::candidates_`, i.e. those potentially matching
+  // and expected to become servable at the head of
+  // `PrefetchMatchResolver2::FindPrefetch()`.
+  //
+  // This can be called multiple times, because this can be called for multiple
+  // `PrefetchMatchResolver2`s.
+  void OnUnregisterCandidate(const GURL& navigated_url,
+                             bool is_served,
+                             std::optional<base::TimeDelta> blocked_duration);
+
   bool is_in_dtor() const { return is_in_dtor_; }
 
  protected:
@@ -687,6 +738,11 @@
   // purpose.
   std::optional<PrefetchStatus> prefetch_status_;
 
+  // True iff `PrefetchStatus` was set to `kPrefetchNotUsedCookiesChanged` once.
+  //
+  // TODO(crbug.com/40075414): Remove this.
+  bool on_detected_cookies_change_called_ = false;
+
   // The current status of the prefetch.
   LoadState load_state_ = LoadState::kNotStarted;
 
@@ -786,6 +842,8 @@
   // True iff the destructor was called.
   bool is_in_dtor_ = false;
 
+  base::ObserverList<Observer> observers_;
+
   base::WeakPtrFactory<PrefetchContainer> weak_method_factory_{this};
 };
 
diff --git a/content/browser/preloading/prefetch/prefetch_container_unittest.cc b/content/browser/preloading/prefetch/prefetch_container_unittest.cc
index 48c648b0..5285907b 100644
--- a/content/browser/preloading/prefetch/prefetch_container_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_container_unittest.cc
@@ -866,6 +866,9 @@
 }
 
 TEST_P(PrefetchContainerTest, BlockUntilHeadHistograms) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures({}, {features::kPrefetchNewWaitLoop});
+
   struct TestCase {
     blink::mojom::SpeculationEagerness eagerness;
     bool block_until_head;
@@ -937,6 +940,82 @@
       base::Milliseconds(40), 1);
 }
 
+TEST_P(PrefetchContainerTest, BlockUntilHeadHistograms2) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures({features::kPrefetchNewWaitLoop}, {});
+
+  struct TestCase {
+    blink::mojom::SpeculationEagerness eagerness;
+    bool is_served;
+    std::optional<base::TimeDelta> prefetch_match_resolver_wait_duration;
+  };
+
+  std::vector<TestCase> test_cases{
+      {blink::mojom::SpeculationEagerness::kEager, true, std::nullopt},
+      {blink::mojom::SpeculationEagerness::kModerate, true,
+       base::Milliseconds(10)},
+      {blink::mojom::SpeculationEagerness::kConservative, false,
+       base::Milliseconds(20)}};
+
+  base::HistogramTester histogram_tester;
+  for (const auto& test_case : test_cases) {
+    PrefetchContainer prefetch_container(
+        *main_rfhi(), blink::DocumentToken(),
+        GURL("https://test.com/?nvsparam=1"),
+        PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                     /*use_prefetch_proxy=*/true, test_case.eagerness),
+        blink::mojom::Referrer(),
+        /*no_vary_search_expected=*/std::nullopt,
+        /*prefetch_document_manager=*/nullptr);
+
+    prefetch_container.OnUnregisterCandidate(
+        GURL("https://test.com/"), test_case.is_served,
+        test_case.prefetch_match_resolver_wait_duration);
+  }
+
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Eager",
+      true, 0);
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Eager",
+      false, 1);
+  histogram_tester.ExpectTotalCount(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.Eager", 0);
+  histogram_tester.ExpectTotalCount(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.Eager", 0);
+
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Moderate",
+      true, 1);
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Moderate",
+      false, 0);
+  histogram_tester.ExpectUniqueTimeSample(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.Moderate",
+      base::Milliseconds(10), 1);
+  histogram_tester.ExpectTotalCount(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.Moderate", 0);
+
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Conservative",
+      true, 1);
+  histogram_tester.ExpectBucketCount(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Conservative",
+      false, 0);
+  histogram_tester.ExpectTotalCount(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.Conservative",
+      0);
+  histogram_tester.ExpectUniqueTimeSample(
+      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.Conservative",
+      base::Milliseconds(20), 1);
+}
+
 TEST_P(PrefetchContainerTest, RecordRedirectChainSize) {
   base::HistogramTester histogram_tester;
 
diff --git a/content/browser/preloading/prefetch/prefetch_match_resolver.cc b/content/browser/preloading/prefetch/prefetch_match_resolver.cc
index e5091fc..04962e5 100644
--- a/content/browser/preloading/prefetch/prefetch_match_resolver.cc
+++ b/content/browser/preloading/prefetch/prefetch_match_resolver.cc
@@ -2,17 +2,38 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <vector>
-
 #include "content/browser/preloading/prefetch/prefetch_match_resolver.h"
 
+#include <vector>
+
+#include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/ranges/algorithm.h"
 #include "content/browser/preloading/prefetch/prefetch_container.h"
+#include "content/browser/preloading/prefetch/prefetch_params.h"
+#include "content/browser/preloading/prefetch/prefetch_service.h"
 #include "content/public/browser/navigation_handle_user_data.h"
 
 namespace content {
 
+namespace {
+
+// Returns keys of `map` for safe iteration.
+//
+// `K` must be copyable.
+template <class K, class V>
+std::vector<K> Keys(const std::map<K, V>& map) {
+  std::vector<K> keys;
+
+  for (auto& item : map) {
+    keys.push_back(item.first);
+  }
+
+  return keys;
+}
+
+}  // namespace
+
 PrefetchMatchResolver::PrefetchMatchResolver(
     NavigationHandle& navigation_handle) {}
 
@@ -102,7 +123,7 @@
         const GURL& navigated_url) {
   // The prefetch_container has already received its head.
   CHECK(!IsWaitingForPrefetch(prefetch_container));
-  prefetch_container.OnCookiesChanged();
+  prefetch_container.OnDetectedCookiesChange();
   prefetch_container.OnReturnPrefetchToServe(/*served=*/false, navigated_url);
   DVLOG(1) << *this
            << "::FallbackToRegularNavigationWhenMatchedPrefetchCookiesChanged:"
@@ -114,7 +135,7 @@
     if (!weak_prefetch_container) {
       continue;
     }
-    weak_prefetch_container->OnCookiesChanged();
+    weak_prefetch_container->OnDetectedCookiesChange();
     weak_prefetch_container->OnReturnPrefetchToServe(/*served=*/false,
                                                      navigated_url);
     DVLOG(1)
@@ -145,4 +166,338 @@
 
 NAVIGATION_HANDLE_USER_DATA_KEY_IMPL(PrefetchMatchResolver);
 
+PrefetchMatchResolver2::CandidateData::CandidateData() = default;
+PrefetchMatchResolver2::CandidateData::~CandidateData() = default;
+
+PrefetchMatchResolver2::PrefetchMatchResolver2(
+    PrefetchContainer::Key navigated_key,
+    base::WeakPtr<PrefetchService> prefetch_service,
+    Callback callback)
+    : navigated_key_(std::move(navigated_key)),
+      prefetch_service_(std::move(prefetch_service)),
+      callback_(std::move(callback)) {}
+
+PrefetchMatchResolver2::~PrefetchMatchResolver2() = default;
+
+std::optional<base::TimeDelta> PrefetchMatchResolver2::GetBlockedDuration()
+    const {
+  if (wait_started_at_.has_value()) {
+    return base::TimeTicks::Now() - wait_started_at_.value();
+  } else {
+    return std::nullopt;
+  }
+}
+
+// static
+void PrefetchMatchResolver2::FindPrefetch(
+    PrefetchContainer::Key navigated_key,
+    PrefetchService& prefetch_service,
+    base::WeakPtr<PrefetchServingPageMetricsContainer>
+        serving_page_metrics_container,
+    Callback callback) {
+  // See the comment of `self_`.
+  auto prefetch_match_resolver = base::WrapUnique(new PrefetchMatchResolver2(
+      std::move(navigated_key), prefetch_service.GetWeakPtr(),
+      std::move(callback)));
+  PrefetchMatchResolver2& ref = *prefetch_match_resolver.get();
+  ref.self_ = std::move(prefetch_match_resolver);
+
+  ref.FindPrefetchInternal(prefetch_service,
+                           std::move(serving_page_metrics_container));
+}
+
+void PrefetchMatchResolver2::FindPrefetchInternal(
+    PrefetchService& prefetch_service,
+    base::WeakPtr<PrefetchServingPageMetricsContainer>
+        serving_page_metrics_container) {
+  for (auto& prefetch_container :
+       prefetch_service.CollectPotentiallyMatchingPrefetchContainers(
+           navigated_key_, std::move(serving_page_metrics_container))) {
+    PrefetchContainer::ServableState servable_state =
+        prefetch_container->GetServableState(PrefetchCacheableDuration());
+
+    // Filter out `kNotServable` ones.
+    //
+    // Note that
+    // `PrefetchService::CollectPotentiallyMatchingPrefetchContainers()` above
+    // checks `ServableState`, but the return value of
+    // `PrefetchContainer::GetServableState()` depends on
+    // `base::TimeTicks::now()` and can expires (can become `kServable` to
+    // `kNotServable`) in the minute between two calls. So, we rely on
+    // `cached_servable_state` during this method.
+    switch (servable_state) {
+      case PrefetchContainer::ServableState::kNotServable:
+        continue;
+      case PrefetchContainer::ServableState::kServable:
+      case PrefetchContainer::ServableState::kShouldBlockUntilHeadReceived:
+        break;
+    }
+
+    RegisterCandidate(*prefetch_container, servable_state);
+  }
+
+  // Backward compatibility to the behavior of `PrefetchMatchResolver`: If it
+  // finds a `PrefetchContainer` in which the cookies of the head of redirect
+  // chain changed, propagate it to other potentially matching
+  // `PrefetchContainer`s and give up to serve.
+  //
+  // TODO(kenoss): kenoss@ believes that we can improve it by propagating
+  // cookies changed event with `PrefetchContainer` and `PrefetchService`
+  // (without `PrefetchMatchResoler`/`PrefetchMatchResoler2`) and marking them
+  // `kNotServable`.
+  for (auto& candidate : candidates_) {
+    switch (candidate.second->cached_servable_state) {
+      case PrefetchContainer::ServableState::kServable:
+        if (candidate.second->prefetch_container->CreateReader()
+                .HaveDefaultContextCookiesChanged()) {
+          UnblockForCookiesChanged();
+          return;
+        }
+        break;
+      case PrefetchContainer::ServableState::kNotServable:
+        NOTREACHED_NORETURN();
+      case PrefetchContainer::ServableState::kShouldBlockUntilHeadReceived:
+        // nop
+        break;
+    }
+  }
+
+  for (auto& candidate : candidates_) {
+    switch (candidate.second->cached_servable_state) {
+      case PrefetchContainer::ServableState::kServable:
+        // Got matching and servable.
+        UnblockForMatch(candidate.first);
+        return;
+      case PrefetchContainer::ServableState::kNotServable:
+        NOTREACHED_NORETURN();
+      case PrefetchContainer::ServableState::kShouldBlockUntilHeadReceived:
+        // nop
+        break;
+    }
+  }
+
+  // There is no matching and servable prefetch at this point. We should wait
+  // remaining ones.
+
+  CHECK(!wait_started_at_.has_value());
+  wait_started_at_ = base::TimeTicks::Now();
+
+  for (auto& candidate : candidates_) {
+    StartWaitFor(candidate.first);
+  }
+
+  if (candidates_.size() == 0) {
+    UnblockForNoCandidates();
+  }
+}
+
+void PrefetchMatchResolver2::RegisterCandidate(
+    PrefetchContainer& prefetch_container,
+    PrefetchContainer::ServableState servable_state) {
+  auto candidate_data = std::make_unique<CandidateData>();
+  // #prefetch-key-availability
+  //
+  // Note that `CHECK(candidates_.contains(prefetch_key))` and
+  // `CHECK(candidate_data->prefetch_container)` below always hold because
+  // `PrefetchMatchResolver2` observes lifecycle events of `PrefetchContainer`.
+  candidate_data->prefetch_container = prefetch_container.GetWeakPtr();
+  candidate_data->cached_servable_state = servable_state;
+  candidate_data->timeout_timer = nullptr;
+
+  candidates_[prefetch_container.GetPrefetchContainerKey()] =
+      std::move(candidate_data);
+}
+
+void PrefetchMatchResolver2::StartWaitFor(
+    const PrefetchContainer::Key& prefetch_key) {
+  // By #prefetch-key-availability
+  CHECK(candidates_.contains(prefetch_key));
+  auto& candidate_data = candidates_[prefetch_key];
+  CHECK(candidate_data->prefetch_container);
+  PrefetchContainer& prefetch_container = *candidate_data->prefetch_container;
+
+  CHECK_EQ(candidate_data->cached_servable_state,
+           PrefetchContainer::ServableState::kShouldBlockUntilHeadReceived);
+  // `kServable` -> `kNotServable` is the only possible change during
+  // `FindPrefetchInternal()` call. If `cached_servable_state` is
+  // `kShouldBlockUntilHeadReceived`, `GetServableState()` is the same.
+  CHECK_EQ(prefetch_container.GetServableState(PrefetchCacheableDuration()),
+           PrefetchContainer::ServableState::kShouldBlockUntilHeadReceived);
+  CHECK(!candidate_data->timeout_timer);
+
+  // TODO(crbug.com/356552413): Merge
+  // https://chromium-review.googlesource.com/c/chromium/src/+/5668924 and write
+  // tests.
+  base::TimeDelta timeout =
+      PrefetchBlockUntilHeadTimeout(prefetch_container.GetPrefetchType());
+  if (timeout.is_positive()) {
+    candidate_data->timeout_timer = std::make_unique<base::OneShotTimer>();
+    candidate_data->timeout_timer->Start(
+        FROM_HERE, timeout,
+        base::BindOnce(&PrefetchMatchResolver2::OnTimeout,
+                       // Safety: `timeout_timer` is owned by this.
+                       Unretained(this), prefetch_key));
+  }
+
+  prefetch_container.AddObserver(this);
+}
+
+void PrefetchMatchResolver2::UnregisterCandidate(
+    const PrefetchContainer::Key& prefetch_key,
+    bool is_served) {
+  // By #prefetch-key-availability
+  CHECK(candidates_.contains(prefetch_key));
+  auto& candidate_data = candidates_[prefetch_key];
+  CHECK(candidate_data->prefetch_container);
+  PrefetchContainer& prefetch_container = *candidate_data->prefetch_container;
+
+  prefetch_container.OnUnregisterCandidate(navigated_key_.prefetch_url(),
+                                           is_served, GetBlockedDuration());
+  prefetch_container.RemoveObserver(this);
+  candidates_.erase(prefetch_key);
+}
+
+void PrefetchMatchResolver2::OnWillBeDestroyed(
+    PrefetchContainer& prefetch_container) {
+  MaybeUnblockForUnmatch(prefetch_container.GetPrefetchContainerKey());
+}
+
+void PrefetchMatchResolver2::OnDeterminedHead(
+    PrefetchContainer& prefetch_container) {
+  CHECK(candidates_.contains(prefetch_container.GetPrefetchContainerKey()));
+  CHECK(!prefetch_container.is_in_dtor());
+
+  if (prefetch_container.CreateReader().HaveDefaultContextCookiesChanged()) {
+    UnblockForCookiesChanged();
+    return;
+  }
+
+  switch (prefetch_container.GetServableState(PrefetchCacheableDuration())) {
+    case PrefetchContainer::ServableState::kServable:
+      // Proceed.
+      break;
+    case PrefetchContainer::ServableState::kShouldBlockUntilHeadReceived:
+      NOTREACHED_NORETURN();
+    case PrefetchContainer::ServableState::kNotServable:
+      MaybeUnblockForUnmatch(prefetch_container.GetPrefetchContainerKey());
+      return;
+  }
+
+  // Non-redirect header is received and now the value of
+  // `PrefetchContainer::IsNoVarySearchHeaderMatch()` is determined.
+  const bool is_match =
+      prefetch_container.IsExactMatch(navigated_key_.prefetch_url()) ||
+      prefetch_container.IsNoVarySearchHeaderMatch(
+          navigated_key_.prefetch_url());
+  if (!is_match) {
+    MaybeUnblockForUnmatch(prefetch_container.GetPrefetchContainerKey());
+    return;
+  }
+
+  if (prefetch_container.HasPrefetchBeenConsideredToServe()) {
+    MaybeUnblockForUnmatch(prefetch_container.GetPrefetchContainerKey());
+    return;
+  }
+
+  // Got matching and servable.
+  UnblockForMatch(prefetch_container.GetPrefetchContainerKey());
+}
+
+void PrefetchMatchResolver2::OnTimeout(PrefetchContainer::Key prefetch_key) {
+  // `timeout_timer` is alive, which implies `candidate` is alive.
+  CHECK(candidates_.contains(prefetch_key));
+  auto& candidate_data = candidates_[prefetch_key];
+  CHECK(candidate_data->prefetch_container);
+
+  MaybeUnblockForUnmatch(prefetch_key);
+}
+
+void PrefetchMatchResolver2::UnblockForMatch(
+    const PrefetchContainer::Key& prefetch_key) {
+  // By #prefetch-key-availability
+  CHECK(candidates_.contains(prefetch_key));
+  auto& candidate_data = candidates_[prefetch_key];
+  CHECK(candidate_data->prefetch_container);
+  PrefetchContainer& prefetch_container = *candidate_data->prefetch_container;
+
+  UnregisterCandidate(prefetch_key, /*is_served=*/true);
+
+  // Unregister remaining candidates as not served.
+  for (auto& key2 : Keys(candidates_)) {
+    UnregisterCandidate(key2, /*is_served=*/false);
+  }
+
+  // Postprocess for success case.
+
+  PrefetchContainer::Reader reader = prefetch_container.CreateReader();
+
+  // Cookie change is handled in two paths:
+  //
+  // - Before waiting `PrefetchContainer`, which is `kServable` at the match
+  //   start timing. It is handled in `FindPrefetchInternal()`.
+  // - After waiting `PrefetchContainer`, which is `kShouldBlockUntilHead` at
+  //   the match start timing. It is handled in `OnDeterminedHead()`.
+  //
+  // So, the below condition is satisfied.
+  CHECK(!reader.HaveDefaultContextCookiesChanged());
+
+  if (!reader.HasIsolatedCookieCopyStarted()) {
+    // Basically, we can assume `PrefetchService` is available as waiting
+    // `PrefetchContainer` is owned by it. But in unit tests, we use invalid
+    // frame tree node id and this `prefetch_service` is not available.
+    if (prefetch_service_) {
+      prefetch_service_->CopyIsolatedCookies(reader);
+    }
+  }
+  CHECK(reader);
+
+  UnblockInternal(std::move(reader));
+}
+
+void PrefetchMatchResolver2::UnblockForNoCandidates() {
+  UnblockInternal({});
+}
+
+void PrefetchMatchResolver2::MaybeUnblockForUnmatch(
+    const PrefetchContainer::Key& prefetch_key) {
+  UnregisterCandidate(prefetch_key, /*is_served=*/false);
+
+  if (candidates_.size() == 0) {
+    UnblockForNoCandidates();
+  }
+
+  // It still waits for other `PrefetchContainer`s.
+}
+
+void PrefetchMatchResolver2::UnblockForCookiesChanged() {
+  // Unregister remaining candidates as not served, with calling
+  // `PrefetchContainer::OnDetectedCookiesChange2()`.
+  for (auto& prefetch_key : Keys(candidates_)) {
+    // By #prefetch-key-availability
+    CHECK(candidates_.contains(prefetch_key));
+    auto& candidate_data = candidates_[prefetch_key];
+    CHECK(candidate_data->prefetch_container);
+    PrefetchContainer& prefetch_container = *candidate_data->prefetch_container;
+
+    UnregisterCandidate(prefetch_key, /*is_served=*/false);
+
+    prefetch_container.OnDetectedCookiesChange2();
+  }
+
+  UnblockForNoCandidates();
+}
+
+void PrefetchMatchResolver2::UnblockInternal(PrefetchContainer::Reader reader) {
+  // Postcondition: This resolver waits for no `PrefetchContainer`s when it has
+  // been unblocking.
+  CHECK_EQ(candidates_.size(), 0u);
+
+  auto callback = std::move(callback_);
+
+  base::SequencedTaskRunner::GetCurrentDefault()->DeleteSoon(FROM_HERE,
+                                                             std::move(self_));
+
+  std::move(callback).Run(std::move(reader));
+}
+
 }  // namespace content
diff --git a/content/browser/preloading/prefetch/prefetch_match_resolver.h b/content/browser/preloading/prefetch/prefetch_match_resolver.h
index 69ba25a9..421fe07 100644
--- a/content/browser/preloading/prefetch/prefetch_match_resolver.h
+++ b/content/browser/preloading/prefetch/prefetch_match_resolver.h
@@ -85,6 +85,154 @@
   NAVIGATION_HANDLE_USER_DATA_KEY_DECL();
 };
 
+// Manages matching process of prefetch
+// https://wicg.github.io/nav-speculation/prefetch.html#wait-for-a-matching-prefetch-record
+//
+// This class is created per call of
+// `PrefetchURLLoaderInterceptor::MaybeCreateLoader()` except redirects for
+// already matched prefetch and still servable ones, i.e. a prefetch was matched
+// by prior call of `PrefetchMatchResolver2::FindPrefetch()`.
+//
+// Lifetime of this class is from the call of `FindPrefetch()` to calling
+// `callback_`. This is owned by itself. See the comment on `self_`.
+//
+// Note about "2": This is the new implementation of the matching process
+// of prefetch that is used when `UseNewWaitLoop()` returns true. The old
+// implementation is `PrefetchMatchResolver`, so this is named "2".
+// Differences are, for example:
+//
+// - `PrefetchMatchResolver2` has strict precondition/postcondition
+//   e.g. `CHECK_EQ(candidates_.size(), 0u);` when the matching process
+//   starts/ends.
+// - `PrefetchMatchResolver` is `NavigationHandleUserData`
+//   and can be used multiple times for redirects, while
+//   `PrefetchMatchResolver2` forbids it in architecture level.
+//
+// That's the reason why we decided to implement the separate class.
+//
+// TODO(crbug.com/353490734): Remove the above `Note about "2"`.
+class CONTENT_EXPORT PrefetchMatchResolver2 final
+    : public PrefetchContainer::Observer {
+ public:
+  using Callback = base::OnceCallback<void(PrefetchContainer::Reader reader)>;
+
+  ~PrefetchMatchResolver2() override;
+
+  // Not movable nor copyable.
+  PrefetchMatchResolver2(PrefetchMatchResolver2&& other) = delete;
+  PrefetchMatchResolver2& operator=(PrefetchMatchResolver2&& other) = delete;
+  PrefetchMatchResolver2(const PrefetchMatchResolver2&) = delete;
+  PrefetchMatchResolver2& operator=(const PrefetchMatchResolver2&) = delete;
+
+  // PrefetchContainer::Observer implementation
+  void OnWillBeDestroyed(PrefetchContainer& prefetch_container) override;
+  void OnDeterminedHead(PrefetchContainer& prefetch_container) override;
+
+  // Finds prefetch that matches to a navigation and is servable.
+  //
+  // Corresponds to
+  // https://wicg.github.io/nav-speculation/prefetch.html#wait-for-a-matching-prefetch-record
+  //
+  // This method is async. `callback` will be called when it is done.
+  // `bool(reader)` is true iff a matching servable prefetch is found.
+  static void FindPrefetch(PrefetchContainer::Key navigated_key,
+                           PrefetchService& prefetch_service,
+                           base::WeakPtr<PrefetchServingPageMetricsContainer>
+                               serving_page_metrics_container,
+                           Callback callback);
+
+ private:
+  struct CandidateData final {
+    CandidateData();
+    ~CandidateData();
+
+    base::WeakPtr<PrefetchContainer> prefetch_container;
+    // `PrefetchContainer::GetServableState()` depends on
+    // `base::TimeTicks::now()` and can expires (can become `kServable` to
+    // `kNotServable`) in the minute between two calls. Deciding something with
+    // multiple `PrefetchContainer::GetServableState()` calls can lead
+    // inconsistent state. To avoid that, we record `ServableState` at the
+    // beginning of `FindPrefethInternal()` and refer to it in the method.
+    //
+    // One can use this field only during `FindPrefethInternal()`.
+    PrefetchContainer::ServableState cached_servable_state;
+    std::unique_ptr<base::OneShotTimer> timeout_timer;
+  };
+
+  explicit PrefetchMatchResolver2(PrefetchContainer::Key navigated_key,
+                                  base::WeakPtr<PrefetchService>,
+                                  Callback callback);
+
+  // Returns blocked duration. Returns null iff it's not blocked yet.
+  std::optional<base::TimeDelta> GetBlockedDuration() const;
+
+  // Helpers of `FindPrefetch()`.
+  //
+  // Control flow starts with `FindPrefetchInternal()` and ends with
+  // `UnblockInternal()`.
+  //
+  // Actually, it is different from
+  // https://wicg.github.io/nav-speculation/prefetch.html#wait-for-a-matching-prefetch-record
+  // Major ones:
+  //
+  // - This implementation has timeout: `CandidateData::timeout_timer`.
+  // - This implementation collects candidate prefetches first. So, it doesn't
+  //   handle prefetches started after this method started.
+  void FindPrefetchInternal(PrefetchService& prefetch_service,
+                            base::WeakPtr<PrefetchServingPageMetricsContainer>
+                                serving_page_metrics_container);
+  // Each candidate `PrefetchContainer` proceeds to
+  //
+  //    `RegisterCandidate()` (required)
+  // -> `StartWaitFor()` (optional, if servable state is
+  //    `kShouldBlockUntilHead`)
+  // -> `UnregisterCandidate()` (required)
+  void RegisterCandidate(PrefetchContainer& prefetch_container,
+                         PrefetchContainer::ServableState servable_state);
+  // `StartWaitFor()` should be called only from `FindPrefetchInternal()`
+  // (because it uses `CandidateData::cached_servable_state`).
+  void StartWaitFor(const PrefetchContainer::Key& prefetch_key);
+  void UnregisterCandidate(const PrefetchContainer::Key& prefetch_key,
+                           bool is_served);
+  void OnTimeout(PrefetchContainer::Key prefetch_key);
+  void UnblockForMatch(const PrefetchContainer::Key& prefetch_key);
+  void UnblockForNoCandidates();
+  // Unregisters unmatched prefetch and unblocks if there are no other waiting
+  // prefetches.
+  void MaybeUnblockForUnmatch(const PrefetchContainer::Key& prefetch_key);
+  void UnblockForCookiesChanged();
+  void UnblockInternal(PrefetchContainer::Reader reader);
+
+  // Lifetime of this class is from the call of `FindPrefetch()` to calling
+  // `callback_`. Note that
+  //
+  // - `FindPrefetchInternal()` consumes this class. We don't want to use this
+  //   class twice.
+  // - `NavigationLoaderInterceptor::MaybeCreateLoader()` can be called multiple
+  //   times, e.g. redirect.
+  //
+  // So, we don't believe that `NavigationHandleUserData` is an appropriate
+  // choice to manage lifetime. Possible choices are:
+  //
+  // A. This way.
+  // B. Have another class that inherits `NavigationHandleUserData` and manages
+  //    this class for each `NavigationLoaderInterceptor::MaybeCreateLoader()`
+  //    call.
+  //
+  // Note that `NavigationLoaderInterceptor::MaybeCreateLoader()` requires that
+  // `callback_` is eventually called. So, we don't need to care about memory
+  // leak.
+  //
+  // A would be enough.
+  std::unique_ptr<PrefetchMatchResolver2> self_;
+
+  const PrefetchContainer::Key navigated_key_;
+  base::WeakPtr<PrefetchService> prefetch_service_;
+  Callback callback_;
+  std::map<PrefetchContainer::Key, std::unique_ptr<CandidateData>> candidates_;
+  std::optional<base::TimeTicks> wait_started_at_ = std::nullopt;
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_MATCH_RESOLVER_H_
diff --git a/content/browser/preloading/prefetch/prefetch_service.cc b/content/browser/preloading/prefetch/prefetch_service.cc
index 6bf7583..3a98bf2 100644
--- a/content/browser/preloading/prefetch/prefetch_service.cc
+++ b/content/browser/preloading/prefetch/prefetch_service.cc
@@ -1150,7 +1150,10 @@
                      base::Unretained(this), prefetch_container),
       base::BindRepeating(&PrefetchService::OnPrefetchRedirect,
                           base::Unretained(this), prefetch_container),
-      base::BindOnce(&PrefetchContainer::OnDeterminedHead, prefetch_container),
+      UseNewWaitLoop() ? base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
+                                        prefetch_container)
+                       : base::BindOnce(&PrefetchContainer::OnDeterminedHead,
+                                        prefetch_container),
       prefetch_container->GetResponseReaderForCurrentPrefetch());
   prefetch_container->SetStreamingURLLoader(std::move(streaming_loader));
 
@@ -1574,6 +1577,8 @@
     base::WeakPtr<PrefetchServingPageMetricsContainer>
         serving_page_metrics_container,
     PrefetchMatchResolver& prefetch_match_resolver) {
+  CHECK(!UseNewWaitLoop());
+
   DumpPrefetchesForDebug();
   auto potential_matching_prefetches =
       CollectPotentiallyMatchingPrefetchContainers(
diff --git a/content/browser/preloading/prefetch/prefetch_service.h b/content/browser/preloading/prefetch/prefetch_service.h
index 52f2ecf..3dcf317 100644
--- a/content/browser/preloading/prefetch/prefetch_service.h
+++ b/content/browser/preloading/prefetch/prefetch_service.h
@@ -149,6 +149,14 @@
   GetAllForUrlWithoutRefAndQueryForTesting(
       const PrefetchContainer::Key& key) const;
 
+  // Helper function for |GetPrefetchToServe|, which collects
+  // |PrefetchContainer|s that are potentially matching. Corresponds to 3.4. of
+  // https://wicg.github.io/nav-speculation/prefetch.html#wait-for-a-matching-prefetch-record
+  std::vector<PrefetchContainer*> CollectPotentiallyMatchingPrefetchContainers(
+      const PrefetchContainer::Key& key,
+      base::WeakPtr<PrefetchServingPageMetricsContainer>
+          serving_page_metrics_container);
+
   base::WeakPtr<PrefetchService> GetWeakPtr();
 
  private:
@@ -342,14 +350,6 @@
       base::WeakPtr<PrefetchMatchResolver> prefetch_match_resolver,
       PrefetchContainer& prefetch_container);
 
-  // Helper function for |GetPrefetchToServe|, which collects
-  // |PrefetchContainer|s that are potentially matching. Corresponds to 3.4. of
-  // https://wicg.github.io/nav-speculation/prefetch.html#wait-for-a-matching-prefetch-record
-  std::vector<PrefetchContainer*> CollectPotentiallyMatchingPrefetchContainers(
-      const PrefetchContainer::Key& key,
-      base::WeakPtr<PrefetchServingPageMetricsContainer>
-          serving_page_metrics_container);
-
   // Helper function for |GetPrefetchToServe| which handles a
   // |prefetch_container| that could potentially be served to the navigation.
   HandlePrefetchContainerResult HandlePrefetchContainerToServe(
diff --git a/content/browser/preloading/prefetch/prefetch_service_unittest.cc b/content/browser/preloading/prefetch/prefetch_service_unittest.cc
index aef9ca7a..a2d80f3 100644
--- a/content/browser/preloading/prefetch/prefetch_service_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_service_unittest.cc
@@ -33,6 +33,7 @@
 #include "content/browser/preloading/preloading_attempt_impl.h"
 #include "content/browser/preloading/preloading_config.h"
 #include "content/browser/preloading/preloading_data_impl.h"
+#include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/common/features.h"
 #include "content/public/browser/browser_context.h"
@@ -237,6 +238,11 @@
   const base::raw_ref<BrowserTaskEnvironment> task_environment_;
 };
 
+struct NavigationResult {
+  std::unique_ptr<testing::NiceMock<MockNavigationHandle>> navigation_handle;
+  base::test::TestFuture<PrefetchContainer::Reader> reader_future;
+};
+
 class PrefetchServiceTest : public RenderViewHostTestHarness {
  public:
   const int kServiceWorkerCheckDuration = 1000;
@@ -675,9 +681,7 @@
       base::test::TestFuture<PrefetchContainer::Reader>& future,
       const GURL& url,
       std::optional<blink::DocumentToken> initiator_document_token) {
-    PrefetchMatchResolver* prefetch_match_resolver =
-        GetPrefetchMatchResolverForMostRecentNavigation();
-    prefetch_match_resolver->SetOnPrefetchToServeReadyCallback(base::BindOnce(
+    auto callback = base::BindOnce(
         [](base::test::TestFuture<PrefetchContainer::Reader>* future,
            std::vector<PrefetchRequestHandler>* request_handler_keep_alive,
            PrefetchContainer::Reader prefetch_to_serve) {
@@ -696,11 +700,23 @@
           future->SetValue(std::move(prefetch_to_serve));
         },
         base::Unretained(&future),
-        base::Unretained(&request_handler_keep_alive_)));
-    prefetch_service_->GetPrefetchToServe(
-        PrefetchContainer::Key(initiator_document_token, url),
-        GetServingPageMetricsContainerForMostRecentNavigation(),
-        *prefetch_match_resolver);
+        base::Unretained(&request_handler_keep_alive_));
+    if (UseNewWaitLoop()) {
+      auto key = PrefetchContainer::Key(initiator_document_token, url);
+      PrefetchMatchResolver2::FindPrefetch(
+          std::move(key), *prefetch_service_.get(),
+          GetServingPageMetricsContainerForMostRecentNavigation(),
+          std::move(callback));
+    } else {
+      PrefetchMatchResolver* prefetch_match_resolver =
+          GetPrefetchMatchResolverForMostRecentNavigation();
+      prefetch_match_resolver->SetOnPrefetchToServeReadyCallback(
+          std::move(callback));
+      prefetch_service_->GetPrefetchToServe(
+          PrefetchContainer::Key(initiator_document_token, url),
+          GetServingPageMetricsContainerForMostRecentNavigation(),
+          *prefetch_match_resolver);
+    }
   }
 
   PrefetchContainer::Reader GetPrefetchToServe(
@@ -715,6 +731,78 @@
     return GetPrefetchToServe(url, MainDocumentToken());
   }
 
+  // Simulates part of navigation.
+  //
+  // - Creates MockNavigationHandle.
+  // - Starts matching of prefetch `PrefetchMatchResolver2::FindPrefetch()`.
+  //
+  // Note that these are very small part of navigation. For example,
+  // post-matching process `OnGotPrefetchToServe()` and redirects are not
+  // handled.
+  std::unique_ptr<NavigationResult> SimulatePartOfNavigation(
+      const GURL& url,
+      bool is_renderer_initiated) {
+    return is_renderer_initiated
+               ? SimulatePartOfNavigation(
+                     url, main_rfh()->GetProcess()->GetID(),
+                     main_rfh()->GetFrameToken(), MainDocumentToken())
+               : SimulatePartOfNavigation(url,
+                                          ChildProcessHost::kInvalidUniqueID,
+                                          std::nullopt, std::nullopt);
+  }
+
+  std::unique_ptr<NavigationResult> SimulatePartOfNavigation(
+      const GURL& url,
+      int initiator_process_id,
+      const std::optional<blink::LocalFrameToken>& initiator_local_frame_token,
+      const std::optional<blink::DocumentToken>& initiator_document_token) {
+    CHECK(UseNewWaitLoop());
+
+    // Use std::unique_ptr as the below uses raw pointers and references.
+    auto res = std::make_unique<NavigationResult>();
+
+    res->navigation_handle =
+        std::make_unique<testing::NiceMock<MockNavigationHandle>>(
+            web_contents());
+    res->navigation_handle->set_url(url);
+    res->navigation_handle->set_initiator_process_id(initiator_process_id);
+    res->navigation_handle->set_initiator_frame_token(
+        base::OptionalToPtr(initiator_local_frame_token));
+
+    // Simulate how `NavigationRequest` calls
+    // `PrefetchServingPageMetricsContainer::GetOrCreateForNavigationHandle()`.
+    if (initiator_document_token &&
+        PrefetchDocumentManager::FromDocumentToken(initiator_process_id,
+                                                   *initiator_document_token)) {
+      PrefetchServingPageMetricsContainer::GetOrCreateForNavigationHandle(
+          *res->navigation_handle);
+    }
+
+    auto callback = base::BindOnce(
+        [](base::test::TestFuture<PrefetchContainer::Reader>& reader_future,
+           PrefetchContainer::Reader reader) {
+          reader_future.SetValue(std::move(reader));
+        },
+        std::ref(res->reader_future));
+    auto serving_page_metrics_container =
+        [&res]() -> base::WeakPtr<PrefetchServingPageMetricsContainer> {
+      auto* serving_page_metrics_container =
+          PrefetchServingPageMetricsContainer::GetForNavigationHandle(
+              *res->navigation_handle);
+      if (!serving_page_metrics_container) {
+        return nullptr;
+      }
+
+      return serving_page_metrics_container->GetWeakPtr();
+    }();
+    auto key = PrefetchContainer::Key(initiator_document_token, url);
+    PrefetchMatchResolver2::FindPrefetch(
+        std::move(key), *prefetch_service_.get(),
+        std::move(serving_page_metrics_container), std::move(callback));
+
+    return res;
+  }
+
   ScopedPrefetchServiceContentBrowserClient* test_content_browser_client() {
     return test_content_browser_client_.get();
   }
@@ -933,25 +1021,39 @@
   }
 
   // ##### Helpers for serving-related metrics #####
-  void ExpectServingMetrics(PrefetchStatus expected_prefetch_status,
-                            bool prefetch_header_latency = false,
-                            bool required_private_prefetch_proxy = true) {
-    std::optional<PrefetchServingPageMetrics> serving_page_metrics =
-        GetMetricsForMostRecentNavigation();
+  static void ExpectServingMetrics(
+      const base::Location& location,
+      std::optional<PrefetchServingPageMetrics> serving_page_metrics,
+      PrefetchStatus expected_prefetch_status,
+      std::optional<base::TimeDelta> prefetch_header_latency,
+      bool required_private_prefetch_proxy) {
+    SCOPED_TRACE(::testing::Message() << "callsite: " << location.ToString());
+
     ASSERT_TRUE(serving_page_metrics);
     ASSERT_TRUE(serving_page_metrics->prefetch_status);
     EXPECT_EQ(serving_page_metrics->prefetch_status.value(),
               static_cast<int>(expected_prefetch_status));
+    EXPECT_EQ(serving_page_metrics->prefetch_header_latency,
+              prefetch_header_latency);
     EXPECT_EQ(serving_page_metrics->required_private_prefetch_proxy,
               required_private_prefetch_proxy);
     EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab);
+  }
+
+  void ExpectServingMetrics(PrefetchStatus expected_prefetch_status,
+                            bool prefetch_header_latency = false,
+                            bool required_private_prefetch_proxy = true) {
+    // std::optional<base::TimeDelta> prefetch_header_latency_value =
+    // prefetch_header_latency ? base::Milliseconds(kHeaderLatency) :
+    // std::nullopt;
+    std::optional<base::TimeDelta> prefetch_header_latency_value;
     if (prefetch_header_latency) {
-      ASSERT_TRUE(serving_page_metrics->prefetch_header_latency);
-      EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(),
-                base::Milliseconds(kHeaderLatency));
-    } else {
-      EXPECT_FALSE(serving_page_metrics->prefetch_header_latency);
+      prefetch_header_latency_value = base::Milliseconds(kHeaderLatency);
     }
+    ExpectServingMetrics(FROM_HERE, GetMetricsForMostRecentNavigation(),
+                         expected_prefetch_status,
+                         std::move(prefetch_header_latency_value),
+                         required_private_prefetch_proxy);
   }
 
   void ExpectServingMetricsSuccess(
@@ -961,8 +1063,32 @@
                          required_private_prefetch_proxy);
   }
 
+  struct ExpectServingMetricsArgs {
+    PrefetchStatus prefetch_status;
+    std::optional<base::TimeDelta> prefetch_header_latency;
+    bool required_private_prefetch_proxy;
+  };
+  static void ExpectServingMetrics(
+      const base::Location& location,
+      const std::unique_ptr<NavigationResult>& nav_res,
+      ExpectServingMetricsArgs args) {
+    ExpectServingMetrics(location,
+                         PrefetchServingPageMetrics::GetForNavigationHandle(
+                             *nav_res->navigation_handle),
+                         args.prefetch_status, args.prefetch_header_latency,
+                         args.required_private_prefetch_proxy);
+  }
+
   static void ExpectServingReaderSuccess(
       const PrefetchContainer::Reader& serveable_reader) {
+    ExpectServingReaderSuccess(FROM_HERE, serveable_reader);
+  }
+
+  static void ExpectServingReaderSuccess(
+      const base::Location& location,
+      const PrefetchContainer::Reader& serveable_reader) {
+    SCOPED_TRACE(::testing::Message() << "callsite: " << location.ToString());
+
     ASSERT_TRUE(serveable_reader);
     EXPECT_TRUE(serveable_reader.HasPrefetchStatus());
     EXPECT_EQ(serveable_reader.GetPrefetchStatus(),
@@ -1136,48 +1262,35 @@
 
   NavigateInitiatedByRenderer(GURL("https://example.com"));
 
+  // No servable PrefetchContainer is returned for different DocumentToken.
+  blink::DocumentToken different_document_token;
+  PrefetchContainer::Reader serveable_reader_for_different_initiator =
+      GetPrefetchToServe(GURL("https://example.com"), different_document_token);
+  EXPECT_FALSE(serveable_reader_for_different_initiator);
+
   ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
   ExpectServingMetricsSuccess();
 
   histogram_tester.ExpectUniqueSample(
       "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
 
-  histogram_tester.ExpectUniqueSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s",
-          GetPrefetchEagernessHistogramSuffix(
-              blink::mojom::SpeculationEagerness::kEager)
-              .c_str()),
-      false, 1);
-}
-
-TEST_F(PrefetchServiceTest, PrefetchDoesNotMatchIfDocumentTokenDoesNotMatch) {
-  base::HistogramTester histogram_tester;
-
-  MakePrefetchService(
-      std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
-
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
-      PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true,
-                   blink::mojom::SpeculationEagerness::kEager));
-  task_environment()->RunUntilIdle();
-
-  VerifyCommonRequestState(GURL("https://example.com"),
-                           {.use_prefetch_proxy = true});
-  MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
-                      /*use_prefetch_proxy=*/true,
-                      {{"X-Testing", "Hello World"}}, kHTMLBody);
-
-  ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
-
-  NavigateInitiatedByRenderer(GURL("https://example.com"));
-
-  // No servable PrefetchContainer is returned for different DocumentToken.
-  blink::DocumentToken different_document_token;
-  EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com"),
-                                  different_document_token));
+  if (UseNewWaitLoop()) {
+    histogram_tester.ExpectUniqueSample(
+        base::StringPrintf("PrefetchProxy.AfterClick."
+                           "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
+                           GetPrefetchEagernessHistogramSuffix(
+                               blink::mojom::SpeculationEagerness::kEager)
+                               .c_str()),
+        false, 1);
+  } else {
+    histogram_tester.ExpectUniqueSample(
+        base::StringPrintf(
+            "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s",
+            GetPrefetchEagernessHistogramSuffix(
+                blink::mojom::SpeculationEagerness::kEager)
+                .c_str()),
+        false, 1);
+  }
 }
 
 TEST_F(PrefetchServiceTest, SuccessCase_Embedder) {
@@ -1218,6 +1331,10 @@
 
   NavigateInitiatedByBrowser(GURL("https://example.com"));
 
+  // No servable PrefetchContainer is returned for different DocumentToken.
+  EXPECT_FALSE(
+      GetPrefetchToServe(GURL("https://example.com"), MainDocumentToken()));
+
   ExpectServingReaderSuccess(
       GetPrefetchToServe(GURL("https://example.com"), std::nullopt));
 
@@ -1225,50 +1342,6 @@
       "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
 }
 
-TEST_F(PrefetchServiceTest,
-       PrefetchDoesNotMatchIfDocumentTokenDoesNotMatch_Embedder) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      features::kPrefetchBrowserInitiatedTriggers);
-
-  base::HistogramTester histogram_tester;
-  MakePrefetchService(
-      std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
-          /*num_on_prefetch_likely_calls=*/std::nullopt));
-
-  MakePrefetchFromEmbedder(GURL("https://example.com"),
-                           PrefetchType(PreloadingTriggerType::kEmbedder,
-                                        /*use_prefetch_proxy=*/true));
-  task_environment()->RunUntilIdle();
-
-  VerifyCommonRequestState(GURL("https://example.com"),
-                           {.use_prefetch_proxy = true});
-  MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
-                      /*use_prefetch_proxy=*/true,
-                      {{"X-Testing", "Hello World"}}, kHTMLBody);
-
-  // Verify that the prefetch request was successful.
-  // TODO(crbug.com/40269462): Revise current helper functions (ExpectPrefetch*)
-  // for browser-initiated prefetch.
-  histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1);
-  histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
-
-  NavigateInitiatedByBrowser(GURL("https://example.com"));
-
-  // No servable PrefetchContainer is returned for different DocumentToken.
-  EXPECT_FALSE(
-      GetPrefetchToServe(GURL("https://example.com"), MainDocumentToken()));
-}
-
 TEST_F(PrefetchServiceTest, NoPrefetchingPreloadingDisabled) {
   base::HistogramTester histogram_tester;
 
@@ -3374,7 +3447,7 @@
            {"block_until_head_eager_prefetch", "true"},
            {"block_until_head_moderate_prefetch", "true"},
            {"block_until_head_conservative_prefetch", "true"}}}},
-        {});
+        {features::kPrefetchNewWaitLoop});
   }
 };
 
@@ -3588,7 +3661,7 @@
               {"block_until_head_timeout_moderate_prefetch", "1000"},
               {"block_until_head_timeout_conservative_prefetch", "1000"},
           }}},
-        {});
+        {features::kPrefetchNewWaitLoop});
   }
 };
 
@@ -4259,16 +4332,34 @@
 
   std::string histogram_suffix =
       GetPrefetchEagernessHistogramSuffix(GetParam());
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s",
-          histogram_suffix.c_str()),
-      base::Milliseconds(kHeaderLatency), 0);
-  histogram_tester.ExpectUniqueSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s",
-          histogram_suffix.c_str()),
-      true, 1);
+  if (UseNewWaitLoop()) {
+    histogram_tester.ExpectUniqueTimeSample(
+        base::StringPrintf(
+            "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
+            histogram_suffix.c_str()),
+        base::Milliseconds(kHeaderLatency), 1);
+    histogram_tester.ExpectUniqueTimeSample(
+        base::StringPrintf(
+            "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+            histogram_suffix.c_str()),
+        base::Milliseconds(kHeaderLatency), 1);
+    histogram_tester.ExpectUniqueSample(
+        base::StringPrintf("PrefetchProxy.AfterClick."
+                           "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
+                           histogram_suffix.c_str()),
+        true, 2);
+  } else {
+    histogram_tester.ExpectUniqueTimeSample(
+        base::StringPrintf(
+            "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s",
+            histogram_suffix.c_str()),
+        base::Milliseconds(kHeaderLatency), 0);
+    histogram_tester.ExpectUniqueSample(
+        base::StringPrintf(
+            "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s",
+            histogram_suffix.c_str()),
+        true, 1);
+  }
 }
 
 // TODO(crbug.com/40249481): Test flaky on lacros trybots.
@@ -4287,6 +4378,7 @@
 
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(2));
+
   MakePrefetchOnMainFrame(
       GURL(kTestUrl), PrefetchType(PreloadingTriggerType::kSpeculationRule,
                                    /*use_prefetch_proxy=*/false, GetParam()));
@@ -4297,6 +4389,7 @@
   MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
                       /*use_prefetch_proxy=*/false,
                       {{"X-Testing", "Hello World"}}, kHTMLBody);
+
   {
     network::mojom::NoVarySearchPtr no_vary_search_hint =
         network::mojom::NoVarySearch::New();
@@ -4344,9 +4437,57 @@
   // candidates are not eligible serveable_reader will be falsy.
   EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com/index.html")));
 
-  ExpectServingMetrics(PrefetchStatus::kPrefetchNotUsedCookiesChanged,
-                       /*prefetch_header_latency=*/true,
-                       /*required_private_prefetch_proxy=*/false);
+  // Behavioral change.
+  //
+  // kenoss@ believes that `PrefetchServingPageMetrics` is broken (at least non
+  // reliable state).
+  //
+  // In this test, `PrefetchContainer::UpdateServingPageMetrics()` is called
+  // four times: (For simplicity of description, we assumet that
+  // `kPrefetchNewWaitLoop`.)
+  //
+  // - Prefetch for https://example.com/index.html, path is
+  //      PrefetchMatchResolver2::FindPrefetchInternal()
+  //   -> PrefetchService::CollectPotentiallyMatchingPrefetchContainers()
+  //   -> PrefetchContainer::UpdateServingPageMetrics()
+  //   value is 0.456 s.
+  // - Prefetch for https://example.com/index.html?a=1, path is
+  //      PrefetchMatchResolver2::FindPrefetchInternal()
+  //   -> PrefetchService::CollectPotentiallyMatchingPrefetchContainers()
+  //   -> PrefetchContainer::UpdateServingPageMetrics()
+  //   value is std::nullopt.
+  // - Prefetch for https://example.com/index.html, path is
+  //      PrefetchMatchResolver2::FindPrefetchInternal()
+  //   -> PrefetchMatchResolver2::UnblockForCookiesChanged()
+  //   -> PrefetchContainer::OnDetectedCookiesChange2()
+  //   -> PrefetchContainer::OnDetectedCookiesChange()
+  //   -> PrefetchContainer::UpdateServingPageMetrics()
+  //   value is 0.456 s.
+  // - Prefetch for https://example.com/index.html?a=1, path is
+  //      PrefetchMatchResolver2::FindPrefetchInternal()
+  //   -> PrefetchMatchResolver2::UnblockForCookiesChanged()
+  //   -> PrefetchContainer::OnDetectedCookiesChange2()
+  //   -> PrefetchContainer::OnDetectedCookiesChange()
+  //   -> PrefetchContainer::UpdateServingPageMetrics()
+  //   value is std::nullopt.
+  //
+  // If `kPrefetchNewWaitLoop` is enabled, the calling order of the third and
+  // the fourth is unstable because
+  // `PrefetchMatchResolver2::UnblockForCookiesChanged()` uses an iteration of
+  // `std::map` and those prefetch refer the same
+  // `PrefetchServingPageMetricsContanier`. The essential problem is 1. they
+  // share the same `PrefetchServingPageMetricsContanier`. 2. There is no
+  // coordination.
+  //
+  // I don't think this metrics is worth to preserve until the root cause is
+  // fixed.
+  //
+  // TODO(crbug.com/360094997): Reconstruct `PrefetchServingPageMetrics`.
+  if (!UseNewWaitLoop()) {
+    ExpectServingMetrics(PrefetchStatus::kPrefetchNotUsedCookiesChanged,
+                         /*prefetch_header_latency=*/true,
+                         /*required_private_prefetch_proxy=*/false);
+  }
 }
 
 // TODO(crbug.com/40249481): Test flaky on lacros trybots.
@@ -4591,7 +4732,7 @@
               {"block_until_head_timeout_moderate_prefetch", "1000"},
               {"block_until_head_timeout_conservative_prefetch", "1000"},
           }}},
-        {});
+        {features::kPrefetchNewWaitLoop});
   }
 };
 
@@ -5786,5 +5927,275 @@
   EXPECT_FALSE(future.Get());
 }
 
+// With new wait loop, multiple concurrent navigations are handled correctly.
+// TODO(crbug.com/353490734): Remove the above sentence.
+//
+// Scenario:
+//
+// - Prefetch A started.
+// - A received non-redirect header.
+// - Navigation X started, which matches to A. Unblocked synchronously as
+//   success.
+// - Navigation Y started, which matches to A. Unblocked synchronously as
+//   success.
+TEST_F(
+    PrefetchServiceTest,
+    DISABLED_CHROMEOS(MultipleConcurrentNavigationSuccessBeforeNavigations)) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      {features::kPrefetchNewWaitLoop, features::kPrefetchReusable}, {});
+
+  base::HistogramTester histogram_tester;
+
+  MakePrefetchService(
+      std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
+
+  MakePrefetchOnMainFrame(
+      GURL("https://example.com"),
+      PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                   /*use_prefetch_proxy=*/true,
+                   blink::mojom::SpeculationEagerness::kEager));
+  task_environment()->RunUntilIdle();
+
+  VerifyCommonRequestState(GURL("https://example.com"),
+                           {.use_prefetch_proxy = true});
+  MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
+                      /*use_prefetch_proxy=*/true,
+                      {{"X-Testing", "Hello World"}}, kHTMLBody);
+
+  ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
+
+  std::unique_ptr<NavigationResult> nav_res1 = SimulatePartOfNavigation(
+      GURL("https://example.com"), /*is_renderer_initiated=*/true);
+  std::unique_ptr<NavigationResult> nav_res2 = SimulatePartOfNavigation(
+      GURL("https://example.com"), /*is_renderer_initiated=*/true);
+  task_environment()->RunUntilIdle();
+
+  ExpectServingReaderSuccess(FROM_HERE, nav_res1->reader_future.Take());
+  ExpectServingMetrics(
+      FROM_HERE, nav_res1,
+      {.prefetch_status = PrefetchStatus::kPrefetchSuccessful,
+       .prefetch_header_latency = base::Milliseconds(kHeaderLatency),
+       .required_private_prefetch_proxy = true});
+
+  ExpectServingReaderSuccess(FROM_HERE, nav_res2->reader_future.Take());
+  ExpectServingMetrics(
+      FROM_HERE, nav_res2,
+      {.prefetch_status = PrefetchStatus::kPrefetchSuccessful,
+       .prefetch_header_latency = base::Milliseconds(kHeaderLatency),
+       .required_private_prefetch_proxy = true});
+
+  histogram_tester.ExpectUniqueSample(
+      "PrefetchProxy.AfterClick.RedirectChainSize", 1, 2);
+  histogram_tester.ExpectUniqueSample(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Eager",
+      false, 2);
+}
+
+// Scenario:
+//
+// - Prefetch A started.
+// - Navigation X started, which matches to A. Blocked by A.
+// - Navigation Y started, which matches to A. Blocked by A.
+// - A received non-redirect header. Unblocks them as success.
+TEST_F(
+    PrefetchServiceTest,
+    DISABLED_CHROMEOS(MultipleConcurrentNavigationBlockUntilHeadThenSuccess)) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      {features::kPrefetchNewWaitLoop, features::kPrefetchReusable}, {});
+
+  base::HistogramTester histogram_tester;
+
+  MakePrefetchService(
+      std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
+
+  MakePrefetchOnMainFrame(
+      GURL("https://example.com"),
+      PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                   /*use_prefetch_proxy=*/true,
+                   blink::mojom::SpeculationEagerness::kEager));
+  task_environment()->RunUntilIdle();
+
+  VerifyCommonRequestState(GURL("https://example.com"),
+                           {.use_prefetch_proxy = true});
+
+  std::unique_ptr<NavigationResult> nav_res1 = SimulatePartOfNavigation(
+      GURL("https://example.com"), /*is_renderer_initiated=*/true);
+  std::unique_ptr<NavigationResult> nav_res2 = SimulatePartOfNavigation(
+      GURL("https://example.com"), /*is_renderer_initiated=*/true);
+  task_environment()->RunUntilIdle();
+
+  SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
+                            /*use_prefetch_proxy=*/true,
+                            {{"X-Testing", "Hello World"}},
+                            std::size(kHTMLBody));
+  SendBodyContentOfResponseAndWait(kHTMLBody);
+  CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
+
+  ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
+                        blink::mojom::SpeculationEagerness::kEager,
+                        /*is_accurate=*/true);
+
+  ExpectServingReaderSuccess(FROM_HERE, nav_res1->reader_future.Take());
+  // TODO(crbug.com/356540465): See the bug. Make PrefetchServingMetrics
+  // available for multiple concurrent navigations.
+  ExpectServingMetrics(
+      FROM_HERE, nav_res1,
+      {.prefetch_status = PrefetchStatus::kPrefetchNotFinishedInTime,
+       .prefetch_header_latency = std::nullopt,
+       .required_private_prefetch_proxy = true});
+
+  ExpectServingReaderSuccess(FROM_HERE, nav_res2->reader_future.Take());
+  ExpectServingMetrics(
+      FROM_HERE, nav_res2,
+      {.prefetch_status = PrefetchStatus::kPrefetchSuccessful,
+       .prefetch_header_latency = base::Milliseconds(kHeaderLatency),
+       .required_private_prefetch_proxy = true});
+
+  histogram_tester.ExpectUniqueSample(
+      "PrefetchProxy.AfterClick.RedirectChainSize", 1, 2);
+  histogram_tester.ExpectUniqueSample(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Eager",
+      true, 2);
+}
+
+// Scenario:
+//
+// - Prefetch A started with NVS hint.
+// - Navigation X started, which is potentially matches and eventually matches
+//   to A. Blocked by A.
+// - Navigation Y started, which is potentially matches and not eventually
+//   matches to A. Blocked by A.
+// - A received non-redirect header. Unblocks them as success/fail.
+TEST_F(PrefetchServiceTest,
+       DISABLED_CHROMEOS(
+           MultipleConcurrentNavigationBlockUntilHeadThenSuccessFail)) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      {features::kPrefetchNewWaitLoop, features::kPrefetchReusable}, {});
+
+  base::HistogramTester histogram_tester;
+
+  MakePrefetchService(
+      std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
+
+  network::mojom::NoVarySearchPtr no_vary_search_hint =
+      network::mojom::NoVarySearch::New();
+  no_vary_search_hint->vary_on_key_order = true;
+  no_vary_search_hint->search_variance =
+      network::mojom::SearchParamsVariance::NewNoVaryParams(
+          std::vector<std::string>({"match", "notEventuallyMatch"}));
+
+  MakePrefetchOnMainFrame(
+      GURL("https://example.com/?match=0"),
+      PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                   /*use_prefetch_proxy=*/true,
+                   blink::mojom::SpeculationEagerness::kEager),
+      /* referrer */ blink::mojom::Referrer(), std::move(no_vary_search_hint));
+  task_environment()->RunUntilIdle();
+
+  VerifyCommonRequestState(GURL("https://example.com/?match=0"),
+                           {.use_prefetch_proxy = true});
+
+  std::unique_ptr<NavigationResult> nav_res1 = SimulatePartOfNavigation(
+      GURL("https://example.com/?match=1"), /*is_renderer_initiated=*/true);
+  std::unique_ptr<NavigationResult> nav_res2 = SimulatePartOfNavigation(
+      GURL("https://example.com/?notEventuallyMatch=1"),
+      /*is_renderer_initiated=*/true);
+  task_environment()->RunUntilIdle();
+
+  SendHeadOfResponseAndWait(
+      net::HTTP_OK, kHTMLMimeType,
+      /*use_prefetch_proxy=*/true,
+      {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"match\")"}},
+      std::size(kHTMLBody));
+  SendBodyContentOfResponseAndWait(kHTMLBody);
+  CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
+
+  ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
+                        blink::mojom::SpeculationEagerness::kEager,
+                        /*is_accurate=*/true);
+
+  ExpectServingReaderSuccess(FROM_HERE, nav_res1->reader_future.Take());
+  // TODO(crbug.com/356540465): See the bug. Make PrefetchServingMetrics
+  // available for multiple concurrent navigations.
+  ExpectServingMetrics(
+      FROM_HERE, nav_res1,
+      {.prefetch_status = PrefetchStatus::kPrefetchNotFinishedInTime,
+       .prefetch_header_latency = std::nullopt,
+       .required_private_prefetch_proxy = true});
+
+  EXPECT_FALSE(nav_res2->reader_future.Take());
+
+  histogram_tester.ExpectUniqueSample(
+      "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Eager",
+      true, 2);
+}
+
+// Scenario:
+//
+// - Prefetch A started.
+// - Navigation X started, which matches to A. Blocked by A.
+// - Navigation Y started, which matches to A. Blocked by A.
+// - Cookies of domain of A changed.
+// - A received non-redirect header. Unblocks them as fail.
+//
+// This test checks that it is safe to call
+// `PrefetchContainer::OnDetectedCookiesChange2()` multiple times.
+TEST_F(PrefetchServiceTest,
+       DISABLED_CHROMEOS(
+           MultipleConcurrentNavigationBlockUntilHeadThenCookiesChanged)) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      {features::kPrefetchNewWaitLoop, features::kPrefetchReusable}, {});
+
+  base::HistogramTester histogram_tester;
+
+  MakePrefetchService(
+      std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
+
+  MakePrefetchOnMainFrame(
+      GURL("https://example.com"),
+      PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                   /*use_prefetch_proxy=*/true,
+                   blink::mojom::SpeculationEagerness::kEager));
+  task_environment()->RunUntilIdle();
+
+  VerifyCommonRequestState(GURL("https://example.com"),
+                           {.use_prefetch_proxy = true});
+
+  std::unique_ptr<NavigationResult> nav_res1 = SimulatePartOfNavigation(
+      GURL("https://example.com"), /*is_renderer_initiated=*/true);
+  std::unique_ptr<NavigationResult> nav_res2 = SimulatePartOfNavigation(
+      GURL("https://example.com"), /*is_renderer_initiated=*/true);
+  task_environment()->RunUntilIdle();
+
+  ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
+
+  SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
+                            /*use_prefetch_proxy=*/true,
+                            {{"X-Testing", "Hello World"}},
+                            std::size(kHTMLBody));
+  SendBodyContentOfResponseAndWait(kHTMLBody);
+  CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
+
+  EXPECT_FALSE(nav_res1->reader_future.Take());
+
+  EXPECT_FALSE(nav_res2->reader_future.Take());
+
+  histogram_tester.ExpectTotalCount(
+      "PrefetchProxy.AfterClick.RedirectChainSize", 0);
+  histogram_tester.ExpectUniqueSample(
+      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Eager",
+      true, 2);
+}
+
 }  // namespace
 }  // namespace content
diff --git a/content/browser/preloading/prefetch/prefetch_test_util_internal.cc b/content/browser/preloading/prefetch/prefetch_test_util_internal.cc
index afbef2a..a1964490 100644
--- a/content/browser/preloading/prefetch/prefetch_test_util_internal.cc
+++ b/content/browser/preloading/prefetch/prefetch_test_util_internal.cc
@@ -8,6 +8,7 @@
 #include "base/run_loop.h"
 #include "base/time/time.h"
 #include "content/browser/preloading/prefetch/prefetch_container.h"
+#include "content/browser/preloading/prefetch/prefetch_params.h"
 #include "content/browser/preloading/prefetch/prefetch_response_reader.h"
 #include "content/browser/preloading/prefetch/prefetch_streaming_url_loader.h"
 #include "net/cookies/site_for_cookies.h"
@@ -83,8 +84,10 @@
                              network::mojom::URLResponseHeadPtr response_head) {
         NOTREACHED_IN_MIGRATION();
       }),
-      base::BindOnce(&PrefetchContainer::OnDeterminedHead,
-                     prefetch_container->GetWeakPtr()),
+      UseNewWaitLoop() ? base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
+                                        prefetch_container->GetWeakPtr())
+                       : base::BindOnce(&PrefetchContainer::OnDeterminedHead,
+                                        prefetch_container->GetWeakPtr()),
       weak_response_reader);
 
   prefetch_container->SetStreamingURLLoader(weak_streaming_loader);
@@ -126,8 +129,10 @@
                              network::mojom::URLResponseHeadPtr response_head) {
         NOTREACHED_IN_MIGRATION();
       }),
-      base::BindOnce(&PrefetchContainer::OnDeterminedHead,
-                     prefetch_container->GetWeakPtr()),
+      UseNewWaitLoop() ? base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
+                                        prefetch_container->GetWeakPtr())
+                       : base::BindOnce(&PrefetchContainer::OnDeterminedHead,
+                                        prefetch_container->GetWeakPtr()),
       prefetch_container->GetResponseReaderForCurrentPrefetch());
 
   prefetch_container->SetStreamingURLLoader(weak_streaming_loader);
@@ -190,8 +195,10 @@
           &on_response_complete_loop),
       CreatePrefetchRedirectCallbackForTest(&on_receive_redirect_loop,
                                             &redirect_info, &redirect_head),
-      base::BindOnce(&PrefetchContainer::OnDeterminedHead,
-                     prefetch_container->GetWeakPtr()),
+      UseNewWaitLoop() ? base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
+                                        prefetch_container->GetWeakPtr())
+                       : base::BindOnce(&PrefetchContainer::OnDeterminedHead,
+                                        prefetch_container->GetWeakPtr()),
       weak_first_response_reader);
 
   prefetch_container->SetStreamingURLLoader(weak_streaming_loader);
@@ -264,8 +271,10 @@
           }),
       CreatePrefetchRedirectCallbackForTest(&on_receive_redirect_loop,
                                             &redirect_info, &redirect_head),
-      base::BindOnce(&PrefetchContainer::OnDeterminedHead,
-                     prefetch_container->GetWeakPtr()),
+      UseNewWaitLoop() ? base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
+                                        prefetch_container->GetWeakPtr())
+                       : base::BindOnce(&PrefetchContainer::OnDeterminedHead,
+                                        prefetch_container->GetWeakPtr()),
       prefetch_container->GetResponseReaderForCurrentPrefetch());
 
   prefetch_container->SetStreamingURLLoader(weak_first_streaming_loader);
@@ -325,8 +334,11 @@
                  network::mojom::URLResponseHeadPtr response_head) {
                 NOTREACHED_IN_MIGRATION();
               }),
-          base::BindOnce(&PrefetchContainer::OnDeterminedHead,
-                         prefetch_container->GetWeakPtr()),
+          UseNewWaitLoop()
+              ? base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
+                               prefetch_container->GetWeakPtr())
+              : base::BindOnce(&PrefetchContainer::OnDeterminedHead,
+                               prefetch_container->GetWeakPtr()),
           weak_second_response_reader);
 
   prefetch_container->SetStreamingURLLoader(weak_second_streaming_loader);
diff --git a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc
index f342f38d..ca7b76d 100644
--- a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc
+++ b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc
@@ -84,19 +84,24 @@
       PrefetchContainer* prefetch_container =
           redirect_reader_.GetPrefetchContainer();
       CHECK(prefetch_container);
-      // Note: This method can only be called once per PrefetchContainer (we
-      // have a CHECK in the method). This is guaranteed to be the first time
-      // we call this method for |prefetch_container|, as the other callsite
-      // (in PrefetchService::ReturnPrefetchToServe) would have prevented the
-      // prefetch from being used to serve the navigation (making this
-      // unreachable as |redirect_reader_| would never have been set to
-      // |prefetch_container|). This will also never be called for
-      // |prefetch_container| again as we don't use it to serve any subsequent
-      // redirect hops for this navigation (we unset |redirect_reader_| below),
-      // and |PrefetchService::CollectPotentiallyMatchingPrefetchContainers|
-      // ignores any prefetches with the status kPrefetchNotUsedCookiesChanged
-      // (which is set in |PrefetchContainer::OnCookiesChanged|).
-      prefetch_container->OnCookiesChanged();
+      if (UseNewWaitLoop()) {
+        prefetch_container->OnDetectedCookiesChange2();
+      } else {
+        // Note: This method can only be called once per PrefetchContainer (we
+        // have a CHECK in the method). This is guaranteed to be the first time
+        // we call this method for |prefetch_container|, as the other callsite
+        // (in PrefetchService::ReturnPrefetchToServe) would have prevented the
+        // prefetch from being used to serve the navigation (making this
+        // unreachable as |redirect_reader_| would never have been set to
+        // |prefetch_container|). This will also never be called for
+        // |prefetch_container| again as we don't use it to serve any subsequent
+        // redirect hops for this navigation (we unset |redirect_reader_|
+        // below), and
+        // |PrefetchService::CollectPotentiallyMatchingPrefetchContainers|
+        // ignores any prefetches with the status kPrefetchNotUsedCookiesChanged
+        // (which is set in |PrefetchContainer::OnDetectedCookiesChange|).
+        prefetch_container->OnDetectedCookiesChange();
+      }
     } else {
       OnGotPrefetchToServe(
           frame_tree_node_id_, tentative_resource_request,
@@ -170,13 +175,22 @@
     CHECK(!serving_page_metrics_container_);
   }
 
-  prefetch_match_resolver.SetOnPrefetchToServeReadyCallback(base::BindOnce(
-      &OnGotPrefetchToServe, frame_tree_node_id_, tentative_resource_request,
-      std::move(get_prefetch_callback)));
-  prefetch_service->GetPrefetchToServe(
-      PrefetchContainer::Key(initiator_document_token_,
-                             tentative_resource_request.url),
-      serving_page_metrics_container_, prefetch_match_resolver);
+  auto callback = base::BindOnce(&OnGotPrefetchToServe, frame_tree_node_id_,
+                                 tentative_resource_request,
+                                 std::move(get_prefetch_callback));
+  auto key = PrefetchContainer::Key(initiator_document_token_,
+                                    tentative_resource_request.url);
+  if (UseNewWaitLoop()) {
+    PrefetchMatchResolver2::FindPrefetch(std::move(key), *prefetch_service,
+                                         serving_page_metrics_container_,
+                                         std::move(callback));
+  } else {
+    prefetch_match_resolver.SetOnPrefetchToServeReadyCallback(
+        std::move(callback));
+    prefetch_service->GetPrefetchToServe(std::move(key),
+                                         serving_page_metrics_container_,
+                                         prefetch_match_resolver);
+  }
 }
 
 void PrefetchURLLoaderInterceptor::OnGetPrefetchComplete(
diff --git a/content/browser/renderer_host/DEPS b/content/browser/renderer_host/DEPS
index c2ca138..a770e37b 100644
--- a/content/browser/renderer_host/DEPS
+++ b/content/browser/renderer_host/DEPS
@@ -42,6 +42,12 @@
     # embedded in mus won't be able to talk to the native ozone.
     "+ui/ozone/public/ozone_switches.h",
   ],
+  "render_widget_host_view_aura_unittest\.cc": [
+    "+ui/ozone/public/ozone_platform.h",
+  ],
+  "render_widget_host_view_aura\.cc": [
+    "+ui/ozone/public/ozone_platform.h",
+  ],
   "render_widget_host_view_base\.cc": [
     "+ui/ozone/public/ozone_platform.h",
   ],
diff --git a/content/browser/renderer_host/media/media_stream_power_logger.cc b/content/browser/renderer_host/media/media_stream_power_logger.cc
index 733d665..ef1536e2 100644
--- a/content/browser/renderer_host/media/media_stream_power_logger.cc
+++ b/content/browser/renderer_host/media/media_stream_power_logger.cc
@@ -17,13 +17,15 @@
 }  // namespace
 
 MediaStreamPowerLogger::MediaStreamPowerLogger() {
-  base::PowerMonitor::AddPowerSuspendObserver(this);
-  base::PowerMonitor::AddPowerThermalObserver(this);
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  power_monitor->AddPowerSuspendObserver(this);
+  power_monitor->AddPowerThermalObserver(this);
 }
 
 MediaStreamPowerLogger::~MediaStreamPowerLogger() {
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
-  base::PowerMonitor::RemovePowerThermalObserver(this);
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  power_monitor->RemovePowerSuspendObserver(this);
+  power_monitor->RemovePowerThermalObserver(this);
 }
 
 void MediaStreamPowerLogger::OnSuspend() {
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy.cc b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
index ce8494d8..5f359c4 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
@@ -246,7 +246,12 @@
     blink::mojom::MediaStreamRequestResult result,
     std::unique_ptr<MediaStreamUI> stream_ui) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK((result != blink::mojom::MediaStreamRequestResult::OK &&
+  // If SYSTEM_PERMISSION_DENIED it is not guarantied that
+  // `stream_devices_set.stream_devices` is empty. E.g. might be not empty on
+  // CrOS and in tests.
+  DCHECK((result ==
+          blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED) ||
+         (result != blink::mojom::MediaStreamRequestResult::OK &&
           stream_devices_set.stream_devices.empty()) ||
          (result == blink::mojom::MediaStreamRequestResult::OK &&
           stream_devices_set.stream_devices.size() == 1u));
@@ -254,7 +259,9 @@
   blink::mojom::StreamDevicesSetPtr filtered_devices_set =
       blink::mojom::StreamDevicesSet::New();
   blink::mojom::StreamDevices devices;
-  if (!stream_devices_set.stream_devices.empty()) {
+  if (!stream_devices_set.stream_devices.empty() &&
+      result !=
+          blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED) {
     devices = *stream_devices_set.stream_devices[0];
     filtered_devices_set->stream_devices.emplace_back(
         blink::mojom::StreamDevices::New());
diff --git a/content/browser/renderer_host/media/peer_connection_tracker_host.cc b/content/browser/renderer_host/media/peer_connection_tracker_host.cc
index 8e8b7ae..3017ef1 100644
--- a/content/browser/renderer_host/media/peer_connection_tracker_host.cc
+++ b/content/browser/renderer_host/media/peer_connection_tracker_host.cc
@@ -75,10 +75,11 @@
       peer_pid_(frame->GetProcess()->GetProcess().Pid()) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RegisterHost(this);
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  power_monitor->AddPowerSuspendObserver(this);
   // Ensure that the initial thermal state is known by the |tracker_|.
   base::PowerThermalObserver::DeviceThermalState initial_thermal_state =
-      base::PowerMonitor::AddPowerStateObserverAndReturnPowerThermalState(this);
+      power_monitor->AddPowerStateObserverAndReturnPowerThermalState(this);
 
   frame->GetRemoteInterfaces()->GetInterface(
       tracker_.BindNewPipeAndPassReceiver());
@@ -91,8 +92,9 @@
 PeerConnectionTrackerHost::~PeerConnectionTrackerHost() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RemoveHost(this);
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
-  base::PowerMonitor::RemovePowerThermalObserver(this);
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  power_monitor->RemovePowerSuspendObserver(this);
+  power_monitor->RemovePowerThermalObserver(this);
 }
 
 void PeerConnectionTrackerHost::AddPeerConnection(
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 8acbe54..ec648cc 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -6237,10 +6237,8 @@
     const std::vector<GURL>& urls) {
   static const bool kStartupEnabled =
       base::FeatureList::IsEnabled(features::kSpeculativeServiceWorkerStartup);
-  static const bool kWarmUpEnabled =
-      base::FeatureList::IsEnabled(
-          blink::features::kSpeculativeServiceWorkerWarmUp) &&
-      !blink::features::kSpeculativeServiceWorkerWarmUpDryRun.Get();
+  static const bool kWarmUpEnabled = base::FeatureList::IsEnabled(
+      blink::features::kSpeculativeServiceWorkerWarmUp);
   static const bool kHttpDiskCachePrewarmingEnabled =
       base::FeatureList::IsEnabled(blink::features::kHttpDiskCachePrewarming) &&
       !blink::features::kHttpDiskCachePrewarmingTriggerOnNavigation.Get();
@@ -8832,8 +8830,11 @@
   // These checks ensure malformed partitioned popins cannot be created.
   // Most of these checks should already have been done by the renderer.
   // See https://explainers-by-googlers.github.io/partitioned-popins/
-  // TODO(crbug.com/340606651): We should check the runtime enabled feature.
   if (params->features && params->features->is_partitioned_popin) {
+    if (!base::FeatureList::IsEnabled(blink::features::kPartitionedPopins)) {
+      mojo::ReportBadMessage("Partitioned popins not permitted.");
+      return;
+    }
     if (delegate()->PartitionedPopinOpener()) {
       mojo::ReportBadMessage("Partitioned popins cannot open their own popin.");
       return;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 1343a5b..2ba32a68 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -135,6 +135,10 @@
 #include "ui/base/ime/mojom/virtual_keyboard_types.mojom.h"
 #endif
 
+#if BUILDFLAG(IS_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
 using gfx::RectToSkIRect;
 using gfx::SkIRectToRect;
 
@@ -1947,6 +1951,22 @@
 void RenderWidgetHostViewAura::OnDisplayMetricsChanged(
     const display::Display& display,
     uint32_t metrics) {
+#if BUILDFLAG(IS_OZONE)
+  // TODO(crbug.com/348590032) If per-window scaling is enabled, the display
+  // scale comparison below is not applicable as the WindowTreeHost uses the
+  // accurate per-window scale value and the display uses a scale factor value
+  // that is inferred from the logical size which is prone to rounding errors,
+  // in which case this will never match and end up suppressing visual
+  // properties synchronization after the display configuration changes. So
+  // process display metrics change as usual skipping such checks. Also see
+  // RenderWidgetHostViewBase::UpdateScreenInfo().
+  if (ui::OzonePlatform::GetInstance()
+          ->GetPlatformRuntimeProperties()
+          .supports_per_window_scaling) {
+    ProcessDisplayMetricsChanged();
+    return;
+  }
+#endif  // BUILDFLAG(IS_OZONE)
   display::Screen* screen = display::Screen::GetScreen();
   if (display.id() != screen->GetDisplayNearestWindow(window_).id())
     return;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 273db76..18c9b471 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -132,6 +132,10 @@
 #include "ui/base/win/window_event_target.h"
 #endif
 
+#if BUILDFLAG(IS_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
 using testing::_;
 
 using blink::WebGestureEvent;
@@ -3458,6 +3462,42 @@
   }
 }
 
+#if BUILDFLAG(IS_OZONE)
+// Regression test for crbug.com/360147125.
+// Tests that when per-window scaling is used, the window scale is not compared
+// to display scale when display metrics changes.
+TEST_F(RenderWidgetHostViewAuraTest,
+       SkipDisplayScaleCheckOnDisplayMetricsChangeWithPerWindowScaleEnabled) {
+  if (ui::OzonePlatform::GetPlatformNameForTest() != "wayland") {
+    GTEST_SKIP() << "test only applicable on wayland";
+  }
+  using SupportsForTest =
+      ui::OzonePlatform::PlatformRuntimeProperties::SupportsForTest;
+  base::AutoReset<SupportsForTest> auto_reset(
+      &ui::OzonePlatform::PlatformRuntimeProperties::
+          override_supports_per_window_scaling_for_test,
+      SupportsForTest::kYes);
+  aura::Window* root_window = parent_view_->GetNativeView()->GetRootWindow();
+  InitViewForFrame(nullptr);
+  ParentHostView(view_, parent_view_);
+  EXPECT_TRUE(view_->CanSynchronizeVisualProperties());
+
+  // Set different values for window and display scales.
+  aura_test_helper_->GetTestScreen()->SetPreferredScaleFactorForWindow(
+      root_window, 1.75f);
+  aura_test_helper_->GetTestScreen()->SetDeviceScaleFactor(1.74623f);
+
+  view_->OnDisplayMetricsChanged(
+      display::Screen::GetScreen()->GetDisplayNearestView(
+          view_->GetNativeView()),
+      0);
+
+  // Synchronization of visual properties should be allowed in spite of the
+  // mismatch in preferred window scale and display scale.
+  EXPECT_TRUE(view_->CanSynchronizeVisualProperties());
+}
+#endif
+
 // Ensures that touch event positions are never truncated to integers.
 TEST_F(RenderWidgetHostViewAuraTest, TouchEventPositionsArentRounded) {
   const float kX = 30.58f;
diff --git a/content/browser/resources/webxr_internals/session_statistics_table.ts b/content/browser/resources/webxr_internals/session_statistics_table.ts
index 06481d03..8394042 100644
--- a/content/browser/resources/webxr_internals/session_statistics_table.ts
+++ b/content/browser/resources/webxr_internals/session_statistics_table.ts
@@ -48,19 +48,19 @@
 
     const fps = xrSessionStatistics.numFrames / durationInSeconds;
     const droppedFrames = xrSessionStatistics.droppedFrames / durationInSeconds;
-    const frameDataTime =
-        (Number(xrSessionStatistics.frameDataTime.microseconds) * 1.0 / 1000)
-            .toFixed(2);
-    const animationFrameTime =
-        (Number(xrSessionStatistics.pageAnimationFrameTime.microseconds) * 1.0 /
-         1000)
-            .toFixed(2);
+    const frameDataTime = this.getDisplayMillisecondsFromMicroSeconds(
+        xrSessionStatistics.frameDataTime.microseconds);
+    const animationFrameTime = this.getDisplayMillisecondsFromMicroSeconds(
+        xrSessionStatistics.pageAnimationFrameTime.microseconds);
+    const submitFrameTime = this.getDisplayMillisecondsFromMicroSeconds(
+        xrSessionStatistics.submitFrameTime.microseconds);
     const cellValues = [
       `${this.totalDuration}`,
       `${fps}`,
       `${droppedFrames}`,
       `${frameDataTime}`,
       `${animationFrameTime}`,
+      `${submitFrameTime}`,
     ];
 
     this.textLines.push(cellValues.join(', '));
@@ -68,7 +68,8 @@
     const cellValuesString = `Duration:${this.totalDuration}ms, Frame Rate:${
         fps}, Dropped Frames:${droppedFrames}, Frame Data Time:${
         frameDataTime}ms/frame, Animation Frame Time:${
-        animationFrameTime}ms/frame`;
+        animationFrameTime}ms/frame, Submit Frame Time:${
+        submitFrameTime}ms/frame`;
     this.addRow([cellValuesString]);
   }
 
@@ -93,6 +94,13 @@
     const message = xrLogMessage.message;
     this.addRow([message]);
   }
+
+  // Method to convert microseconds to milliseconds and round to 2 decimal
+  // places and return  it as a string
+  getDisplayMillisecondsFromMicroSeconds(time: bigint): string {
+    const timeInMilliseconds = Number(time) / 1000;
+    return timeInMilliseconds.toFixed(2);
+  }
 }
 
 // Declare the custom element
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 84fdbef..1af53cf0 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -54,6 +54,7 @@
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/service_worker/embedded_worker_status.h"
 #include "third_party/blink/public/common/service_worker/service_worker_scope_match.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
@@ -726,8 +727,12 @@
     const GURL& document_url,
     const blink::StorageKey& key,
     ServiceWorkerContext::WarmUpServiceWorkerCallback callback) {
+  // kSpeculativeServiceWorkerWarmUp enqueues navigation candidate URLs. This is
+  // the queue length of the candidate URLs.
   static const size_t kRequestQueueLength =
-      blink::features::kSpeculativeServiceWorkerWarmUpRequestQueueLength.Get();
+      base::GetFieldTrialParamByFeatureAsInt(
+          blink::features::kSpeculativeServiceWorkerWarmUp,
+          "sw_warm_up_request_queue_length", 1000);
 
   // Erase redundant warm-up requests.
   std::vector<ServiceWorkerContext::WarmUpServiceWorkerCallback>
diff --git a/content/browser/startup_helper.cc b/content/browser/startup_helper.cc
index 6ec030c..ea054fe 100644
--- a/content/browser/startup_helper.cc
+++ b/content/browser/startup_helper.cc
@@ -50,40 +50,26 @@
 
 namespace {
 
+#if BUILDFLAG(IS_ANDROID)
+// Mobile config, for iOS see ios/web/app/web_main_loop.cc.
+constexpr size_t kThreadPoolDefaultMin = 6;
+constexpr size_t kThreadPoolMax = 8;
+constexpr double kThreadPoolCoresMultiplier = 0.6;
+constexpr int kThreadPoolOffset = 0;
+#else
+// Desktop config.
+constexpr size_t kThreadPoolDefaultMin = 16;
+constexpr size_t kThreadPoolMax = 32;
+constexpr double kThreadPoolCoresMultiplier = 0.6;
+constexpr int kThreadPoolOffset = 0;
+#endif
+
 BASE_FEATURE(kBrowserThreadPoolAdjustment,
              "BrowserThreadPoolAdjustment",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
-// For iOS see ios/web/app/web_main_loop.cc.
-MIRACLE_PARAMETER_FOR_INT(GetBrowserThreadPoolMin,
-                          kBrowserThreadPoolAdjustment,
-                          "BrowserThreadPoolMin",
-#if BUILDFLAG(IS_ANDROID)
-                          6
-#else
-                          16
-#endif
-)
-
-MIRACLE_PARAMETER_FOR_INT(GetBrowserThreadPoolMax,
-                          kBrowserThreadPoolAdjustment,
-                          "BrowserThreadPoolMax",
-#if BUILDFLAG(IS_ANDROID)
-                          8
-#else
-                          32
-#endif
-)
-
-MIRACLE_PARAMETER_FOR_DOUBLE(GetBrowserThreadPoolCoresMultiplier,
-                             kBrowserThreadPoolAdjustment,
-                             "BrowserThreadPoolCoresMultiplier",
-                             0.6)
-
-MIRACLE_PARAMETER_FOR_INT(GetBrowserThreadPoolOffset,
-                          kBrowserThreadPoolAdjustment,
-                          "BrowserThreadPoolOffset",
-                          0)
+const base::FeatureParam<int> kBrowserThreadPoolMin{
+    &kBrowserThreadPoolAdjustment, "min", kThreadPoolDefaultMin};
 
 }  // namespace
 
@@ -93,11 +79,10 @@
   // Ensure we always support at least one thread regardless of the field trial
   // param setting.
   size_t min =
-      base::checked_cast<size_t>(std::max(GetBrowserThreadPoolMin(), 1));
+      base::checked_cast<size_t>(std::max(kBrowserThreadPoolMin.Get(), 1));
   base::ThreadPoolInstance::InitParams thread_pool_init_params = {
       base::RecommendedMaxNumberOfThreadsInThreadGroup(
-          min, base::checked_cast<size_t>(GetBrowserThreadPoolMax()),
-          GetBrowserThreadPoolCoresMultiplier(), GetBrowserThreadPoolOffset())};
+          min, kThreadPoolMax, kThreadPoolCoresMultiplier, kThreadPoolOffset)};
 
 #if BUILDFLAG(IS_WIN)
   thread_pool_init_params.common_thread_pool_environment = base::
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 88464ec..9729fa8a 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1154,10 +1154,6 @@
     GetServiceWorkerContext()->Shutdown();
   }
 
-  if (GetPlatformNotificationContext()) {
-    GetPlatformNotificationContext()->Shutdown();
-  }
-
   if (GetBackgroundSyncContext()) {
     GetBackgroundSyncContext()->Shutdown();
   }
@@ -1192,6 +1188,10 @@
     GetContentIndexContext()->Shutdown();
   }
 
+  if (GetPlatformNotificationContext()) {
+    GetPlatformNotificationContext()->Shutdown();
+  }
+
   if (keep_alive_url_loader_service_) {
     keep_alive_url_loader_service_->Shutdown();
   }
diff --git a/content/browser/web_exposed_isolation_info.h b/content/browser/web_exposed_isolation_info.h
index 3cf2bb527..65aaa32 100644
--- a/content/browser/web_exposed_isolation_info.h
+++ b/content/browser/web_exposed_isolation_info.h
@@ -25,11 +25,13 @@
 //     This is computed purely by examining Cross-Origin-Opener-Policy and
 //     Cross-Origin-Embedder-Policy headers on a given response.
 //
-// 3.  Isolated Application contexts, whose requirements are still being
-//     fleshed out.
-//
-// TODO(mkwst): Improve the description of the Isolated Application context as
-// we work out what it is: https://crbug.com/1206150.
+// 3.  Isolated Application contexts, which correspond to Isolated Contexts as
+//     defined in:
+//     https://wicg.github.io/isolated-web-apps/isolated-contexts.html
+//     These contexts have higher isolation and integrity requirements than
+//     cross-origin isolation. The embedder is responsible for deciding whether
+//     a particular cross-origin isolated environment can qualify for this
+//     isolation level.
 class CONTENT_EXPORT WebExposedIsolationInfo {
  public:
   static WebExposedIsolationInfo CreateNonIsolated();
@@ -70,11 +72,8 @@
   // Returns `true` for contexts created via `CreateIsolatedApplication()`, and
   // `false` for those created via `CreateNonIsolated()` or `CreatedIsolated()`.
   //
-  // This corresponds to "application isolation", which is not yet defined, but
-  // will certainly include a superset of "cross-origin isolation"'s
-  // requirements.
-  //
-  // TODO(crbug.com/40180791): Define and specify these restrictions.
+  // This corresponds to "isolated contexts" as defined here:
+  // https://wicg.github.io/isolated-web-apps/isolated-contexts.html
   bool is_isolated_application() const {
     return origin_.has_value() && isolated_application_;
   }
diff --git a/content/browser/webauth/authenticator_common_impl.cc b/content/browser/webauth/authenticator_common_impl.cc
index 797b5a4..dc55328 100644
--- a/content/browser/webauth/authenticator_common_impl.cc
+++ b/content/browser/webauth/authenticator_common_impl.cc
@@ -41,6 +41,7 @@
 #include "device/fido/attestation_statement.h"
 #include "device/fido/authenticator_data.h"
 #include "device/fido/authenticator_get_assertion_response.h"
+#include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/features.h"
 #include "device/fido/fido_authenticator.h"
@@ -59,7 +60,7 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_source.h"
-#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/boringssl/src/include/openssl/sha.h"
 #include "third_party/boringssl/src/pki/input.h"
 #include "third_party/boringssl/src/pki/parse_values.h"
@@ -109,6 +110,10 @@
 using GetAssertionOutcome = AuthenticatorCommonImpl::GetAssertionOutcome;
 using MakeCredentialOutcome = AuthenticatorCommonImpl::MakeCredentialOutcome;
 using RequestMode = AuthenticatorCommonImpl::RequestMode;
+using MakeCredentialCallback =
+    blink::mojom::Authenticator::MakeCredentialCallback;
+using GetAssertionCallback = blink::mojom::Authenticator::GetAssertionCallback;
+using ReportCallback = blink::mojom::Authenticator::ReportCallback;
 
 namespace {
 
@@ -664,11 +669,11 @@
   raw_ptr<device::FidoDiscoveryFactory,
           FlakyDanglingUntriaged | AcrossTasksDanglingUntriaged>
       discovery_factory_testing_override = nullptr;
-  blink::mojom::Authenticator::MakeCredentialCallback
-      make_credential_response_callback;
-  blink::mojom::Authenticator::GetAssertionCallback
-      get_assertion_response_callback;
-  blink::mojom::Authenticator::ReportCallback report_response_callback;
+  absl::variant<absl::monostate,
+                MakeCredentialCallback,
+                GetAssertionCallback,
+                ReportCallback>
+      response_callback;
   std::string client_data_json;
   // conditional_ui_treatment tracks any non-standard conditional UI behaviours
   // that have been requested.
@@ -681,25 +686,28 @@
   std::unique_ptr<base::OneShotTimer> timer =
       std::make_unique<base::OneShotTimer>();
   std::optional<std::string> app_id;
-  std::optional<device::CtapMakeCredentialRequest> ctap_make_credential_request;
-  std::optional<device::MakeCredentialOptions> make_credential_options;
-  std::optional<device::CtapGetAssertionRequest> ctap_get_assertion_request;
-  std::optional<device::CtapGetAssertionOptions> ctap_get_assertion_options;
+  absl::variant<absl::monostate,
+                device::CtapMakeCredentialRequest,
+                device::CtapGetAssertionRequest>
+      ctap_request;
+  absl::variant<absl::monostate,
+                device::MakeCredentialOptions,
+                device::CtapGetAssertionOptions>
+      request_options;
   // awaiting_attestation_response_ is true if the embedder has been queried
   // about an attestsation decision and the response is still pending.
   bool awaiting_attestation_response = false;
   blink::mojom::AuthenticatorStatus error_awaiting_user_acknowledgement =
       blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR;
-  std::optional<CredentialRequestResult> get_assertion_result;
-  std::optional<CredentialRequestResult> make_credential_result;
   bool discoverable_credential_request = false;
   // Indicates whether the current request is a modal WebAuthn call, a
   // conditional UI WebAuthn call, or a payment-related request.
   std::optional<RequestMode> mode;
   // The hints set by the request, if any.
   base::flat_set<blink::mojom::Hint> hints;
-  std::optional<MakeCredentialOutcome> make_credential_reporting_outcome;
-  std::optional<GetAssertionOutcome> get_assertion_reporting_outcome;
+  std::optional<CredentialRequestResult> request_result;
+  absl::variant<absl::monostate, MakeCredentialOutcome, GetAssertionOutcome>
+      request_outcome;
 
   base::flat_set<RequestExtension> requested_extensions;
 
@@ -763,27 +771,30 @@
 
 void AuthenticatorCommonImpl::StartMakeCredentialRequest(
     bool allow_skipping_pin_touch) {
-  req_state_->make_credential_result.reset();
+  req_state_->request_result.reset();
   InitDiscoveryFactory();
 
+  auto* ctap_make_credential_request =
+      &absl::get<device::CtapMakeCredentialRequest>(req_state_->ctap_request);
+  auto* make_credential_options =
+      &absl::get<device::MakeCredentialOptions>(req_state_->request_options);
+
   req_state_->request_delegate->ConfigureDiscoveries(
       req_state_->caller_origin, req_state_->relying_party_id, RequestSource(),
       device::FidoRequestType::kMakeCredential,
-      req_state_->make_credential_options->resident_key,
-      req_state_->make_credential_options->user_verification,
-      req_state_->ctap_make_credential_request->user.name,
+      make_credential_options->resident_key,
+      make_credential_options->user_verification,
+      ctap_make_credential_request->user.name,
       base::span<const device::CableDiscoveryData>(),
       browser_passkeys_available_, discovery_factory());
   SetHints(req_state_->request_delegate.get(), req_state_->hints);
 
-  req_state_->make_credential_options->allow_skipping_pin_touch =
-      allow_skipping_pin_touch;
+  make_credential_options->allow_skipping_pin_touch = allow_skipping_pin_touch;
 
   base::flat_set<device::FidoTransportProtocol> transports =
-      GetWebAuthnTransports(
-          GetRenderFrameHost(), discovery_factory(),
-          UsesDiscoverableCreds(*req_state_->make_credential_options),
-          is_uvpaa_override_);
+      GetWebAuthnTransports(GetRenderFrameHost(), discovery_factory(),
+                            UsesDiscoverableCreds(*make_credential_options),
+                            is_uvpaa_override_);
 
   auto platform_discoveries =
       discovery_factory()->IsTestOverride()
@@ -792,8 +803,7 @@
   req_state_->request_handler =
       std::make_unique<device::MakeCredentialRequestHandler>(
           discovery_factory(), std::move(platform_discoveries), transports,
-          *req_state_->ctap_make_credential_request,
-          *req_state_->make_credential_options,
+          *ctap_make_credential_request, *make_credential_options,
           base::BindOnce(&AuthenticatorCommonImpl::OnRegisterResponse,
                          weak_factory_.GetWeakPtr()));
 
@@ -821,31 +831,34 @@
 
 void AuthenticatorCommonImpl::StartGetAssertionRequest(
     bool allow_skipping_pin_touch) {
-  req_state_->get_assertion_result.reset();
+  req_state_->request_result.reset();
   InitDiscoveryFactory();
 
   base::span<const device::CableDiscoveryData> cable_pairings;
-  if (req_state_->ctap_get_assertion_request->cable_extension && IsFocused()) {
-    cable_pairings = *req_state_->ctap_get_assertion_request->cable_extension;
+  auto* ctap_get_assertion_request =
+      &absl::get<device::CtapGetAssertionRequest>(req_state_->ctap_request);
+  auto* ctap_get_assertion_options =
+      &absl::get<device::CtapGetAssertionOptions>(req_state_->request_options);
+  if (ctap_get_assertion_request->cable_extension && IsFocused()) {
+    cable_pairings = *ctap_get_assertion_request->cable_extension;
   }
   req_state_->request_delegate->ConfigureDiscoveries(
       req_state_->caller_origin, req_state_->relying_party_id, RequestSource(),
       device::FidoRequestType::kGetAssertion,
       /*resident_key_requirement=*/std::nullopt,
-      req_state_->ctap_get_assertion_request->user_verification,
+      ctap_get_assertion_request->user_verification,
       /*user_name=*/std::nullopt, cable_pairings, browser_passkeys_available_,
       discovery_factory());
 #if BUILDFLAG(IS_CHROMEOS)
   discovery_factory()->set_get_assertion_request_for_legacy_credential_check(
-      *req_state_->ctap_get_assertion_request);
+      *ctap_get_assertion_request);
 #endif
   SetHints(req_state_->request_delegate.get(), req_state_->hints);
 
   base::flat_set<device::FidoTransportProtocol> transports =
-      GetWebAuthnTransports(
-          GetRenderFrameHost(), discovery_factory(),
-          UsesDiscoverableCreds(*req_state_->ctap_get_assertion_request),
-          is_uvpaa_override_);
+      GetWebAuthnTransports(GetRenderFrameHost(), discovery_factory(),
+                            UsesDiscoverableCreds(*ctap_get_assertion_request),
+                            is_uvpaa_override_);
 
   auto platform_discoveries =
       discovery_factory()->IsTestOverride()
@@ -853,8 +866,8 @@
           : req_state_->request_delegate->CreatePlatformDiscoveries();
   auto request_handler = std::make_unique<device::GetAssertionRequestHandler>(
       discovery_factory(), std::move(platform_discoveries), transports,
-      *req_state_->ctap_get_assertion_request,
-      *req_state_->ctap_get_assertion_options, allow_skipping_pin_touch,
+      *ctap_get_assertion_request, *ctap_get_assertion_options,
+      allow_skipping_pin_touch,
       base::BindOnce(&AuthenticatorCommonImpl::OnSignResponse,
                      weak_factory_.GetWeakPtr()));
   request_handler->transport_availability_info().conditional_ui_treatment =
@@ -895,7 +908,7 @@
 void AuthenticatorCommonImpl::MakeCredential(
     url::Origin caller_origin,
     blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
-    blink::mojom::Authenticator::MakeCredentialCallback callback) {
+    MakeCredentialCallback callback) {
   if (req_state_) {
     std::move(callback).Run(blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
                             nullptr, nullptr);
@@ -904,7 +917,7 @@
   req_state_ = std::make_unique<RequestState>();
   req_state_->request_key = RequestKey(next_request_key_);
 
-  req_state_->make_credential_response_callback = std::move(callback);
+  req_state_->response_callback = std::move(callback);
   req_state_->hints.insert(options->hints.begin(), options->hints.end());
 
   if (options->is_payment_credential_creation) {
@@ -924,8 +937,7 @@
       security_checker_->ValidateAncestorOrigins(caller_origin, request_type,
                                                  &is_cross_origin_iframe);
   if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
-    req_state_->make_credential_reporting_outcome =
-        MakeCredentialOutcome::kSecurityError;
+    req_state_->request_outcome = MakeCredentialOutcome::kSecurityError;
     CompleteMakeCredentialRequest(status);
     return;
   }
@@ -933,8 +945,7 @@
   if (!security_checker_->DeduplicateCredentialDescriptorListAndValidateLength(
           &options->exclude_credentials)) {
     mojo::ReportBadMessage("invalid exclude_credentials length");
-    req_state_->make_credential_reporting_outcome =
-        MakeCredentialOutcome::kOtherFailure;
+    req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
     return;
@@ -971,16 +982,14 @@
   req_state_->remote_rp_id_validation.reset();
 
   if (rp_id_validation_result != blink::mojom::AuthenticatorStatus::SUCCESS) {
-    req_state_->make_credential_reporting_outcome =
-        MakeCredentialOutcome::kSecurityError;
+    req_state_->request_outcome = MakeCredentialOutcome::kSecurityError;
     CompleteMakeCredentialRequest(rp_id_validation_result);
     return;
   }
 
   req_state_->request_delegate = MaybeCreateRequestDelegate();
   if (!req_state_->request_delegate) {
-    req_state_->make_credential_reporting_outcome =
-        MakeCredentialOutcome::kOtherFailure;
+    req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::PENDING_REQUEST);
     return;
@@ -990,8 +999,7 @@
       !disable_tls_check_ &&
       !GetContentClient()->browser()->IsSecurityLevelAcceptableForWebAuthn(
           GetRenderFrameHost(), caller_origin)) {
-    req_state_->make_credential_reporting_outcome =
-        MakeCredentialOutcome::kOtherFailure;
+    req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
     return;
@@ -1007,8 +1015,7 @@
         *options->appid_exclude, caller_origin,
         options->remote_desktop_client_override, &appid_exclude.value());
     if (add_id_status != blink::mojom::AuthenticatorStatus::SUCCESS) {
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kSecurityError;
+      req_state_->request_outcome = MakeCredentialOutcome::kSecurityError;
       CompleteMakeCredentialRequest(add_id_status);
       return;
     }
@@ -1024,8 +1031,7 @@
   if (proxy) {
     if (options->remote_desktop_client_override) {
       // Don't allow proxying of an already proxied request.
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kOtherFailure;
+      req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
       CompleteMakeCredentialRequest(
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
@@ -1066,16 +1072,14 @@
       // This will be handled by the request handler.
       break;
     case device::fido_filter::Action::BLOCK:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kFilterBlock;
+      req_state_->request_outcome = MakeCredentialOutcome::kFilterBlock;
       CompleteMakeCredentialRequest(
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
   }
 
   if (!IsFocused()) {
-    req_state_->make_credential_reporting_outcome =
-        MakeCredentialOutcome::kOtherFailure;
+    req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::NOT_FOCUSED);
     return;
@@ -1086,27 +1090,28 @@
           options->authenticator_selection
               ? *options->authenticator_selection
               : device::AuthenticatorSelectionCriteria();
-  req_state_->make_credential_options =
-      device::MakeCredentialOptions(authenticator_selection_criteria);
-  req_state_->make_credential_options->json =
-      base::MakeRefCounted<device::JSONRequest>(webauthn::ToValue(options));
 
+  req_state_->request_options =
+      device::MakeCredentialOptions(authenticator_selection_criteria);
+  auto* make_credential_options =
+      &absl::get<device::MakeCredentialOptions>(req_state_->request_options);
+  make_credential_options->json =
+      base::MakeRefCounted<device::JSONRequest>(webauthn::ToValue(options));
   const bool might_create_resident_key =
-      req_state_->make_credential_options->resident_key !=
+      make_credential_options->resident_key !=
       device::ResidentKeyRequirement::kDiscouraged;
   if (might_create_resident_key &&
       !GetWebAuthenticationDelegate()->SupportsResidentKeys(
           GetRenderFrameHost())) {
-    if (req_state_->make_credential_options->resident_key ==
+    if (make_credential_options->resident_key ==
         device::ResidentKeyRequirement::kRequired) {
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kRkNotSupported;
+      req_state_->request_outcome = MakeCredentialOutcome::kRkNotSupported;
       CompleteMakeCredentialRequest(
           blink::mojom::AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED);
       return;
     }
     // Downgrade 'preferred' to 'discouraged'.
-    req_state_->make_credential_options->resident_key =
+    make_credential_options->resident_key =
         device::ResidentKeyRequirement::kDiscouraged;
   }
 
@@ -1127,8 +1132,7 @@
            blink::mojom::ProtectionPolicy::UV_REQUIRED &&
        authenticator_selection_criteria.user_verification_requirement !=
            device::UserVerificationRequirement::kRequired)) {
-    req_state_->make_credential_reporting_outcome =
-        MakeCredentialOutcome::kOtherFailure;
+    req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::PROTECTION_POLICY_INCONSISTENT);
     return;
@@ -1136,9 +1140,9 @@
 
   std::optional<device::CredProtectRequest> cred_protect_request =
       ProtectionPolicyToCredProtect(options->protection_policy,
-                                    *req_state_->make_credential_options);
+                                    *make_credential_options);
   if (cred_protect_request) {
-    req_state_->make_credential_options->cred_protect_request = {
+    make_credential_options->cred_protect_request = {
         {*cred_protect_request, options->enforce_protection_policy}};
   }
 
@@ -1161,34 +1165,33 @@
   req_state_->client_data_json =
       BuildClientDataJson(std::move(client_data_json_params));
 
-  req_state_->ctap_make_credential_request = device::CtapMakeCredentialRequest(
+  req_state_->ctap_request = device::CtapMakeCredentialRequest(
       req_state_->client_data_json, options->relying_party, options->user,
       device::PublicKeyCredentialParams(options->public_key_parameters));
+  auto* ctap_make_credential_request =
+      &absl::get<device::CtapMakeCredentialRequest>(req_state_->ctap_request);
 
-  req_state_->ctap_make_credential_request->exclude_list =
-      options->exclude_credentials;
+  ctap_make_credential_request->exclude_list = options->exclude_credentials;
   if (options->prf_enable) {
     req_state_->requested_extensions.insert(RequestExtension::kPRF);
-    req_state_->ctap_make_credential_request->hmac_secret = true;
+    ctap_make_credential_request->hmac_secret = true;
 
     if (options->prf_input) {
       std::optional<device::PRFInput> prf_input =
           ParsePRFInputForMakeCredential(options->prf_input);
       if (!prf_input) {
         mojo::ReportBadMessage("invalid PRF inputs");
-        req_state_->make_credential_reporting_outcome =
-            MakeCredentialOutcome::kOtherFailure;
+        req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
         CompleteMakeCredentialRequest(
             blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
         return;
       }
-      req_state_->ctap_make_credential_request->prf_input =
-          std::move(*prf_input);
+      ctap_make_credential_request->prf_input = std::move(*prf_input);
     }
   }
   if (options->hmac_create_secret) {
     req_state_->requested_extensions.insert(RequestExtension::kHMACSecret);
-    req_state_->ctap_make_credential_request->hmac_secret = true;
+    ctap_make_credential_request->hmac_secret = true;
   }
   if (options->cred_props) {
     req_state_->requested_extensions.insert(RequestExtension::kCredProps);
@@ -1198,17 +1201,15 @@
   }
   if (options->cred_blob) {
     req_state_->requested_extensions.insert(RequestExtension::kCredBlob);
-    req_state_->ctap_make_credential_request->cred_blob = *options->cred_blob;
+    ctap_make_credential_request->cred_blob = *options->cred_blob;
   }
   if (options->min_pin_length_requested) {
     req_state_->requested_extensions.insert(RequestExtension::kMinPINLength);
-    req_state_->ctap_make_credential_request->min_pin_length_requested = true;
+    ctap_make_credential_request->min_pin_length_requested = true;
   }
-  req_state_->make_credential_options->large_blob_support =
-      options->large_blob_enable;
-  req_state_->ctap_make_credential_request->app_id_exclude =
-      std::move(appid_exclude);
-  req_state_->make_credential_options->is_off_the_record_context =
+  make_credential_options->large_blob_support = options->large_blob_enable;
+  ctap_make_credential_request->app_id_exclude = std::move(appid_exclude);
+  make_credential_options->is_off_the_record_context =
       GetBrowserContext()->IsOffTheRecord();
 
   // Compute the effective attestation conveyance preference.
@@ -1224,9 +1225,7 @@
     attestation =
         device::AttestationConveyancePreference::kEnterpriseApprovedByBrowser;
   }
-  req_state_->ctap_make_credential_request->attestation_preference =
-      attestation;
-
+  ctap_make_credential_request->attestation_preference = attestation;
   GetWebAuthenticationDelegate()->BrowserProvidedPasskeysAvailable(
       GetBrowserContext(),
       base::BindOnce(
@@ -1265,7 +1264,7 @@
     url::Origin caller_origin,
     blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
     blink::mojom::PaymentOptionsPtr payment_options,
-    blink::mojom::Authenticator::GetAssertionCallback callback) {
+    GetAssertionCallback callback) {
   if (req_state_) {
     std::move(callback).Run(blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
                             nullptr, nullptr);
@@ -1274,7 +1273,7 @@
   req_state_ = std::make_unique<RequestState>();
   req_state_->request_key = RequestKey(next_request_key_);
 
-  req_state_->get_assertion_response_callback = std::move(callback);
+  req_state_->response_callback = std::move(callback);
   if (!payment_options.is_null()) {
     req_state_->mode = RequestMode::kPayment;
   } else if (options->is_conditional) {
@@ -1312,8 +1311,7 @@
           : WebAuthRequestSecurityChecker::RequestType::
                 kGetPaymentCredentialAssertion;
   if (!payment_options.is_null() && options->allow_credentials.empty()) {
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kOtherFailure;
+    req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
     NOTREACHED_IN_MIGRATION();
@@ -1324,8 +1322,7 @@
       security_checker_->ValidateAncestorOrigins(caller_origin, request_type,
                                                  &is_cross_origin_iframe);
   if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kSecurityError;
+    req_state_->request_outcome = GetAssertionOutcome::kSecurityError;
     CompleteGetAssertionRequest(status);
     return;
   }
@@ -1333,8 +1330,7 @@
   if (!security_checker_->DeduplicateCredentialDescriptorListAndValidateLength(
           &options->allow_credentials)) {
     mojo::ReportBadMessage("invalid allow_credentials length");
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kOtherFailure;
+    req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
     return;
@@ -1374,16 +1370,14 @@
   req_state_->remote_rp_id_validation.reset();
 
   if (rp_id_validation_result != blink::mojom::AuthenticatorStatus::SUCCESS) {
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kSecurityError;
+    req_state_->request_outcome = GetAssertionOutcome::kSecurityError;
     CompleteGetAssertionRequest(rp_id_validation_result);
     return;
   }
 
   req_state_->request_delegate = MaybeCreateRequestDelegate();
   if (!req_state_->request_delegate) {
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kOtherFailure;
+    req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::PENDING_REQUEST);
     return;
@@ -1392,8 +1386,7 @@
       !disable_tls_check_ &&
       !GetContentClient()->browser()->IsSecurityLevelAcceptableForWebAuthn(
           GetRenderFrameHost(), caller_origin)) {
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kOtherFailure;
+    req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
     return;
@@ -1409,8 +1402,7 @@
         *options->extensions->appid, caller_origin,
         options->extensions->remote_desktop_client_override, &app_id);
     if (add_id_status != blink::mojom::AuthenticatorStatus::SUCCESS) {
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kSecurityError;
+      req_state_->request_outcome = GetAssertionOutcome::kSecurityError;
       CompleteGetAssertionRequest(add_id_status);
       return;
     }
@@ -1426,8 +1418,7 @@
     if (options->is_conditional ||
         (options->extensions->remote_desktop_client_override)) {
       // Don't allow proxying of an already proxied or conditional request.
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kOtherFailure;
+      req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
       CompleteGetAssertionRequest(
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
@@ -1479,8 +1470,7 @@
           req_state_->relying_party_id,
           /*device=*/std::nullopt,
           /*id=*/std::nullopt) == device::fido_filter::Action::BLOCK) {
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kFilterBlock;
+    req_state_->request_outcome = GetAssertionOutcome::kFilterBlock;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
     return;
@@ -1506,8 +1496,7 @@
   if (options->allow_credentials.empty()) {
     if (!GetWebAuthenticationDelegate()->SupportsResidentKeys(
             GetRenderFrameHost())) {
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kRkNotSupported;
+      req_state_->request_outcome = GetAssertionOutcome::kRkNotSupported;
       CompleteGetAssertionRequest(
           blink::mojom::AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED);
       return;
@@ -1517,8 +1506,7 @@
 
   if (options->extensions->large_blob_read &&
       options->extensions->large_blob_write) {
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kOtherFailure;
+    req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::CANNOT_READ_AND_WRITE_LARGE_BLOB);
     return;
@@ -1528,8 +1516,7 @@
     req_state_->requested_extensions.insert(RequestExtension::kLargeBlobRead);
   } else if (options->extensions->large_blob_write) {
     if (options->allow_credentials.size() != 1) {
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kOtherFailure;
+      req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
       CompleteGetAssertionRequest(blink::mojom::AuthenticatorStatus::
                                       INVALID_ALLOW_CREDENTIALS_FOR_LARGE_BLOB);
       return;
@@ -1537,12 +1524,17 @@
     req_state_->requested_extensions.insert(RequestExtension::kLargeBlobWrite);
   }
 
-  req_state_->ctap_get_assertion_request = CreateCtapGetAssertionRequest(
+  req_state_->ctap_request = CreateCtapGetAssertionRequest(
       req_state_->client_data_json, options, req_state_->app_id);
-  req_state_->ctap_get_assertion_options.emplace();
-  req_state_->ctap_get_assertion_options->is_off_the_record_context =
+  auto* ctap_get_assertion_request =
+      &absl::get<device::CtapGetAssertionRequest>(req_state_->ctap_request);
+
+  req_state_->request_options.emplace<device::CtapGetAssertionOptions>();
+  auto* ctap_get_assertion_options =
+      &absl::get<device::CtapGetAssertionOptions>(req_state_->request_options);
+  ctap_get_assertion_options->is_off_the_record_context =
       GetBrowserContext()->IsOffTheRecord();
-  req_state_->ctap_get_assertion_options->json =
+  ctap_get_assertion_options->json =
       base::MakeRefCounted<device::JSONRequest>(webauthn::ToValue(options));
 
   if (options->extensions->prf) {
@@ -1556,23 +1548,22 @@
     // authenticator support on Android.
     if (!prf_inputs || options->extensions->prf_inputs_hashed) {
       mojo::ReportBadMessage("invalid PRF inputs");
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kOtherFailure;
+      req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
       CompleteGetAssertionRequest(
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     }
-    req_state_->ctap_get_assertion_options->prf_inputs = std::move(*prf_inputs);
+    ctap_get_assertion_options->prf_inputs = std::move(*prf_inputs);
   }
 
   if (options->extensions->get_cred_blob) {
     req_state_->requested_extensions.insert(RequestExtension::kGetCredBlob);
-    req_state_->ctap_get_assertion_request->get_cred_blob = true;
+    ctap_get_assertion_request->get_cred_blob = true;
   }
 
-  req_state_->ctap_get_assertion_options->large_blob_read =
+  ctap_get_assertion_options->large_blob_read =
       options->extensions->large_blob_read;
-  req_state_->ctap_get_assertion_options->large_blob_write =
+  ctap_get_assertion_options->large_blob_write =
       options->extensions->large_blob_write;
   GetWebAuthenticationDelegate()->BrowserProvidedPasskeysAvailable(
       GetBrowserContext(),
@@ -1772,7 +1763,7 @@
   req_state_ = std::make_unique<RequestState>();
   req_state_->request_key = RequestKey(next_request_key_);
 
-  req_state_->report_response_callback = std::move(callback);
+  req_state_->response_callback = std::move(callback);
   req_state_->caller_origin = std::move(caller_origin);
   req_state_->relying_party_id = options->relying_party_id;
 
@@ -1853,7 +1844,7 @@
     return;
   }
 
-  req_state_->make_credential_result = CredentialRequestResultFromCode(
+  req_state_->request_result = CredentialRequestResultFromCode(
       status_code == device::MakeCredentialStatus::kSuccess,
       authenticator->GetType());
 
@@ -1869,8 +1860,7 @@
       //
       // Windows already behaves like this and so its representation of
       // InvalidStateError is handled this way too.
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kCredentialExcluded;
+      req_state_->request_outcome = MakeCredentialOutcome::kCredentialExcluded;
       if ((authenticator &&
            IsPlatformAuthenticatorForInvalidStateError(authenticator)) ||
           status_code == device::MakeCredentialStatus::kWinInvalidStateError) {
@@ -1885,7 +1875,7 @@
       }
       return;
     case device::MakeCredentialStatus::kAuthenticatorResponseInvalid:
-      req_state_->make_credential_reporting_outcome =
+      req_state_->request_outcome =
           MakeCredentialOutcome::kUnknownResponseFromAuthenticator;
       // The response from the authenticator was corrupted.
       CompleteMakeCredentialRequest(
@@ -1893,7 +1883,7 @@
           nullptr, Focus::kDoCheck);
       return;
     case device::MakeCredentialStatus::kHybridTransportError:
-      req_state_->make_credential_reporting_outcome =
+      req_state_->request_outcome =
           MakeCredentialOutcome::kHybridTransportError;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
@@ -1901,63 +1891,56 @@
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kEnclaveError:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kEnclaveError;
+      req_state_->request_outcome = MakeCredentialOutcome::kEnclaveError;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kEnclaveError,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kUserConsentDenied:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kUserCancellation;
+      req_state_->request_outcome = MakeCredentialOutcome::kUserCancellation;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kUserConsentDenied,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kSoftPINBlock:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kSoftPinBlock;
+      req_state_->request_outcome = MakeCredentialOutcome::kSoftPinBlock;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kSoftPINBlock,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kHardPINBlock:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kHardPinBlock;
+      req_state_->request_outcome = MakeCredentialOutcome::kHardPinBlock;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kHardPINBlock,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kAuthenticatorRemovedDuringPINEntry:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kOtherFailure;
+      req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kAuthenticatorRemovedDuringPINEntry,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kAuthenticatorMissingResidentKeys:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kRkNotSupported;
+      req_state_->request_outcome = MakeCredentialOutcome::kRkNotSupported;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kAuthenticatorMissingResidentKeys,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kAuthenticatorMissingUserVerification:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kUvNotSupported;
+      req_state_->request_outcome = MakeCredentialOutcome::kUvNotSupported;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kAuthenticatorMissingUserVerification,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kAuthenticatorMissingLargeBlob:
-      req_state_->make_credential_reporting_outcome =
+      req_state_->request_outcome =
           MakeCredentialOutcome::kLargeBlobNotSupported;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
@@ -1965,7 +1948,7 @@
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kNoCommonAlgorithms:
-      req_state_->make_credential_reporting_outcome =
+      req_state_->request_outcome =
           MakeCredentialOutcome::kAlgorithmNotSupported;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
@@ -1973,24 +1956,21 @@
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kStorageFull:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kStorageFull;
+      req_state_->request_outcome = MakeCredentialOutcome::kStorageFull;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kStorageFull,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kWinNotAllowedError:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kPlatformNotAllowed;
+      req_state_->request_outcome = MakeCredentialOutcome::kPlatformNotAllowed;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kWinUserCancelled,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::MakeCredentialStatus::kEnclaveCancel:
-      req_state_->make_credential_reporting_outcome =
-          MakeCredentialOutcome::kUserCancellation;
+      req_state_->request_outcome = MakeCredentialOutcome::kUserCancellation;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kEnclaveCancel,
@@ -2019,7 +1999,8 @@
   }
 
   const auto attestation =
-      req_state_->ctap_make_credential_request->attestation_preference;
+      absl::get<device::CtapMakeCredentialRequest>(req_state_->ctap_request)
+          .attestation_preference;
   std::optional<AttestationErasureOption> attestation_erasure;
 
   if (response_data->attestation_should_be_filtered &&
@@ -2137,13 +2118,13 @@
     return;
   }
 
-  req_state_->get_assertion_result = CredentialRequestResultFromCode(
+  req_state_->request_result = CredentialRequestResultFromCode(
       status_code == device::GetAssertionStatus::kSuccess,
       authenticator->GetType());
 
   switch (status_code) {
     case device::GetAssertionStatus::kUserConsentButCredentialNotRecognized:
-      req_state_->get_assertion_reporting_outcome =
+      req_state_->request_outcome =
           GetAssertionOutcome::kCredentialNotRecognized;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
@@ -2151,78 +2132,70 @@
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kAuthenticatorResponseInvalid:
-      req_state_->get_assertion_reporting_outcome =
+      req_state_->request_outcome =
           GetAssertionOutcome::kUnknownResponseFromAuthenticator;
       // The response from the authenticator was corrupted.
       CompleteGetAssertionRequest(
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kUserConsentDenied:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kUserCancellation;
+      req_state_->request_outcome = GetAssertionOutcome::kUserCancellation;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kUserConsentDenied,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kSoftPINBlock:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kSoftPinBlock;
+      req_state_->request_outcome = GetAssertionOutcome::kSoftPinBlock;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kSoftPINBlock,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kHardPINBlock:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kHardPinBlock;
+      req_state_->request_outcome = GetAssertionOutcome::kHardPinBlock;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kHardPINBlock,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kAuthenticatorRemovedDuringPINEntry:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kOtherFailure;
+      req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kAuthenticatorRemovedDuringPINEntry,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kAuthenticatorMissingResidentKeys:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kRkNotSupported;
+      req_state_->request_outcome = GetAssertionOutcome::kRkNotSupported;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kAuthenticatorMissingResidentKeys,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kAuthenticatorMissingUserVerification:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kUvNotSupported;
+      req_state_->request_outcome = GetAssertionOutcome::kUvNotSupported;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kAuthenticatorMissingUserVerification,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kWinNotAllowedError:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kPlatformNotAllowed;
+      req_state_->request_outcome = GetAssertionOutcome::kPlatformNotAllowed;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kWinUserCancelled,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kHybridTransportError:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kHybridTransportError;
+      req_state_->request_outcome = GetAssertionOutcome::kHybridTransportError;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kHybridTransportError,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kICloudKeychainNoCredentials:
-      req_state_->get_assertion_reporting_outcome =
+      req_state_->request_outcome =
           GetAssertionOutcome::kCredentialNotRecognized;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
@@ -2230,16 +2203,14 @@
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kEnclaveError:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kEnclaveError;
+      req_state_->request_outcome = GetAssertionOutcome::kEnclaveError;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kEnclaveError,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     case device::GetAssertionStatus::kEnclaveCancel:
-      req_state_->get_assertion_reporting_outcome =
-          GetAssertionOutcome::kUserCancellation;
+      req_state_->request_outcome = GetAssertionOutcome::kUserCancellation;
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
               kEnclaveCancel,
@@ -2342,14 +2313,12 @@
     return;
   }
 
-  if (req_state_->get_assertion_response_callback) {
-    req_state_->get_assertion_result = CredentialRequestResult::kTimeout;
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kUiTimeout;
+  req_state_->request_result = CredentialRequestResult::kTimeout;
+  if (absl::holds_alternative<GetAssertionCallback>(
+          req_state_->response_callback)) {
+    req_state_->request_outcome = GetAssertionOutcome::kUiTimeout;
   } else {
-    req_state_->make_credential_result = CredentialRequestResult::kTimeout;
-    req_state_->make_credential_reporting_outcome =
-        MakeCredentialOutcome::kUiTimeout;
+    req_state_->request_outcome = MakeCredentialOutcome::kUiTimeout;
   }
   SignalFailureToRequestDelegate(
       AuthenticatorRequestClientDelegate::InterestingFailureReason::kTimeout,
@@ -2374,28 +2343,33 @@
     proxy->CancelRequest(*req_state_->pending_proxied_request_id);
   }
 
-  DCHECK(!req_state_->make_credential_response_callback ||
-         !req_state_->get_assertion_response_callback);
-  if (req_state_->make_credential_response_callback) {
+  DCHECK(
+      !absl::holds_alternative<absl::monostate>(req_state_->response_callback));
+  if (absl::holds_alternative<MakeCredentialCallback>(
+          req_state_->response_callback) &&
+      absl::get<MakeCredentialCallback>(req_state_->response_callback)) {
     CompleteMakeCredentialRequest(status);
-  } else if (req_state_->get_assertion_response_callback) {
+  } else if (absl::holds_alternative<GetAssertionCallback>(
+                 req_state_->response_callback) &&
+             absl::get<GetAssertionCallback>(req_state_->response_callback)) {
     CompleteGetAssertionRequest(status);
   }
 }
 
 void AuthenticatorCommonImpl::OnCancelFromUI() {
-  if (!req_state_->get_assertion_result &&
-      req_state_->get_assertion_response_callback) {
+  if (!req_state_->request_result &&
+      absl::holds_alternative<GetAssertionCallback>(
+          req_state_->response_callback) &&
+      absl::get<GetAssertionCallback>(req_state_->response_callback)) {
     // The user cancelled before the request finished.
-    req_state_->get_assertion_result = CredentialRequestResult::kUserCancelled;
-    req_state_->get_assertion_reporting_outcome =
-        GetAssertionOutcome::kUserCancellation;
-  } else if (!req_state_->make_credential_result &&
-             req_state_->make_credential_response_callback) {
-    req_state_->make_credential_result =
-        CredentialRequestResult::kUserCancelled;
-    req_state_->make_credential_reporting_outcome =
-        MakeCredentialOutcome::kUserCancellation;
+    req_state_->request_result = CredentialRequestResult::kUserCancelled;
+    req_state_->request_outcome = GetAssertionOutcome::kUserCancellation;
+  } else if (!req_state_->request_result &&
+             absl::holds_alternative<MakeCredentialCallback>(
+                 req_state_->response_callback) &&
+             absl::get<MakeCredentialCallback>(req_state_->response_callback)) {
+    req_state_->request_result = CredentialRequestResult::kUserCancelled;
+    req_state_->request_outcome = MakeCredentialOutcome::kUserCancellation;
   }
   CancelWithStatus(req_state_->error_awaiting_user_acknowledgement);
 }
@@ -2567,17 +2541,22 @@
     blink::mojom::MakeCredentialAuthenticatorResponsePtr response,
     blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details,
     Focus check_focus) {
-  DCHECK(req_state_->make_credential_response_callback);
+  DCHECK(absl::holds_alternative<MakeCredentialCallback>(
+             req_state_->response_callback) &&
+         absl::get<MakeCredentialCallback>(req_state_->response_callback));
+  auto make_credential_response_callback = std::move(
+      absl::get<MakeCredentialCallback>(req_state_->response_callback));
 
-  if (req_state_->make_credential_result) {
+  if (req_state_->request_result) {
     UMA_HISTOGRAM_ENUMERATION("WebAuthentication.MakeCredential.Result",
-                              *req_state_->make_credential_result);
+                              *req_state_->request_result);
   }
 
-  if (req_state_->make_credential_reporting_outcome.has_value()) {
-    RecordRegisterOutcomeMetric(req_state_->mode,
-                                GetRenderFrameHost()->GetPageUkmSourceId(),
-                                *req_state_->make_credential_reporting_outcome);
+  if (absl::holds_alternative<MakeCredentialOutcome>(
+          req_state_->request_outcome)) {
+    RecordRegisterOutcomeMetric(
+        req_state_->mode, GetRenderFrameHost()->GetPageUkmSourceId(),
+        absl::get<MakeCredentialOutcome>(req_state_->request_outcome));
   } else if (status == blink::mojom::AuthenticatorStatus::SUCCESS) {
     RecordRegisterOutcomeMetric(req_state_->mode,
                                 GetRenderFrameHost()->GetPageUkmSourceId(),
@@ -2586,10 +2565,10 @@
 
   if (check_focus != Focus::kDontCheck &&
       !(req_state_->request_delegate && IsFocused())) {
-    std::move(req_state_->make_credential_response_callback)
+    std::move(make_credential_response_callback)
         .Run(blink::mojom::AuthenticatorStatus::NOT_FOCUSED, nullptr, nullptr);
   } else {
-    std::move(req_state_->make_credential_response_callback)
+    std::move(make_credential_response_callback)
         .Run(status, std::move(response), std::move(dom_exception_details));
   }
 
@@ -2688,17 +2667,22 @@
     blink::mojom::AuthenticatorStatus status,
     blink::mojom::GetAssertionAuthenticatorResponsePtr response,
     blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
-  DCHECK(req_state_->get_assertion_response_callback);
+  DCHECK(absl::holds_alternative<GetAssertionCallback>(
+             req_state_->response_callback) &&
+         absl::get<GetAssertionCallback>(req_state_->response_callback));
+  auto get_assertion_response_callback =
+      std::move(absl::get<GetAssertionCallback>(req_state_->response_callback));
 
-  if (req_state_->get_assertion_result) {
+  if (req_state_->request_result) {
     UMA_HISTOGRAM_ENUMERATION("WebAuthentication.GetAssertion.Result",
-                              *req_state_->get_assertion_result);
+                              *req_state_->request_result);
   }
 
-  if (req_state_->get_assertion_reporting_outcome.has_value()) {
-    RecordSignOutcomeMetric(req_state_->mode,
-                            GetRenderFrameHost()->GetPageUkmSourceId(),
-                            *req_state_->get_assertion_reporting_outcome);
+  if (absl::holds_alternative<GetAssertionOutcome>(
+          req_state_->request_outcome)) {
+    RecordSignOutcomeMetric(
+        req_state_->mode, GetRenderFrameHost()->GetPageUkmSourceId(),
+        absl::get<GetAssertionOutcome>(req_state_->request_outcome));
   } else if (status == blink::mojom::AuthenticatorStatus::SUCCESS) {
     RecordSignOutcomeMetric(req_state_->mode,
                             GetRenderFrameHost()->GetPageUkmSourceId(),
@@ -2710,7 +2694,7 @@
         ->WebAuthnAssertionRequestSucceeded();
   }
 
-  std::move(req_state_->get_assertion_response_callback)
+  std::move(get_assertion_response_callback)
       .Run(status, std::move(response), std::move(dom_exception_details));
   Cleanup();
 }
@@ -2718,8 +2702,10 @@
 void AuthenticatorCommonImpl::CompleteReportRequest(
     blink::mojom::AuthenticatorStatus status,
     blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
-  DCHECK(req_state_->report_response_callback);
-  std::move(req_state_->report_response_callback)
+  DCHECK(
+      absl::holds_alternative<ReportCallback>(req_state_->response_callback) &&
+      absl::get<ReportCallback>(req_state_->response_callback));
+  std::move(absl::get<ReportCallback>(req_state_->response_callback))
       .Run(status, std::move(dom_exception_details));
   Cleanup();
 }
@@ -2815,7 +2801,9 @@
     return;
   }
   DCHECK_EQ(*req_state_->pending_proxied_request_id, request_id);
-  DCHECK(req_state_->make_credential_response_callback);
+  DCHECK(absl::holds_alternative<MakeCredentialCallback>(
+             req_state_->response_callback) &&
+         absl::get<MakeCredentialCallback>(req_state_->response_callback));
   req_state_->pending_proxied_request_id.reset();
   if (error) {
     DCHECK(!response);
@@ -2837,7 +2825,8 @@
     return;
   }
   DCHECK_EQ(*req_state_->pending_proxied_request_id, request_id);
-  DCHECK(req_state_->get_assertion_response_callback);
+  DCHECK(absl::holds_alternative<GetAssertionCallback>(
+      req_state_->response_callback));
   req_state_->pending_proxied_request_id.reset();
   if (error) {
     DCHECK(!response);
diff --git a/content/browser/worker_host/dedicated_worker_host_factory_impl.cc b/content/browser/worker_host/dedicated_worker_host_factory_impl.cc
index 5cd40e25..f739b61 100644
--- a/content/browser/worker_host/dedicated_worker_host_factory_impl.cc
+++ b/content/browser/worker_host/dedicated_worker_host_factory_impl.cc
@@ -93,6 +93,28 @@
     std::move(callback).Run(
         creator_client_security_state_->cross_origin_embedder_policy,
         /*back_forward_cache_controller_host=*/mojo::NullRemote());
+    RenderFrameHostImpl* ancestor_render_frame_host =
+        RenderFrameHostImpl::FromID(ancestor_render_frame_host_id_);
+    SCOPED_CRASH_KEY_STRING32(
+        "", "is_primary_main_frame",
+        (ancestor_render_frame_host &&
+         ancestor_render_frame_host->IsInPrimaryMainFrame())
+            ? "true"
+            : "false");
+    SCOPED_CRASH_KEY_STRING256(
+        "", "lifecycle_state",
+        ancestor_render_frame_host
+            ? RenderFrameHostImpl::LifecycleStateImplToString(
+                  ancestor_render_frame_host->lifecycle_state())
+            : "no_rfh");
+    SCOPED_CRASH_KEY_STRING256(
+        "", "browser_origin",
+        ancestor_render_frame_host
+            ? ancestor_render_frame_host->GetLastCommittedOrigin()
+                  .GetDebugString()
+            : "");
+    SCOPED_CRASH_KEY_STRING256("", "renderer_origin",
+                               renderer_origin.GetDebugString());
     mojo::ReportBadMessage("DWH_INVALID_ORIGIN");
     return;
   }
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index d9482bc..e7aa37c 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -706,12 +706,13 @@
   }
 
   // In single process mode we may already have initialized the power monitor,
-  if (!base::PowerMonitor::IsInitialized()) {
+  if (auto* power_monitor = base::PowerMonitor::GetInstance();
+      !power_monitor->IsInitialized()) {
     auto power_monitor_source =
         std::make_unique<device::PowerMonitorBroadcastSource>(
             GetIOTaskRunner());
     auto* source_ptr = power_monitor_source.get();
-    base::PowerMonitor::Initialize(std::move(power_monitor_source));
+    power_monitor->Initialize(std::move(power_monitor_source));
     // The two-phase init is necessary to ensure that the process-wide
     // PowerMonitor is set before the power monitor source receives incoming
     // communication from the browser process (see https://crbug.com/821790 for
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index b088d9d..2f679f1 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -84,7 +84,7 @@
 
 viz::VizMainImpl::ExternalDependencies CreateVizMainDependencies() {
   viz::VizMainImpl::ExternalDependencies deps;
-  if (!base::PowerMonitor::IsInitialized()) {
+  if (!base::PowerMonitor::GetInstance()->IsInitialized()) {
     deps.power_monitor_source =
         std::make_unique<base::PowerMonitorDeviceSource>();
   }
diff --git a/content/public/browser/isolated_context_util.cc b/content/public/browser/isolated_context_util.cc
index 144c50a..c122090 100644
--- a/content/public/browser/isolated_context_util.cc
+++ b/content/public/browser/isolated_context_util.cc
@@ -24,16 +24,16 @@
 
 }  // namespace
 
-bool IsIsolatedContext(RenderProcessHost* process) {
-  return (process->GetWebExposedIsolationLevel() ==
-          WebExposedIsolationLevel::kIsolatedApplication) ||
-         IsIsolatedContextAllowedByEmbedder(process);
-}
-
 bool HasIsolatedContextCapability(RenderFrameHost* frame) {
   return (frame->GetWebExposedIsolationLevel() ==
           WebExposedIsolationLevel::kIsolatedApplication) ||
          IsIsolatedContextAllowedByEmbedder(frame->GetProcess());
 }
 
+bool IsIsolatedContext(RenderProcessHost* process) {
+  return (process->GetWebExposedIsolationLevel() ==
+          WebExposedIsolationLevel::kIsolatedApplication) ||
+         IsIsolatedContextAllowedByEmbedder(process);
+}
+
 }  // namespace content
diff --git a/content/public/browser/isolated_context_util.h b/content/public/browser/isolated_context_util.h
index 472fc25..f4dcf08 100644
--- a/content/public/browser/isolated_context_util.h
+++ b/content/public/browser/isolated_context_util.h
@@ -12,24 +12,33 @@
 class RenderFrameHost;
 class RenderProcessHost;
 
-// Whether the given frame is sufficiently isolated to have access
-// to interfaces intended only for isolated contexts.
-// See [IsolatedContext] IDL extended attribute for more details.
+// These functions check whether a frame or process is sufficiently isolated
+// to have access to interfaces intended only for isolated contexts.
+// See the isolated contexts spec:
+// https://wicg.github.io/isolated-web-apps/isolated-contexts.html
 // Isolated Web Apps Explainer:
 // https://github.com/WICG/isolated-web-apps/blob/main/README.md
 
-// Checks whether the given `process` fulfills the necessary requirements for
-// qualifying as an isolated context.
-CONTENT_EXPORT bool IsIsolatedContext(RenderProcessHost* process);
-
-// Checks whether the given `frame` fulfills the necessary requirements for
-// qualifying as an isolated context.
-// RenderFrameHost* could have a lower WebExposedIsolationLevel than its
-// RenderProcessHost* because of the cross-origin-isolated permissions policy;
-// that's why it's undesirable to delegate to `IsIsolatedContext()` via
-// `frame->GetProcess()`.
+// Checks whether `frame` meets the requirements for qualifying as an isolated
+// context, and is therefore allowed access to isolated context gated APIs.
+//
+// RenderFrameHost* could have a lower isolation level than its
+// RenderProcessHost* because of the cross-origin-isolated permissions policy.
+//
+// This should be used to check for API access instead of IsIsolatedContext
+// whenever possible.
 CONTENT_EXPORT bool HasIsolatedContextCapability(RenderFrameHost* frame);
 
+// Checks whether `process` meets the requirements for qualifying as an
+// isolated context.
+//
+// HasIsolatedContextCapability should be used to check for API access instead
+// of this function whenever possible. Shared/service workers should use this
+// function because they don't have a RenderFrameHost, so the additional
+// permissions policy check done by HasIsolatedContextCapability doesn't apply
+// to them (permissions policy applies to documents).
+CONTENT_EXPORT bool IsIsolatedContext(RenderProcessHost* process);
+
 }  // namespace content
 
 #endif  // CONTENT_PUBLIC_BROWSER_ISOLATED_CONTEXT_UTIL_H_
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index da6ca6a..7e3f570 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -497,10 +497,10 @@
   // RenderProcessHost::GetWebExposedIsolationLevel() when making decisions
   // based on the isolation level, such as API availability.
   //
-  // Note that this function doesn't account for API availability for certain
-  // documents and URLs that might be force-enabled by the embedder even if they
-  // lack the necessary privilege; in order for this matter to be taken into
-  // consideration, use content::HasIsolatedContextCapability(RenderFrameHost*).
+  // Note that the embedder can force-enable APIs in processes even if they
+  // lack the necessary privilege. This function doesn't account for that; use
+  // content::HasIsolatedContextCapability(RenderFrameHost*) to handle this
+  // case.
   //
   // TODO(https://936696): Once RenderDocument ships this should be exposed as
   // an invariant of the document host.
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index 918a7fc..f2267a2 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -684,18 +684,13 @@
 
   // Returns the cross-origin isolation mode used by content in this process.
   //
-  // This returns the kMaybe* enum values because it can't take Permissions
-  // Policy into account. A frame's isolation capability may be kNotIsolated
-  // even if it is running in a kMaybeIsolated process if the
-  // "cross-origin-isolated" feature was not delegated to the frame. Because
-  // of this, not all frames or workers in the same process will share the same
-  // isolation capability.
-  //
-  // Additionally, unlike WebExposedIsolationInfo, this is not guaranteed to be
-  // the same for all processes in a BrowsingInstance; content that is
-  // cross-origin to a kMaybeIsolatedApplication main frame will return
-  // kMaybeIsolated, as the application isolation level cannot be inherited
-  // cross-origin.
+  // Unlike WebExposedIsolationInfo, this is not guaranteed to be the same for
+  // all processes in a BrowsingInstance; frames that are not delegated the
+  // "cross-origin-isolated" permissions policy will have a kNotIsolated
+  // isolation level, even if their WebExposedIsolationInfo is isolated.
+  // Additionally, content that is cross-origin to a kIsolatedApplication main
+  // frame will return kIsolated, as the application isolation level cannot be
+  // inherited cross-origin.
   //
   // RenderFrameHost::GetWebExposedIsolationLevel() should typically be used
   // instead of this function if running in the context a frame so that
@@ -705,10 +700,9 @@
   // API to access isolation capability may need to be introduced which should
   // be used instead of this.
   //
-  // Note that this function doesn't account for API availability for certain
-  // documents and URLs that might be force-enabled by the embedder even if they
-  // lack the necessary privilege; in order for this matter to be taken into
-  // consideration, use content::IsIsolatedContext(RenderProcessHost*).
+  // Note that the embedder can force-enable APIs in frames even if they
+  // lack the necessary privilege. This function doesn't account for that;
+  // use content::IsIsolatedContext(RenderProcessHost*) to handle this case.
   WebExposedIsolationLevel GetWebExposedIsolationLevel();
 
   // Posts |task|, if this RenderProcessHost is ready or when it becomes ready
diff --git a/content/public/browser/web_exposed_isolation_level.h b/content/public/browser/web_exposed_isolation_level.h
index c2568e91..b9365c8 100644
--- a/content/public/browser/web_exposed_isolation_level.h
+++ b/content/public/browser/web_exposed_isolation_level.h
@@ -28,16 +28,18 @@
 //     workers only if they have opted in to being embedded by asserting CORS or
 //     CORP headers.
 //
-// 3.  The frame or worker may be an "isolated application", corresponding to a
-//     mostly TBD set of restrictions we're exploring in
-//     https://crbug.com/1206150, and which currently gate the set of APIs
-//     which specify [IsolatedContext] attributes.
+// 3.  The frame or worker may be an "isolated context", which provides
+//     additional isolation and integrity guarantees compared to cross-origin
+//     isolation. This isolation level grants access to APIs gated on the
+//     [IsolatedContext] IDL attribute in addition to [CrossOriginIsolated].
+//     Isolated contexts are specified in [3] below.
 //
 // The enum below is ordered from least-isolated to most-isolated.
 //
 // [1]
 // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/crossOriginIsolated
 // [2] https://w3c.github.io/webappsec-permissions-policy/
+// [3] https://wicg.github.io/isolated-web-apps/isolated-contexts.html
 //
 // NOTE: some of the information needed to fully determine a frame or worker's
 // isolation status is currently not available in the browser process.
@@ -57,7 +59,7 @@
   kIsolated,
 
   // The frame or worker is in a cross-origin isolated process and agent cluster
-  // that supports application isolation, allowing access to web platform APIs
+  // that is also an isolated context, allowing access to web platform APIs
   // gated on both [CrossOriginIsolated] and [IsolatedContext].
   kIsolatedApplication
 };
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 09d8da1..dc04593 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -504,6 +504,11 @@
 // (activated by kUserAgentClientHint)
 BASE_FEATURE(kGreaseUACH, "GreaseUACH", base::FEATURE_ENABLED_BY_DEFAULT);
 
+// See crbug.com/359623664
+BASE_FEATURE(kIdbPrioritizeForegroundClients,
+             "IdbPrioritizeForegroundClients",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Kill switch for the GetInstalledRelatedApps API.
 BASE_FEATURE(kInstalledApp, "InstalledApp", base::FEATURE_ENABLED_BY_DEFAULT);
 
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 78abffc..258d391 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -121,6 +121,7 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebIdentityDigitalCredentials);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFractionalScrollOffsets);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kGreaseUACH);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kIdbPrioritizeForegroundClients);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kInstalledApp);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kInstalledAppProvider);
 // LINT.IfChange
diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc
index bdc9112..79de678db 100644
--- a/content/public/common/content_switch_dependent_feature_overrides.cc
+++ b/content/public/common/content_switch_dependent_feature_overrides.cc
@@ -87,6 +87,9 @@
       {switches::kEnableExperimentalWebPlatformFeatures,
        std::cref(blink::features::kCSSDisplayModePictureInPicture),
        base::FeatureList::OVERRIDE_ENABLE_FEATURE},
+      {switches::kEnableExperimentalWebPlatformFeatures,
+       std::cref(blink::features::kPartitionedPopins),
+       base::FeatureList::OVERRIDE_ENABLE_FEATURE},
 
       // Overrides for --enable-experimental-cookie-features.
       {switches::kEnableExperimentalCookieFeatures,
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.cc b/content/renderer/webgraphicscontext3d_provider_impl.cc
index 4dbf27e..f9d8756d 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.cc
+++ b/content/renderer/webgraphicscontext3d_provider_impl.cc
@@ -15,7 +15,7 @@
 #include "gpu/config/gpu_feature_info.h"
 #include "media/renderers/paint_canvas_video_renderer.h"
 #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 namespace content {
 
diff --git a/content/shell/common/power_monitor_test_impl.cc b/content/shell/common/power_monitor_test_impl.cc
index 74631a2..ac72911 100644
--- a/content/shell/common/power_monitor_test_impl.cc
+++ b/content/shell/common/power_monitor_test_impl.cc
@@ -20,11 +20,11 @@
 }
 
 PowerMonitorTestImpl::PowerMonitorTestImpl() {
-  base::PowerMonitor::AddPowerStateObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerStateObserver(this);
 }
 
 PowerMonitorTestImpl::~PowerMonitorTestImpl() {
-  base::PowerMonitor::RemovePowerStateObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerStateObserver(this);
 }
 
 void PowerMonitorTestImpl::QueryNextState(QueryNextStateCallback callback) {
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 620346c..41fc6a5 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2501,6 +2501,7 @@
     "../browser/file_system_access/file_system_access_handle_base_unittest.cc",
     "../browser/file_system_access/file_system_access_lock_manager_unittest.cc",
     "../browser/file_system_access/file_system_access_manager_impl_unittest.cc",
+    "../browser/file_system_access/file_system_access_observer_observation_unittest.cc",
     "../browser/file_system_access/file_system_access_safe_move_helper_unittest.cc",
     "../browser/file_system_access/file_system_access_watch_scope_unittest.cc",
     "../browser/file_system_access/file_system_access_watcher_manager_unittest.cc",
@@ -2982,10 +2983,6 @@
         [ "../browser/media/web_app_system_media_controls_manager_unittest.cc" ]
   }
 
-  if (is_win || is_mac || is_linux || is_chromeos) {
-    sources += [ "../browser/file_system_access/file_system_access_observer_observation_unittest.cc" ]
-  }
-
   if (is_android || is_linux || is_chromeos || is_mac || is_win || is_fuchsia) {
     data = [
       "$root_out_dir/content_shell.pak",
diff --git a/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
index 1c5dddd8..a2f5239bb 100644
--- a/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index c3911e6..ab1e64e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
index 7bfcb502..6eedbfe 100644
--- a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index 141d3af7..144db64 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
@@ -138,12 +138,14 @@
 # Seems to kill the system UI/otherwise make the device unusable.
 crbug.com/1294065 [ android android-shield-android-tv ] GpuProcess_visibility [ Skip ]
 
+# SwiftShader is disabled on Mac M1 by default
+crbug.com/1378476 [ mac mac-arm64 ] GpuProcess_no_swiftshader_for_webgl_without_flags [ Skip ]
+
 ###################
 # Failures/Flakes #
 ###################
 # Non-"Skip" expectations go here to suppress regular flakes/failures.
 
-crbug.com/332592691 [ android android-moto-g-power-5g---2023 ] GpuProcess_webgpu_iframe_removed [ Failure ]
 
 
 #######################################################################
diff --git a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
index ff66777..8c1516e7 100644
--- a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
index c462590..4f9316c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 3bf5f58..cbcc233 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
@@ -497,11 +497,6 @@
 crbug.com/40935289 [ angle-vulkan arm fuchsia no-clang-coverage web-engine-shell ] Pixel_Video_VP9 [ Failure ]
 
 
-# Tab change tests failing to find foreground tabs
-crbug.com/338574386 [ angle-metal apple-angle-metal-renderer:-apple-m1 graphite-disabled mac passthrough ] Pixel_Canvas2DTabSwitch [ Failure ]
-crbug.com/338574386 [ angle-metal apple-angle-metal-renderer:-apple-m1 graphite-disabled mac passthrough ] Pixel_Canvas2DTabSwitch_SoftwareCompositing [ Failure ]
-crbug.com/338574386 [ angle-metal apple-angle-metal-renderer:-apple-m1 graphite-disabled mac passthrough ] Pixel_WebGLReadPixelsTabSwitch [ Failure ]
-crbug.com/338574386 [ angle-metal apple-angle-metal-renderer:-apple-m1 graphite-disabled mac passthrough ] Pixel_WebGLReadPixelsTabSwitch_SoftwareCompositing [ Failure ]
 
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
diff --git a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
index ff66777..8c1516e7 100644
--- a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
index 46d9239b..4dbfb26f 100644
--- a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index ce52f5f0..1ae02493 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index 58c50a0..bb40897 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index edacefb8..f764393b 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 8001e55..0b4528e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -5,7 +5,7 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac highsierra mojave catalina bigsur monterey ventura sonoma
+#         mac highsierra mojave catalina bigsur monterey ventura sonoma sequoia
 #         win win8 win10 win11 ]
 # Devices
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
@@ -466,7 +466,7 @@
 crbug.com/1175371 [ angle-opengles chromeos mesa_ge_21.0 passthrough target-cpu-64 ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
 crbug.com/1175371 [ angle-opengl display-server-x linux passthrough renderer-skia-gl ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
 crbug.com/1175371 [ angle-opengles display-server-wayland linux passthrough ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
-crbug.com/1175371 [ mac angle-metal ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
+crbug.com/1175371 [ amd-0x7340 angle-metal graphite-disabled mac-x86_64 no-clang-coverage release sonoma ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
 
 # Won't investigate failure on validating command decoder. Remove once it's unshipped.
 crbug.com/angleproject/5499 [ no-passthrough ] conformance/glsl/misc/shaders-with-name-conflicts.html [ Failure ]
@@ -666,8 +666,6 @@
 crbug.com/1288603 [ android android-pixel-6 passthrough angle-opengles ] conformance/extensions/angle-instanced-arrays-out-of-bounds.html [ Failure ]
 crbug.com/40236526 [ android android-pixel-6 no-passthrough ] conformance/rendering/blending.html [ Failure ]
 
-crbug.com/1495573 [ android-14 android-pixel-6 arm graphite-disabled passthrough renderer-skia-vulkan ] conformance/extensions/oes-texture-half-float-with-image.html [ RetryOnFailure ]
-crbug.com/1495573 [ android-pixel-6 arm graphite-disabled passthrough renderer-skia-vulkan ] conformance/extensions/oes-vertex-array-object.html [ RetryOnFailure ]
 
 ## Misc failures ##
 
diff --git a/content/test/gpu/validate_tag_consistency.py b/content/test/gpu/validate_tag_consistency.py
index 89ea13c4..8d9b70d 100755
--- a/content/test/gpu/validate_tag_consistency.py
+++ b/content/test/gpu/validate_tag_consistency.py
@@ -47,6 +47,7 @@
             'monterey',
             'ventura',
             'sonoma',
+            'sequoia',
         ],
         'win': [
             'win8',
diff --git a/content/utility/utility_main.cc b/content/utility/utility_main.cc
index 51c3f2ab..d3c0fbb 100644
--- a/content/utility/utility_main.cc
+++ b/content/utility/utility_main.cc
@@ -388,7 +388,7 @@
   // base::HighResolutionTimerManager here for future possible usage of high
   // resolution timer in service utility process.
   std::optional<base::HighResolutionTimerManager> hi_res_timer_manager;
-  if (base::PowerMonitor::IsInitialized()) {
+  if (base::PowerMonitor::GetInstance()->IsInitialized()) {
     hi_res_timer_manager.emplace();
   }
 
diff --git a/crypto/unexportable_key_metrics.cc b/crypto/unexportable_key_metrics.cc
index 75a14ceb..1c80ceb 100644
--- a/crypto/unexportable_key_metrics.cc
+++ b/crypto/unexportable_key_metrics.cc
@@ -188,6 +188,7 @@
   std::unique_ptr<UnexportableKeyProvider> provider =
       GetUnexportableKeyProvider(std::move(config));
   if (!provider) {
+    base::UmaHistogramEnumeration("Crypto.TPMSupportType", supported_algo);
     return;
   }
 
diff --git a/device/bluetooth/chromeos_platform_features.cc b/device/bluetooth/chromeos_platform_features.cc
index af912b13..2832ab9 100644
--- a/device/bluetooth/chromeos_platform_features.cc
+++ b/device/bluetooth/chromeos_platform_features.cc
@@ -16,7 +16,7 @@
 
 BASE_FEATURE(kBluetoothFlossTelephony,
              "BluetoothFlossTelephony",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kBluetoothBtsnoopInternals,
              "BluetoothBtsnoopInternals",
diff --git a/device/vr/public/mojom/xr_session.mojom b/device/vr/public/mojom/xr_session.mojom
index 18ed3a1..57867f0 100644
--- a/device/vr/public/mojom/xr_session.mojom
+++ b/device/vr/public/mojom/xr_session.mojom
@@ -152,6 +152,8 @@
     mojo_base.mojom.TimeDelta frame_data_time;
     // The duration for animation frame callback execution.
     mojo_base.mojom.TimeDelta page_animation_frame_time;
+    // The duration for rendering the frame.
+    mojo_base.mojom.TimeDelta submit_frame_time;
 };
 
 struct XrLogMessage {
diff --git a/docs/updater/dev_manual.md b/docs/updater/dev_manual.md
index 1b5b387..b977fa9 100644
--- a/docs/updater/dev_manual.md
+++ b/docs/updater/dev_manual.md
@@ -434,10 +434,29 @@
 * Add these `.sha1` files to your CL. Do not add the actual `.png` images to
 your CL.
 * Once the images are successfully uploaded via `upload_screenshots.py`, delete
-them from your local enlistment. However, if `upload_screenshots.py` encounters
-the following error:
-`ServiceException: 401 Anonymous caller does not have storage.objects.list access to the Google Cloud Storage bucket. Permission 'storage.objects.list' denied on resource (or it may not exist).`
-see crbug.com/1491876 for a resolution or workaround to upload the images.
+them from your local enlistment.
+
+However, if `upload_screenshots.py` encounters the following error, then the
+screenshots have to be manually uploaded to
+https://storage.cloud.google.com/chromium-translation-screenshots/.
+
+```
+ServiceException: 401 Anonymous caller does not have storage.objects.list
+access to the Google Cloud Storage bucket. Permission 'storage.objects.list'
+denied on resource (or it may not exist).
+```
+
+To manually upload each screenshot:
+* Get the `sha1` generated from the tool. So for example, for
+chrome/app/chromium_strings_grd/IDS_UNKNOWN_APPLICATION.png, the `sha1` is
+`085c88707d854787e0c1310d93b519e93d906592`.
+* Rename the `.png` file to the `sha1` hash name. So for example, rename
+`chrome/app/chromium_strings_grd/IDS_UNKNOWN_APPLICATION.png` to
+`chrome/app/chromium_strings_grd/085c88707d854787e0c1310d93b519e93d906592`.
+* upload
+`chrome/app/chromium_strings_grd/085c88707d854787e0c1310d93b519e93d906592` to
+https://storage.cloud.google.com/chromium-translation-screenshots/.
+* Edit the `content-type` from `application/octet-stream` to `image/png`.
 
 ## Troubleshooting
 
diff --git a/google_apis/gcm/engine/heartbeat_manager.cc b/google_apis/gcm/engine/heartbeat_manager.cc
index 0279aa51..93e84fe1 100644
--- a/google_apis/gcm/engine/heartbeat_manager.cc
+++ b/google_apis/gcm/engine/heartbeat_manager.cc
@@ -62,7 +62,7 @@
 
 HeartbeatManager::~HeartbeatManager() {
   // Stop listening for system suspend and resume events.
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 }
 
 void HeartbeatManager::Start(
@@ -74,7 +74,7 @@
   trigger_reconnect_callback_ = trigger_reconnect_callback;
 
   // Listen for system suspend and resume events.
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 
   // Calculated the heartbeat interval just before we start the timer.
   UpdateHeartbeatInterval();
@@ -90,7 +90,7 @@
   heartbeat_timer_->Stop();
   waiting_for_ack_ = false;
 
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 }
 
 void HeartbeatManager::OnHeartbeatAcked() {
diff --git a/gpu/command_buffer/client/raster_implementation_gles_unittest.cc b/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
index 5338188..2687c92 100644
--- a/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
+++ b/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
@@ -8,6 +8,7 @@
 #include <GLES2/gl2ext.h>
 #include <GLES2/gl2extchromium.h>
 #include <GLES3/gl3.h>
+
 #include <memory>
 #include <utility>
 #include <vector>
@@ -23,7 +24,7 @@
 #include "gpu/command_buffer/common/mailbox.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/color_space.h"
 
 using testing::_;
diff --git a/gpu/command_buffer/client/raster_interface.h b/gpu/command_buffer/client/raster_interface.h
index 78146d50..cdc67a6b 100644
--- a/gpu/command_buffer/client/raster_interface.h
+++ b/gpu/command_buffer/client/raster_interface.h
@@ -19,7 +19,7 @@
 #include "third_party/skia/include/core/SkPixmap.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 
 namespace cc {
 class DisplayItemList;
diff --git a/gpu/command_buffer/client/shared_image_interface.h b/gpu/command_buffer/client/shared_image_interface.h
index 80fd13e..27a4645 100644
--- a/gpu/command_buffer/client/shared_image_interface.h
+++ b/gpu/command_buffer/client/shared_image_interface.h
@@ -19,7 +19,7 @@
 #include "gpu/gpu_export.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/gpu_memory_buffer.h"
diff --git a/gpu/command_buffer/common/skia_utils.cc b/gpu/command_buffer/common/skia_utils.cc
index 91735c7a2..1254ed9 100644
--- a/gpu/command_buffer/common/skia_utils.cc
+++ b/gpu/command_buffer/common/skia_utils.cc
@@ -12,7 +12,7 @@
 #include "base/trace_event/process_memory_dump.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkTraceMemoryDump.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gl/trace_util.h"
 
 namespace gpu {
diff --git a/gpu/command_buffer/service/copy_shared_image_helper.cc b/gpu/command_buffer/service/copy_shared_image_helper.cc
index 5f63bd8..4e2943d 100644
--- a/gpu/command_buffer/service/copy_shared_image_helper.cc
+++ b/gpu/command_buffer/service/copy_shared_image_helper.cc
@@ -33,15 +33,15 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
-#include "third_party/skia/include/gpu/GrYUVABackendTextures.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrYUVABackendTextures.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "third_party/skia/include/gpu/graphite/Context.h"
 #include "third_party/skia/include/gpu/graphite/Image.h"
 #include "third_party/skia/include/gpu/graphite/Recorder.h"
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index e75890b..9ef0c17b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -32,7 +32,7 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/overlay_plane_data.h"
 #include "ui/gfx/overlay_priority_hint.h"
diff --git a/gpu/command_buffer/service/gr_shader_cache.cc b/gpu/command_buffer/service/gr_shader_cache.cc
index f4706e07..73a78783 100644
--- a/gpu/command_buffer/service/gr_shader_cache.cc
+++ b/gpu/command_buffer/service/gr_shader_cache.cc
@@ -20,7 +20,7 @@
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
 #include "gpu/config/gpu_finch_features.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 namespace gpu {
 namespace raster {
diff --git a/gpu/command_buffer/service/gr_shader_cache.h b/gpu/command_buffer/service/gr_shader_cache.h
index 56b35fd..60ab1b6 100644
--- a/gpu/command_buffer/service/gr_shader_cache.h
+++ b/gpu/command_buffer/service/gr_shader_cache.h
@@ -16,7 +16,7 @@
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "gpu/raster_export.h"
-#include "third_party/skia/include/gpu/GrContextOptions.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextOptions.h"
 
 class GrDirectContext;
 
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 4861e522..d1efc15 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -84,11 +84,11 @@
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
-#include "third_party/skia/include/gpu/GrYUVABackendTextures.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrYUVABackendTextures.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/graphite/Context.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
diff --git a/gpu/command_buffer/service/service_transfer_cache.cc b/gpu/command_buffer/service/service_transfer_cache.cc
index 97067d1..9eb4aa42 100644
--- a/gpu/command_buffer/service/service_transfer_cache.cc
+++ b/gpu/command_buffer/service/service_transfer_cache.cc
@@ -20,10 +20,10 @@
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "third_party/skia/include/core/SkImage.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gl/trace_util.h"
 
 namespace gpu {
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index c1eb3fdc8..bc3afe4 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -35,12 +35,12 @@
 #include "gpu/vulkan/buildflags.h"
 #include "skia/buildflags.h"
 #include "skia/ext/skia_trace_memory_dump_impl.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/mock/GrMockTypes.h"
 #include "third_party/skia/include/gpu/graphite/Context.h"
-#include "third_party/skia/include/gpu/mock/GrMockTypes.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_share_group.h"
diff --git a/gpu/command_buffer/service/shared_context_state.h b/gpu/command_buffer/service/shared_context_state.h
index 7eb2e04e..5fe4c27 100644
--- a/gpu/command_buffer/service/shared_context_state.h
+++ b/gpu/command_buffer/service/shared_context_state.h
@@ -34,7 +34,7 @@
 #include "gpu/vulkan/buildflags.h"
 #include "skia/buildflags.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gl/progress_reporter.h"
 
 #if BUILDFLAG(IS_WIN)
diff --git a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
index df77403f..210353c 100644
--- a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
@@ -28,9 +28,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/skia_conversions.h"
diff --git a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory_unittest.cc
index 1cf8dacb..be82739 100644
--- a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory_unittest.cc
@@ -23,7 +23,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_switches.h"
diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing.cc b/gpu/command_buffer/service/shared_image/compound_image_backing.cc
index b627ef7..9b7ff6e 100644
--- a/gpu/command_buffer/service/shared_image/compound_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/compound_image_backing.cc
@@ -29,8 +29,8 @@
 #include "third_party/skia/include/core/SkPixmap.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkSurfaceProps.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/graphite/BackendTexture.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gfx/buffer_format_util.h"
diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
index 39c7fd8..05a1793 100644
--- a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
@@ -19,8 +19,8 @@
 #include "gpu/ipc/common/surface_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkAlphaType.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/buffer_types.h"
 
 namespace gpu {
diff --git a/gpu/command_buffer/service/shared_image/dcomp_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/dcomp_image_backing_factory_unittest.cc
index e2b95385..e42fb72f 100644
--- a/gpu/command_buffer/service/shared_image/dcomp_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/dcomp_image_backing_factory_unittest.cc
@@ -20,8 +20,8 @@
 #include "third_party/skia/include/core/SkAlphaType.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/skia_conversions.h"
diff --git a/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc b/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc
index 26bd456..aeadf7f6 100644
--- a/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc
@@ -16,10 +16,10 @@
 #include "third_party/angle/include/EGL/eglext_angle.h"
 #include "third_party/skia/include/core/SkAlphaType.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/color_space_win.h"
 #include "ui/gl/debug_utils.h"
diff --git a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc
index 2444381..6c48a6c 100644
--- a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc
@@ -24,7 +24,7 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/skia_gl_image_representation.h"
 #include "third_party/skia/include/core/SkAlphaType.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/color_space_win.h"
diff --git a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.h b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.h
index 8466802..7caaf042 100644
--- a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.h
@@ -18,7 +18,7 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "third_party/skia/include/core/SkAlphaType.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gpu_fence.h"
diff --git a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.cc b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.cc
index 6d3e47e..fe1b6566 100644
--- a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.h"
+
 #include <memory>
 
 #include "base/memory/ptr_util.h"
@@ -13,8 +14,8 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrContextThreadSafeProxy.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextThreadSafeProxy.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gl/scoped_restore_texture.h"
 
diff --git a/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
index b2b34b4..c9ddaf8 100644
--- a/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
@@ -38,8 +38,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkImage.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gfx/buffer_format_util.h"
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
index 8439689..fa2a5a1 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
@@ -43,13 +43,13 @@
 #include "third_party/skia/include/core/SkAlphaType.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkPixmap.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
 #include "third_party/skia/include/gpu/MutableTextureState.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
-#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
 #include "third_party/skia/include/gpu/vk/VulkanMutableTextureState.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gfx/buffer_format_util.h"
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
index 7d446cec..6d36407c6 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
@@ -33,7 +33,7 @@
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gl/buildflags.h"
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_dawn_representation.cc b/gpu/command_buffer/service/shared_image/external_vk_image_dawn_representation.cc
index 45b8c2f..b06a238 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_dawn_representation.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_dawn_representation.cc
@@ -12,10 +12,10 @@
 #include "base/posix/eintr_wrapper.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "gpu/vulkan/vulkan_image.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/MutableTextureState.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
-#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
 #include "third_party/skia/include/gpu/vk/VulkanMutableTextureState.h"
 
 namespace gpu {
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc b/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc
index 181d9ee..df2c43c 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc
@@ -15,13 +15,13 @@
 #include "third_party/skia/include/core/SkColorType.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkSurfaceProps.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
 #include "third_party/skia/include/gpu/MutableTextureState.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
-#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
 #include "third_party/skia/include/gpu/vk/VulkanMutableTextureState.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_holder.cc b/gpu/command_buffer/service/shared_image/gl_texture_holder.cc
index 6c3951e3..19517b1 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_holder.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_holder.cc
@@ -10,7 +10,7 @@
 #include "gpu/command_buffer/service/shared_image/gl_repack_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_gl_utils.h"
 #include "gpu/command_buffer/service/skia_utils.h"
-#include "third_party/skia/include/gpu/GrContextThreadSafeProxy.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextThreadSafeProxy.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_version_info.h"
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
index f730e2c..78137047 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
@@ -29,8 +29,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/gpu_memory_buffer.h"
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
index aa19622..b049387 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
@@ -33,7 +33,7 @@
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
-#include "third_party/skia/include/gpu/GrContextThreadSafeProxy.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextThreadSafeProxy.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/graphite/Recorder.h"
 #include "third_party/skia/include/gpu/graphite/Surface.h"
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
index ed5f6c5..ae47dc8 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
@@ -37,8 +37,8 @@
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gl/buildflags.h"
diff --git a/gpu/command_buffer/service/shared_image/ozone_image_backing.cc b/gpu/command_buffer/service/shared_image/ozone_image_backing.cc
index 8ef214f..68093e6 100644
--- a/gpu/command_buffer/service/shared_image/ozone_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/ozone_image_backing.cc
@@ -29,7 +29,7 @@
 #include "gpu/command_buffer/service/shared_memory_region_wrapper.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/config/gpu_finch_features.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/buffer_types.h"
diff --git a/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc b/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc
index 00332a18..f1b3cd07 100644
--- a/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc
@@ -16,7 +16,7 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 
diff --git a/gpu/command_buffer/service/shared_image/raw_draw_image_backing.h b/gpu/command_buffer/service/shared_image/raw_draw_image_backing.h
index 607da90d1..00456a94 100644
--- a/gpu/command_buffer/service/shared_image/raw_draw_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/raw_draw_image_backing.h
@@ -14,7 +14,7 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkSurfaceProps.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 
 class GrPromiseImageTexture;
 
diff --git a/gpu/command_buffer/service/shared_image/shared_image_backing.h b/gpu/command_buffer/service/shared_image/shared_image_backing.h
index e6be661..11dd91d 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_backing.h
@@ -27,7 +27,7 @@
 #include "gpu/vulkan/buildflags.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkPixmap.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
diff --git a/gpu/command_buffer/service/shared_image/shared_image_backing_factory.h b/gpu/command_buffer/service/shared_image/shared_image_backing_factory.h
index d95e4d30..6f53d5db 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_backing_factory.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_backing_factory.h
@@ -17,7 +17,7 @@
 #include "gpu/gpu_gles2_export.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
diff --git a/gpu/command_buffer/service/shared_image/shared_image_representation.cc b/gpu/command_buffer/service/shared_image/shared_image_representation.cc
index 47b21893..ec5a309 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_representation.cc
@@ -12,9 +12,9 @@
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImage.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrYUVABackendTextures.h"
 #include "third_party/skia/include/gpu/MutableTextureState.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrYUVABackendTextures.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/graphite/Image.h"
 #include "third_party/skia/include/gpu/graphite/YUVABackendTextures.h"
diff --git a/gpu/command_buffer/service/shared_image/shared_image_representation.h b/gpu/command_buffer/service/shared_image/shared_image_representation.h
index 9bb4bc1c..c4e0c53 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_representation.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_representation.h
@@ -24,7 +24,7 @@
 #include "skia/buildflags.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/graphite/BackendTexture.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
diff --git a/gpu/command_buffer/service/shared_image/shared_image_representation_unittest.cc b/gpu/command_buffer/service/shared_image/shared_image_representation_unittest.cc
index 342ad094..4e40544 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_representation_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_representation_unittest.cc
@@ -13,7 +13,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/dawn/include/dawn/dawn_proc.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/service/shared_image/shared_image_test_base.cc b/gpu/command_buffer/service/shared_image/shared_image_test_base.cc
index 6dbe1641..5b1dcee 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_test_base.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_test_base.cc
@@ -14,7 +14,7 @@
 #include "gpu/command_buffer/service/shared_image/copy_image_plane.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "third_party/skia/include/core/SkImage.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/graphite/Context.h"
 #include "third_party/skia/include/gpu/graphite/Image.h"
diff --git a/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc b/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc
index 8b282af..49aec927 100644
--- a/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc
@@ -12,8 +12,8 @@
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrContextThreadSafeProxy.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextThreadSafeProxy.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gl/gl_bindings.h"
diff --git a/gpu/command_buffer/service/shared_image/skia_vk_android_image_representation.cc b/gpu/command_buffer/service/shared_image/skia_vk_android_image_representation.cc
index b9fbdd8..1faa915b 100644
--- a/gpu/command_buffer/service/shared_image/skia_vk_android_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/skia_vk_android_image_representation.cc
@@ -23,9 +23,9 @@
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkColorType.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/MutableTextureState.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSemaphore.h"
 #include "third_party/skia/include/gpu/vk/VulkanMutableTextureState.h"
diff --git a/gpu/command_buffer/service/shared_image/skia_vk_ozone_image_representation.cc b/gpu/command_buffer/service/shared_image/skia_vk_ozone_image_representation.cc
index 55e1a19e..98013286 100644
--- a/gpu/command_buffer/service/shared_image/skia_vk_ozone_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/skia_vk_ozone_image_representation.cc
@@ -24,9 +24,9 @@
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkColorType.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/MutableTextureState.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
diff --git a/gpu/command_buffer/service/shared_image/test_image_backing.cc b/gpu/command_buffer/service/shared_image/test_image_backing.cc
index c2a7467..c0f67c3 100644
--- a/gpu/command_buffer/service/shared_image/test_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/test_image_backing.cc
@@ -3,16 +3,17 @@
 // found in the LICENSE file.
 
 #include "gpu/command_buffer/service/shared_image/test_image_backing.h"
+
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "skia/ext/legacy_display_globals.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
-#include "third_party/skia/include/gpu/mock/GrMockTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/mock/GrMockTypes.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 
 namespace gpu {
diff --git a/gpu/command_buffer/service/shared_image/test_utils.cc b/gpu/command_buffer/service/shared_image/test_utils.cc
index e906dc64..bfb6e1c 100644
--- a/gpu/command_buffer/service/shared_image/test_utils.cc
+++ b/gpu/command_buffer/service/shared_image/test_utils.cc
@@ -11,7 +11,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImage.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gfx/geometry/size.h"
diff --git a/gpu/command_buffer/service/shared_image/texture_holder_vk.h b/gpu/command_buffer/service/shared_image/texture_holder_vk.h
index 45421a1e..c38a3d59 100644
--- a/gpu/command_buffer/service/shared_image/texture_holder_vk.h
+++ b/gpu/command_buffer/service/shared_image/texture_holder_vk.h
@@ -7,8 +7,8 @@
 
 #include <memory>
 
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 
 namespace gfx {
diff --git a/gpu/command_buffer/service/shared_image/video_image_reader_image_backing.cc b/gpu/command_buffer/service/shared_image/video_image_reader_image_backing.cc
index f408d87..6430f04 100644
--- a/gpu/command_buffer/service/shared_image/video_image_reader_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/video_image_reader_image_backing.cc
@@ -31,8 +31,8 @@
 #include "gpu/vulkan/vulkan_image.h"
 #include "gpu/vulkan/vulkan_implementation.h"
 #include "gpu/vulkan/vulkan_util.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 #include "ui/gl/android/egl_fence_utils.h"
diff --git a/gpu/command_buffer/service/shared_image/video_surface_texture_image_backing.cc b/gpu/command_buffer/service/shared_image/video_surface_texture_image_backing.cc
index 7e366588..4ef41fb 100644
--- a/gpu/command_buffer/service/shared_image/video_surface_texture_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/video_surface_texture_image_backing.cc
@@ -17,8 +17,8 @@
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "gpu/command_buffer/service/texture_owner.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "ui/gl/gl_utils.h"
 
 namespace gpu {
diff --git a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.h b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.h
index da129a9..335deaac 100644
--- a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.h
@@ -17,7 +17,7 @@
 #include "third_party/skia/include/core/SkColorType.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSurfaceProps.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 
 class SkSurface;
diff --git a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
index c7f8afed..050fe39 100644
--- a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
@@ -22,8 +22,8 @@
 #include "third_party/skia/include/core/SkColorType.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkTextureCompressionType.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 
 namespace gpu {
 namespace {
diff --git a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc
index fbc3927..4c5e460 100644
--- a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc
@@ -25,7 +25,7 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkColorType.h"
 #include "third_party/skia/include/core/SkImage.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 
diff --git a/gpu/command_buffer/service/skia_utils.cc b/gpu/command_buffer/service/skia_utils.cc
index 9006985..95759f73 100644
--- a/gpu/command_buffer/service/skia_utils.cc
+++ b/gpu/command_buffer/service/skia_utils.cc
@@ -19,12 +19,12 @@
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkTextureCompressionType.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrContextThreadSafeProxy.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextThreadSafeProxy.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
 #include "third_party/skia/include/gpu/graphite/Context.h"
 #include "third_party/skia/include/gpu/graphite/GraphiteTypes.h"
 #include "third_party/skia/include/gpu/graphite/Recorder.h"
diff --git a/gpu/command_buffer/service/skia_utils.h b/gpu/command_buffer/service/skia_utils.h
index d69da830..f419a0c 100644
--- a/gpu/command_buffer/service/skia_utils.h
+++ b/gpu/command_buffer/service/skia_utils.h
@@ -15,12 +15,12 @@
 #include "gpu/vulkan/buildflags.h"
 #include "skia/buildflags.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrContextOptions.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextOptions.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "third_party/skia/include/gpu/graphite/ContextOptions.h"
 
 #if BUILDFLAG(ENABLE_VULKAN)
-#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
 namespace skgpu {
 struct VulkanYcbcrConversionInfo;
 }
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index ae844b5..d9740a2f 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -64,7 +64,7 @@
 #include "third_party/abseil-cpp/absl/base/attributes.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/graphite/Context.h"
 #include "ui/gl/gl_context_egl.h"
@@ -1628,8 +1628,9 @@
     case WebGPUPowerPreference::kNone:
       if (power_preference == wgpu::PowerPreference::Undefined) {
         // If on battery power, default to the integrated GPU.
-        if (!base::PowerMonitor::IsInitialized() ||
-            base::PowerMonitor::IsOnBatteryPower()) {
+        if (auto* power_monitor = base::PowerMonitor::GetInstance();
+            !power_monitor->IsInitialized() ||
+            power_monitor->IsOnBatteryPower()) {
           power_preference = wgpu::PowerPreference::LowPower;
         } else {
           power_preference = wgpu::PowerPreference::HighPerformance;
diff --git a/gpu/ipc/service/dcomp_texture_win.cc b/gpu/ipc/service/dcomp_texture_win.cc
index 4e7c2b1..c0ef8f4c 100644
--- a/gpu/ipc/service/dcomp_texture_win.cc
+++ b/gpu/ipc/service/dcomp_texture_win.cc
@@ -127,7 +127,7 @@
   IPC::ScopedAllowOffSequenceChannelAssociatedBindings allow_binding;
   receiver_.Bind(std::move(receiver), runner);
   context_state_->AddContextLostObserver(this);
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
   channel_->AddRoute(route_id, sequence_);
 }
 
@@ -138,7 +138,7 @@
   DCHECK(!channel_);
 
   context_state_->RemoveContextLostObserver(this);
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 
   if (window_pos_timer_.IsRunning()) {
     window_pos_timer_.Stop();
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index d8d9e3d..f190c87 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -42,8 +42,8 @@
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
 #include "third_party/skia/include/core/SkGraphics.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_enums.h"
 #include "ui/gl/gl_features.h"
diff --git a/gpu/ipc/service/gpu_watchdog_thread.cc b/gpu/ipc/service/gpu_watchdog_thread.cc
index 8740bf3..10a863b 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread.cc
@@ -127,7 +127,7 @@
   Stop();  // stop the watchdog thread
 
   base::CurrentThread::Get()->RemoveTaskObserver(this);
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
   GpuWatchdogThreadEventHistogram(GpuWatchdogThreadEvent::kGpuWatchdogEnd);
 #if BUILDFLAG(IS_WIN)
   if (watched_thread_handle_)
@@ -343,7 +343,8 @@
   // Adding the Observer to the power monitor is safe even if power monitor is
   // not yet initialized.
   bool is_system_suspended =
-      base::PowerMonitor::AddPowerSuspendObserverAndReturnSuspendedState(this);
+      base::PowerMonitor::GetInstance()
+          ->AddPowerSuspendObserverAndReturnSuspendedState(this);
   if (is_system_suspended)
     StopWatchdogTimeoutTask(kPowerSuspendResume);
 }
diff --git a/gpu/ipc/service/image_decode_accelerator_stub.cc b/gpu/ipc/service/image_decode_accelerator_stub.cc
index ba53cfe..20172f2 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub.cc
@@ -51,9 +51,9 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/color_space.h"
diff --git a/gpu/skia_bindings/gl_bindings_skia_cmd_buffer.cc b/gpu/skia_bindings/gl_bindings_skia_cmd_buffer.cc
index e3890212..ff969a7 100644
--- a/gpu/skia_bindings/gl_bindings_skia_cmd_buffer.cc
+++ b/gpu/skia_bindings/gl_bindings_skia_cmd_buffer.cc
@@ -8,7 +8,7 @@
 #include "base/memory/raw_ptr.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 
 using gpu::gles2::GLES2Interface;
 using gpu::ContextSupport;
diff --git a/gpu/skia_bindings/gles2_implementation_with_grcontext_support.cc b/gpu/skia_bindings/gles2_implementation_with_grcontext_support.cc
index e6a776e8..2ad00f1 100644
--- a/gpu/skia_bindings/gles2_implementation_with_grcontext_support.cc
+++ b/gpu/skia_bindings/gles2_implementation_with_grcontext_support.cc
@@ -8,7 +8,7 @@
 
 #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 
 namespace skia_bindings {
 
diff --git a/gpu/skia_bindings/grcontext_for_gles2_interface.cc b/gpu/skia_bindings/grcontext_for_gles2_interface.cc
index 3b9185c7f..f4331e8 100644
--- a/gpu/skia_bindings/grcontext_for_gles2_interface.cc
+++ b/gpu/skia_bindings/grcontext_for_gles2_interface.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 #include <string.h>
+
 #include <utility>
 
 #include "base/lazy_instance.h"
@@ -15,10 +16,10 @@
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/skia_bindings/gl_bindings_skia_cmd_buffer.h"
 #include "gpu/skia_bindings/gles2_implementation_with_grcontext_support.h"
-#include "third_party/skia/include/gpu/GrContextOptions.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextOptions.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 
 namespace skia_bindings {
 
diff --git a/gpu/skia_bindings/grcontext_for_gles2_interface.h b/gpu/skia_bindings/grcontext_for_gles2_interface.h
index 7adeffde..70a36040 100644
--- a/gpu/skia_bindings/grcontext_for_gles2_interface.h
+++ b/gpu/skia_bindings/grcontext_for_gles2_interface.h
@@ -7,7 +7,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/gpu/GrContextOptions.h"
+#include "third_party/skia/include/gpu/ganesh/GrContextOptions.h"
 
 class GrDirectContext;
 
diff --git a/infra/config/generated/builder-owners/chrome-skia-graphite@google.com.txt b/infra/config/generated/builder-owners/chrome-skia-graphite@google.com.txt
index 3674901..4f5994c 100644
--- a/infra/config/generated/builder-owners/chrome-skia-graphite@google.com.txt
+++ b/infra/config/generated/builder-owners/chrome-skia-graphite@google.com.txt
@@ -1,2 +1,2 @@
-ci/mac13-skia-alt-arm64-rel-tests
-try/mac13-skia-alt-arm64-blink-rel
\ No newline at end of file
+ci/mac-skia-alt-arm64-rel-tests
+try/mac-skia-alt-arm64-blink-rel
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/mac-arm64-rel/properties.json b/infra/config/generated/builders/ci/mac-arm64-rel/properties.json
index 280f970f..eb23e8c 100644
--- a/infra/config/generated/builders/ci/mac-arm64-rel/properties.json
+++ b/infra/config/generated/builders/ci/mac-arm64-rel/properties.json
@@ -64,6 +64,35 @@
           {
             "builder_id": {
               "bucket": "ci",
+              "builder": "mac-skia-alt-arm64-rel-tests",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.mac",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "mac"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "mac-arm64-rel",
+                "project": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
               "builder": "mac11-arm64-rel-tests",
               "project": "chromium"
             },
@@ -151,35 +180,6 @@
           {
             "builder_id": {
               "bucket": "ci",
-              "builder": "mac13-skia-alt-arm64-rel-tests",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "builder_group": "chromium.mac",
-              "execution_mode": "TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "chromium",
-                "target_arch": "arm",
-                "target_bits": 64,
-                "target_platform": "mac"
-              },
-              "legacy_gclient_config": {
-                "config": "chromium"
-              },
-              "parent": {
-                "bucket": "ci",
-                "builder": "mac-arm64-rel",
-                "project": "chromium"
-              }
-            }
-          },
-          {
-            "builder_id": {
-              "bucket": "ci",
               "builder": "mac14-arm64-rel-tests",
               "project": "chromium"
             },
@@ -223,6 +223,11 @@
         },
         {
           "bucket": "ci",
+          "builder": "mac-skia-alt-arm64-rel-tests",
+          "project": "chromium"
+        },
+        {
+          "bucket": "ci",
           "builder": "mac11-arm64-rel-tests",
           "project": "chromium"
         },
@@ -238,18 +243,13 @@
         },
         {
           "bucket": "ci",
-          "builder": "mac13-skia-alt-arm64-rel-tests",
-          "project": "chromium"
-        },
-        {
-          "bucket": "ci",
           "builder": "mac14-arm64-rel-tests",
           "project": "chromium"
         }
       ],
       "mirroring_builder_group_and_names": [
         {
-          "builder": "mac13-skia-alt-arm64-blink-rel",
+          "builder": "mac-skia-alt-arm64-blink-rel",
           "group": "tryserver.blink"
         },
         {
diff --git a/infra/config/generated/builders/ci/mac-skia-alt-arm64-rel-tests/properties.json b/infra/config/generated/builders/ci/mac-skia-alt-arm64-rel-tests/properties.json
new file mode 100644
index 0000000..1336d41
--- /dev/null
+++ b/infra/config/generated/builders/ci/mac-skia-alt-arm64-rel-tests/properties.json
@@ -0,0 +1,89 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "mac-arm64-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.mac",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "mac"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "chromium_with_telemetry_dependencies"
+                ],
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "mac-skia-alt-arm64-rel-tests",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.mac",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "mac"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "mac-arm64-rel",
+                "project": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "mac-skia-alt-arm64-rel-tests",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "mac-skia-alt-arm64-blink-rel",
+          "group": "tryserver.blink"
+        }
+      ],
+      "retry_failed_shards": true
+    }
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.mac",
+  "recipe": "chromium"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/mac13-skia-alt-arm64-rel-tests/properties.json b/infra/config/generated/builders/ci/mac13-skia-alt-arm64-rel-tests/properties.json
deleted file mode 100644
index c643442..0000000
--- a/infra/config/generated/builders/ci/mac13-skia-alt-arm64-rel-tests/properties.json
+++ /dev/null
@@ -1,89 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "mac-arm64-rel",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "builder_group": "chromium.mac",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "chromium",
-                "target_arch": "arm",
-                "target_bits": 64,
-                "target_platform": "mac"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "chromium_with_telemetry_dependencies"
-                ],
-                "config": "chromium"
-              }
-            }
-          },
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "mac13-skia-alt-arm64-rel-tests",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "builder_group": "chromium.mac",
-              "execution_mode": "TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "chromium",
-                "target_arch": "arm",
-                "target_bits": 64,
-                "target_platform": "mac"
-              },
-              "legacy_gclient_config": {
-                "config": "chromium"
-              },
-              "parent": {
-                "bucket": "ci",
-                "builder": "mac-arm64-rel",
-                "project": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "mac13-skia-alt-arm64-rel-tests",
-          "project": "chromium"
-        }
-      ],
-      "mirroring_builder_group_and_names": [
-        {
-          "builder": "mac13-skia-alt-arm64-blink-rel",
-          "group": "tryserver.blink"
-        }
-      ],
-      "retry_failed_shards": true
-    }
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.mac",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index 8161ba3..e229367 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -523,12 +523,12 @@
   "tryserver.blink": {
     "linux-blink-rel": "try/linux-blink-rel/gn-args.json",
     "linux-wpt-chromium-rel": "try/linux-wpt-chromium-rel/gn-args.json",
+    "mac-skia-alt-arm64-blink-rel": "try/mac-skia-alt-arm64-blink-rel/gn-args.json",
     "mac11.0-blink-rel": "try/mac11.0-blink-rel/gn-args.json",
     "mac11.0.arm64-blink-rel": "try/mac11.0.arm64-blink-rel/gn-args.json",
     "mac12.0-blink-rel": "try/mac12.0-blink-rel/gn-args.json",
     "mac12.0.arm64-blink-rel": "try/mac12.0.arm64-blink-rel/gn-args.json",
     "mac13-blink-rel": "try/mac13-blink-rel/gn-args.json",
-    "mac13-skia-alt-arm64-blink-rel": "try/mac13-skia-alt-arm64-blink-rel/gn-args.json",
     "mac13-wpt-chromium-rel": "try/mac13-wpt-chromium-rel/gn-args.json",
     "mac13.arm64-blink-rel": "try/mac13.arm64-blink-rel/gn-args.json",
     "mac14-blink-rel": "try/mac14-blink-rel/gn-args.json",
diff --git a/infra/config/generated/builders/try/mac13-skia-alt-arm64-blink-rel/gn-args.json b/infra/config/generated/builders/try/mac-skia-alt-arm64-blink-rel/gn-args.json
similarity index 100%
rename from infra/config/generated/builders/try/mac13-skia-alt-arm64-blink-rel/gn-args.json
rename to infra/config/generated/builders/try/mac-skia-alt-arm64-blink-rel/gn-args.json
diff --git a/infra/config/generated/builders/try/mac-skia-alt-arm64-blink-rel/properties.json b/infra/config/generated/builders/try/mac-skia-alt-arm64-blink-rel/properties.json
new file mode 100644
index 0000000..e2ab881
--- /dev/null
+++ b/infra/config/generated/builders/try/mac-skia-alt-arm64-blink-rel/properties.json
@@ -0,0 +1,107 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/try/mac-skia-alt-arm64-blink-rel/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "mac-arm64-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.mac",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "mac"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "chromium_with_telemetry_dependencies"
+                ],
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "mac-skia-alt-arm64-rel-tests",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.mac",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "mac"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "mac-arm64-rel",
+                "project": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "mac-arm64-rel",
+          "project": "chromium"
+        }
+      ],
+      "builder_ids_in_scope_for_testing": [
+        {
+          "bucket": "ci",
+          "builder": "mac-skia-alt-arm64-rel-tests",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-untrusted",
+    "remote_jobs": 150
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.blink",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/mac13-skia-alt-arm64-blink-rel/properties.json b/infra/config/generated/builders/try/mac13-skia-alt-arm64-blink-rel/properties.json
deleted file mode 100644
index e372c9d..0000000
--- a/infra/config/generated/builders/try/mac13-skia-alt-arm64-blink-rel/properties.json
+++ /dev/null
@@ -1,107 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/try/mac13-skia-alt-arm64-blink-rel/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "mac-arm64-rel",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "builder_group": "chromium.mac",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "chromium",
-                "target_arch": "arm",
-                "target_bits": 64,
-                "target_platform": "mac"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "chromium_with_telemetry_dependencies"
-                ],
-                "config": "chromium"
-              }
-            }
-          },
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "mac13-skia-alt-arm64-rel-tests",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "builder_group": "chromium.mac",
-              "execution_mode": "TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "chromium",
-                "target_arch": "arm",
-                "target_bits": 64,
-                "target_platform": "mac"
-              },
-              "legacy_gclient_config": {
-                "config": "chromium"
-              },
-              "parent": {
-                "bucket": "ci",
-                "builder": "mac-arm64-rel",
-                "project": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "mac-arm64-rel",
-          "project": "chromium"
-        }
-      ],
-      "builder_ids_in_scope_for_testing": [
-        {
-          "bucket": "ci",
-          "builder": "mac13-skia-alt-arm64-rel-tests",
-          "project": "chromium"
-        }
-      ]
-    }
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-untrusted",
-    "metrics_project": "chromium-reclient-metrics",
-    "scandeps_server": true
-  },
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "project": "rbe-chromium-untrusted",
-    "remote_jobs": 150
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.blink",
-  "recipe": "chromium_trybot"
-}
\ No newline at end of file
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index cae96c35..6f6c903 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -11345,6 +11345,27 @@
           }
         ]
       },
+      "mac-skia-alt-arm64-rel-tests": {
+        "contact_team_email": "chrome-skia-graphite@google.com",
+        "problem_specs": [
+          {
+            "name": "Unhealthy",
+            "period_days": 7,
+            "score": 5,
+            "thresholds": {
+              "_default": "_default"
+            }
+          },
+          {
+            "name": "Low Value",
+            "period_days": 90,
+            "score": 1,
+            "thresholds": {
+              "_default": "_default"
+            }
+          }
+        ]
+      },
       "mac-swangle-chromium-x64": {
         "contact_team_email": "chrome-gpu-infra@google.com",
         "problem_specs": [
@@ -11865,27 +11886,6 @@
           }
         ]
       },
-      "mac13-skia-alt-arm64-rel-tests": {
-        "contact_team_email": "chrome-skia-graphite@google.com",
-        "problem_specs": [
-          {
-            "name": "Unhealthy",
-            "period_days": 7,
-            "score": 5,
-            "thresholds": {
-              "_default": "_default"
-            }
-          },
-          {
-            "name": "Low Value",
-            "period_days": 90,
-            "score": 1,
-            "thresholds": {
-              "_default": "_default"
-            }
-          }
-        ]
-      },
       "mac13-wpt-chromium-rel": {
         "contact_team_email": "chrome-blink-engprod@google.com",
         "problem_specs": [
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index f53e586d..0cdf17a 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -5199,6 +5199,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/mac-skia-alt-arm64-blink-rel"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/mac-swangle-chromium-try-x64"
         includable_only: true
       }
@@ -5305,10 +5309,6 @@
         includable_only: true
       }
       builders {
-        name: "chromium/try/mac13-skia-alt-arm64-blink-rel"
-        includable_only: true
-      }
-      builders {
         name: "chromium/try/mac13-tests"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 2365aae..b2c3d2fb 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -34040,6 +34040,7 @@
         '      "tools/android/avd/proto_creation/android_31_google_atd_x64.textpb",'
         '      "tools/android/avd/proto_creation/android_32_google_atd_x64_foldable.textpb",'
         '      "tools/android/avd/proto_creation/android_33_google_atd_x64.textpb",'
+        '      "tools/android/avd/proto_creation/android_34_desktop_x64.textpb",'
         '      "tools/android/avd/proto_creation/generic_android19.textpb",'
         '      "tools/android/avd/proto_creation/generic_android22.textpb",'
         '      "tools/android/avd/proto_creation/generic_android23.textpb",'
@@ -37598,10 +37599,10 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:high"
+      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:1"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -37692,10 +37693,10 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
+      dimensions: "free_space:high"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
+      dimensions: "ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -37879,10 +37880,10 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:high"
+      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:1"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -37973,10 +37974,10 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
+      dimensions: "free_space:high"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
+      dimensions: "ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -52866,7 +52867,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac-fieldtrial-tester\">mac-fieldtrial-tester</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac11-arm64-rel\">mac11-arm64-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac12-arm64-rel\">mac12-arm64-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac13-arm64-rel\">mac13-arm64-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac13-skia-alt-arm64-blink-rel\">mac13-skia-alt-arm64-blink-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac14-arm64-rel\">mac14-arm64-rel</a></li></ul>"
+      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac-fieldtrial-tester\">mac-fieldtrial-tester</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac-skia-alt-arm64-blink-rel\">mac-skia-alt-arm64-blink-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac11-arm64-rel\">mac11-arm64-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac12-arm64-rel\">mac12-arm64-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac13-arm64-rel\">mac13-arm64-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac14-arm64-rel\">mac14-arm64-rel</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -54164,6 +54165,98 @@
       }
     }
     builders {
+      name: "mac-skia-alt-arm64-rel-tests"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/ci/mac-skia-alt-arm64-rel-tests/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.mac",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
+        '}'
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "Runs web tests with Skia Graphite on Mac ARM machines<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac-skia-alt-arm64-blink-rel\">mac-skia-alt-arm64-blink-rel</a></li></ul>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+        pool: "luci.chromium.try"
+        dimensions: "free_space:"
+        dimensions: "pool:luci.chromium.try"
+      }
+      contact_team_email: "chrome-skia-graphite@google.com"
+    }
+    builders {
       name: "mac-swangle-chromium-x64"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -56459,98 +56552,6 @@
       }
     }
     builders {
-      name: "mac13-skia-alt-arm64-rel-tests"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/mac13-skia-alt-arm64-rel-tests/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.mac",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      execution_timeout_secs: 10800
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "Runs web tests with Skia Graphite on Mac ARM machines<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac13-skia-alt-arm64-blink-rel\">mac13-skia-alt-arm64-blink-rel</a></li></ul>"
-      shadow_builder_adjustments {
-        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-        pool: "luci.chromium.try"
-        dimensions: "free_space:"
-        dimensions: "pool:luci.chromium.try"
-      }
-      contact_team_email: "chrome-skia-graphite@google.com"
-    }
-    builders {
       name: "mac13-wpt-chromium-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -101752,6 +101753,102 @@
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/mac-rust-x64-dbg\">mac-rust-x64-dbg</a></li></ul>"
     }
     builders {
+      name: "mac-skia-alt-arm64-blink-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
+      dimensions: "os:Mac-14"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:1"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/mac-skia-alt-arm64-blink-rel/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.blink",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.buildbucket.canary_software"
+        value: 5
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
+        key: "swarming.prpc.cli"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/mac-arm64-rel\">mac-arm64-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/mac-skia-alt-arm64-rel-tests\">mac-skia-alt-arm64-rel-tests</a></li></ul>"
+      contact_team_email: "chrome-skia-graphite@google.com"
+    }
+    builders {
       name: "mac-swangle-chromium-try-x64"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -102978,102 +103075,6 @@
       }
     }
     builders {
-      name: "mac13-skia-alt-arm64-blink-rel"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cpu:arm64"
-      dimensions: "os:Mac-14"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:1"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/try/mac13-skia-alt-arm64-blink-rel/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.blink",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.buildbucket.canary_software"
-        value: 5
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      experiments {
-        key: "swarming.prpc.cli"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/mac-arm64-rel\">mac-arm64-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/mac13-skia-alt-arm64-rel-tests\">mac13-skia-alt-arm64-rel-tests</a></li></ul>"
-      contact_team_email: "chrome-skia-graphite@google.com"
-    }
-    builders {
       name: "mac13-tests"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 5e09edf6..e8459908 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -649,7 +649,7 @@
     short_name: "bld"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/mac13-skia-alt-arm64-rel-tests"
+    name: "buildbucket/luci.chromium.ci/mac-skia-alt-arm64-rel-tests"
     category: "chromium.mac|release|arm64"
     short_name: "skia-alt"
   }
@@ -3220,6 +3220,9 @@
     name: "buildbucket/luci.chromium.try/mac-rel-compilator"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/mac-skia-alt-arm64-blink-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/mac-updater-try-builder-dbg"
   }
   builders {
@@ -3232,9 +3235,6 @@
     name: "buildbucket/luci.chromium.try/mac13-arm64-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/mac13-skia-alt-arm64-blink-rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/mac13-wpt-chromium-rel"
   }
   builders {
@@ -12684,7 +12684,7 @@
     short_name: "bld"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/mac13-skia-alt-arm64-rel-tests"
+    name: "buildbucket/luci.chromium.ci/mac-skia-alt-arm64-rel-tests"
     category: "release|arm64"
     short_name: "skia-alt"
   }
@@ -17938,6 +17938,9 @@
     name: "buildbucket/luci.chromium.try/mac-rust-x64-dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/mac-skia-alt-arm64-blink-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/mac-swangle-chromium-try-x64"
   }
   builders {
@@ -17977,9 +17980,6 @@
     name: "buildbucket/luci.chromium.try/mac13-blink-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/mac13-skia-alt-arm64-blink-rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/mac13-tests"
   }
   builders {
@@ -18276,6 +18276,9 @@
     name: "buildbucket/luci.chromium.try/linux-wpt-chromium-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/mac-skia-alt-arm64-blink-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/mac11.0-blink-rel"
   }
   builders {
@@ -18291,9 +18294,6 @@
     name: "buildbucket/luci.chromium.try/mac13-blink-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/mac13-skia-alt-arm64-blink-rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/mac13-wpt-chromium-rel"
   }
   builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index c1cceab1..8fa91576 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -5641,6 +5641,15 @@
   }
 }
 job {
+  id: "mac-skia-alt-arm64-rel-tests"
+  realm: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "mac-skia-alt-arm64-rel-tests"
+  }
+}
+job {
   id: "mac-swangle-chromium-x64"
   realm: "ci"
   buildbucket {
@@ -5868,15 +5877,6 @@
   }
 }
 job {
-  id: "mac13-skia-alt-arm64-rel-tests"
-  realm: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "mac13-skia-alt-arm64-rel-tests"
-  }
-}
-job {
   id: "mac13-wpt-chromium-rel"
   realm: "ci"
   buildbucket {
diff --git a/infra/config/generated/luci/realms.cfg b/infra/config/generated/luci/realms.cfg
index 71f4919c..ff6d70e4 100644
--- a/infra/config/generated/luci/realms.cfg
+++ b/infra/config/generated/luci/realms.cfg
@@ -259,6 +259,7 @@
         values: "linux-updater-tester-dbg"
         values: "linux-updater-tester-rel"
         values: "mac-fieldtrial-tester"
+        values: "mac-skia-alt-arm64-rel-tests"
         values: "mac11-arm64-enterprise-companion-tester-dbg"
         values: "mac11-arm64-enterprise-companion-tester-rel"
         values: "mac11-arm64-rel-tests"
@@ -276,7 +277,6 @@
         values: "mac13-arm64-enterprise-companion-tester-dbg"
         values: "mac13-arm64-rel-tests"
         values: "mac13-arm64-updater-tester-dbg"
-        values: "mac13-skia-alt-arm64-rel-tests"
         values: "mac13-x64-enterprise-companion-tester-rel"
         values: "mac13-x64-updater-tester-rel"
         values: "mac14-arm64-rel-tests"
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.desktop.star b/infra/config/subprojects/chromium/ci/chromium.android.desktop.star
index 50177742..1218049 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.desktop.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.desktop.star
@@ -80,8 +80,6 @@
     targets = targets.bundle(
         additional_compile_targets = "all",
     ),
-    ssd = True,
-    free_space = builders.free_space.high,
     console_view_entry = consoles.console_view_entry(
         category = "builder|arm64",
         short_name = "dbg",
@@ -127,6 +125,8 @@
     targets = targets.bundle(
         additional_compile_targets = "all",
     ),
+    ssd = True,
+    free_space = builders.free_space.high,
     console_view_entry = consoles.console_view_entry(
         category = "builder|arm64",
         short_name = "rel",
@@ -169,8 +169,6 @@
     targets = targets.bundle(
         additional_compile_targets = "all",
     ),
-    ssd = True,
-    free_space = builders.free_space.high,
     console_view_entry = consoles.console_view_entry(
         category = "builder|x64",
         short_name = "dbg",
@@ -218,6 +216,8 @@
     targets = targets.bundle(
         additional_compile_targets = "all",
     ),
+    ssd = True,
+    free_space = builders.free_space.high,
     console_view_entry = consoles.console_view_entry(
         category = "builder|x64",
         short_name = "rel",
diff --git a/infra/config/subprojects/chromium/ci/chromium.infra.star b/infra/config/subprojects/chromium/ci/chromium.infra.star
index 16e9866..e147d5c 100644
--- a/infra/config/subprojects/chromium/ci/chromium.infra.star
+++ b/infra/config/subprojects/chromium/ci/chromium.infra.star
@@ -167,6 +167,9 @@
                 "tools/android/avd/proto_creation/android_32_google_atd_x64_foldable.textpb",
                 "tools/android/avd/proto_creation/android_33_google_atd_x64.textpb",
 
+                # Desktop system images
+                "tools/android/avd/proto_creation/android_34_desktop_x64.textpb",
+
                 # TODO(hypan): Using more specific names for the configs below.
                 "tools/android/avd/proto_creation/generic_android19.textpb",
                 "tools/android/avd/proto_creation/generic_android22.textpb",
diff --git a/infra/config/subprojects/chromium/ci/chromium.mac.star b/infra/config/subprojects/chromium/ci/chromium.mac.star
index 7f38841..e32d82d 100644
--- a/infra/config/subprojects/chromium/ci/chromium.mac.star
+++ b/infra/config/subprojects/chromium/ci/chromium.mac.star
@@ -382,7 +382,7 @@
 )
 
 ci.thin_tester(
-    name = "mac13-skia-alt-arm64-rel-tests",
+    name = "mac-skia-alt-arm64-rel-tests",
     description_html = "Runs web tests with Skia Graphite on Mac ARM machines",
     triggered_by = ["ci/mac-arm64-rel"],
     builder_spec = builder_config.builder_spec(
diff --git a/infra/config/subprojects/chromium/try/tryserver.blink.star b/infra/config/subprojects/chromium/try/tryserver.blink.star
index 9472ad6..b7b5b0d 100644
--- a/infra/config/subprojects/chromium/try/tryserver.blink.star
+++ b/infra/config/subprojects/chromium/try/tryserver.blink.star
@@ -419,11 +419,11 @@
 )
 
 blink_mac_builder(
-    name = "mac13-skia-alt-arm64-blink-rel",
+    name = "mac-skia-alt-arm64-blink-rel",
     branch_selector = None,
     mirrors = [
         "ci/mac-arm64-rel",
-        "ci/mac13-skia-alt-arm64-rel-tests",
+        "ci/mac-skia-alt-arm64-rel-tests",
     ],
     gn_args = gn_args.config(
         # TODO(crbug.com/40937352): Currently we override the gn args instead
diff --git a/internal b/internal
index f6ebb87..f30a872 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit f6ebb87b52ef6b2cbd2dd6968eef2f24e7df84fd
+Subproject commit f30a872d58afa35e1ff0f265d1a11c676e6d9958
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 91b0481e..9e1d039 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -231,7 +231,6 @@
       "//ios/chrome/browser/passwords/model:store_factory",
       "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
       "//ios/chrome/browser/shared/model/application_context",
-      "//ios/chrome/browser/shared/model/browser_state",
       "//ios/chrome/browser/shared/model/profile",
       "//ios/chrome/browser/sync/model",
       "//ios/chrome/browser/webauthn/model",
@@ -255,7 +254,6 @@
     "//ios/chrome/browser/metrics/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/public/provider/chrome/browser/primes:primes_api",
   ]
@@ -328,7 +326,6 @@
     "//ios/chrome/app/application_delegate:observing_app_state_agent",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ntp/ui_bundled:feature_flags",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
@@ -409,7 +406,6 @@
     "//ios/chrome/browser/promos_manager/model:constants",
     "//ios/chrome/browser/promos_manager/model:factory",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
@@ -456,7 +452,6 @@
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/chrome/browser/signin/model:signin_util",
     "//ios/chrome/browser/ui/search_engine_choice",
@@ -486,7 +481,6 @@
     "//ios/chrome/browser/push_notification/model:push_notification_service",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/utils",
     "//ios/chrome/browser/shared/public/features",
@@ -638,7 +632,6 @@
     "//ios/chrome/browser/shared/coordinator/scene",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index f1753ef..37f01bf1 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -202,7 +202,6 @@
     "//ios/chrome/browser/shared/coordinator/scene",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
diff --git a/ios/chrome/app/spotlight/BUILD.gn b/ios/chrome/app/spotlight/BUILD.gn
index 8a322cd..0e55f47 100644
--- a/ios/chrome/app/spotlight/BUILD.gn
+++ b/ios/chrome/app/spotlight/BUILD.gn
@@ -50,7 +50,6 @@
     "//ios/chrome/browser/reading_list/model",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/app/startup/BUILD.gn b/ios/chrome/app/startup/BUILD.gn
index cfa55c2..a96e7f5 100644
--- a/ios/chrome/app/startup/BUILD.gn
+++ b/ios/chrome/app/startup/BUILD.gn
@@ -35,10 +35,7 @@
     deps += [ ":sandbox_dump" ]
   }
 
-  assert_no_deps = [
-    "//ios/chrome/browser/shared/model/browser_state",
-    "//ios/chrome/browser/web/model",
-  ]
+  assert_no_deps = [ "//ios/chrome/browser/web/model" ]
 }
 
 buildflag_header("ios_enable_sandbox_dump_buildflags") {
@@ -81,7 +78,6 @@
     "//ios/chrome/browser/default_browser/model:utils",
     "//ios/chrome/browser/first_run/model",
     "//ios/chrome/browser/net/model:model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/web/model:web_internal",
@@ -92,6 +88,7 @@
     "//ios/public/provider/chrome/browser/app_utils:app_utils_api",
     "//ios/public/provider/chrome/browser/raccoon:raccoon_api",
     "//ios/web",
+    "//ios/web/public",
     "//ios/web/public/init",
   ]
 }
diff --git a/ios/chrome/browser/account_picker/ui_bundled/account_picker_confirmation/BUILD.gn b/ios/chrome/browser/account_picker/ui_bundled/account_picker_confirmation/BUILD.gn
index ea934b7..91d0d2f 100644
--- a/ios/chrome/browser/account_picker/ui_bundled/account_picker_confirmation/BUILD.gn
+++ b/ios/chrome/browser/account_picker/ui_bundled/account_picker_confirmation/BUILD.gn
@@ -26,7 +26,6 @@
     "//ios/chrome/browser/account_picker/ui_bundled/account_picker_screen",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/ui/elements",
diff --git a/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/BUILD.gn b/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/BUILD.gn
index 64c273f..57783d26 100644
--- a/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/BUILD.gn
+++ b/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/BUILD.gn
@@ -25,7 +25,6 @@
     "//ios/chrome/browser/net/model:crurl",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/affiliations/model/BUILD.gn b/ios/chrome/browser/affiliations/model/BUILD.gn
index 629676f6..a338b798 100644
--- a/ios/chrome/browser/affiliations/model/BUILD.gn
+++ b/ios/chrome/browser/affiliations/model/BUILD.gn
@@ -12,12 +12,12 @@
     "//components/keyed_service/core",
     "//components/password_manager/core/browser",
     "//ios/chrome/browser/shared/model/application_context",
+    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//services/network/public/cpp",
   ]
   public_deps = [
     "//base",
     "//components/keyed_service/ios",
-    "//ios/chrome/browser/shared/model/browser_state",
   ]
 }
diff --git a/ios/chrome/browser/app_launcher/model/BUILD.gn b/ios/chrome/browser/app_launcher/model/BUILD.gn
index 1ffa776..97304c8 100644
--- a/ios/chrome/browser/app_launcher/model/BUILD.gn
+++ b/ios/chrome/browser/app_launcher/model/BUILD.gn
@@ -31,7 +31,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_observer",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/app_store_rating/ui_bundled/BUILD.gn b/ios/chrome/browser/app_store_rating/ui_bundled/BUILD.gn
index 9b46224..8eff258 100644
--- a/ios/chrome/browser/app_store_rating/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/app_store_rating/ui_bundled/BUILD.gn
@@ -23,7 +23,6 @@
     "//ios/chrome/browser/promos_manager/model:types",
     "//ios/chrome/browser/shared/coordinator/scene:observing_scene_agent",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/ui/promos_manager:promos",
@@ -66,7 +65,6 @@
     "//ios/chrome/browser/shared/coordinator/scene/test",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile/test",
diff --git a/ios/chrome/browser/application_context/model/BUILD.gn b/ios/chrome/browser/application_context/model/BUILD.gn
index c10d00b..e3eb99f 100644
--- a/ios/chrome/browser/application_context/model/BUILD.gn
+++ b/ios/chrome/browser/application_context/model/BUILD.gn
@@ -43,7 +43,6 @@
     "//ios/chrome/browser/profile/model",
     "//ios/chrome/browser/push_notification/model:push_notification_service_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/browser_state:incognito_session_tracker",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
diff --git a/ios/chrome/browser/autocomplete/model/BUILD.gn b/ios/chrome/browser/autocomplete/model/BUILD.gn
index 4f63e4f..0117fff 100644
--- a/ios/chrome/browser/autocomplete/model/BUILD.gn
+++ b/ios/chrome/browser/autocomplete/model/BUILD.gn
@@ -58,6 +58,7 @@
     "//ios/chrome/browser/sync/model",
     "//ios/components/webui:url_constants",
     "//ios/web",
+    "//ios/web/public",
     "//url",
   ]
 }
diff --git a/ios/chrome/browser/autofill/model/BUILD.gn b/ios/chrome/browser/autofill/model/BUILD.gn
index 0eabddd7..1380299 100644
--- a/ios/chrome/browser/autofill/model/BUILD.gn
+++ b/ios/chrome/browser/autofill/model/BUILD.gn
@@ -58,7 +58,10 @@
     "//ios/chrome/browser/webdata_services/model",
     "//ios/chrome/common/ui/colors:colors",
     "//ios/web/common",
+    "//ios/web/public",
+    "//ios/web/public:web_state_observer",
     "//ios/web/public/js_messaging",
+    "//ios/web/public/ui",
     "//third_party/leveldatabase",
     "//third_party/libaddressinput",
     "//ui/base",
@@ -106,7 +109,6 @@
     "//ios/chrome/browser/autofill/ui_bundled:coordinator",
     "//ios/chrome/browser/infobars/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands:commands",
     "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/autofill/model/automation/BUILD.gn b/ios/chrome/browser/autofill/model/automation/BUILD.gn
index 63bff98..90f6c81 100644
--- a/ios/chrome/browser/autofill/model/automation/BUILD.gn
+++ b/ios/chrome/browser/autofill/model/automation/BUILD.gn
@@ -42,7 +42,6 @@
     "//components/autofill/ios/browser",
     "//components/strings",
     "//ios/chrome/browser/autofill/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/test/app:test_support",
     "//ios/testing:nserror_support",
diff --git a/ios/chrome/browser/autofill/model/bottom_sheet/BUILD.gn b/ios/chrome/browser/autofill/model/bottom_sheet/BUILD.gn
index f85c8ad..4a945d99 100644
--- a/ios/chrome/browser/autofill/model/bottom_sheet/BUILD.gn
+++ b/ios/chrome/browser/autofill/model/bottom_sheet/BUILD.gn
@@ -27,7 +27,6 @@
     "//components/password_manager/core/common:features",
     "//components/password_manager/ios",
     "//components/prefs",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/autofill/ui_bundled/BUILD.gn b/ios/chrome/browser/autofill/ui_bundled/BUILD.gn
index 3a8bdb4d..f049397 100644
--- a/ios/chrome/browser/autofill/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/autofill/ui_bundled/BUILD.gn
@@ -49,7 +49,6 @@
     "//ios/chrome/browser/passwords/model",
     "//ios/chrome/browser/plus_addresses/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list:web_state_list",
     "//ios/chrome/browser/shared/public/commands:commands",
@@ -258,7 +257,6 @@
     "//ios/chrome/browser/autofill/model",
     "//ios/chrome/browser/passwords/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/common/ui/reauthentication:reauthentication",
diff --git a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn
index 09cdb6a..663bcf96 100644
--- a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn
+++ b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn
@@ -54,7 +54,6 @@
     "//ios/chrome/browser/infobars/model:public",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
@@ -174,7 +173,6 @@
     "//ios/chrome/browser/net/model:crurl",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h b/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h
index 47b8ff3..dac9d00 100644
--- a/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h
+++ b/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h
@@ -101,7 +101,7 @@
   void ShowDeleteAddressProfileDialog(
       const AutofillProfile& profile,
       AddressProfileDeleteDialogCallback delete_dialog_callback) override;
-  void ShowAutofillSuggestions(
+  SuggestionUiSessionId ShowAutofillSuggestions(
       const PopupOpenArgs& open_args,
       base::WeakPtr<AutofillSuggestionDelegate> delegate) override;
   void UpdateAutofillDataListValues(
diff --git a/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.mm b/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.mm
index e89b789..136b6e6 100644
--- a/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.mm
@@ -296,10 +296,12 @@
   NOTREACHED();
 }
 
-void ChromeAutofillClientIOS::ShowAutofillSuggestions(
+AutofillClient::SuggestionUiSessionId
+ChromeAutofillClientIOS::ShowAutofillSuggestions(
     const AutofillClient::PopupOpenArgs& open_args,
     base::WeakPtr<AutofillSuggestionDelegate> delegate) {
   [bridge_ showAutofillPopup:open_args.suggestions suggestionDelegate:delegate];
+  return SuggestionUiSessionId();
 }
 
 AutofillPlusAddressDelegate* ChromeAutofillClientIOS::GetPlusAddressDelegate() {
diff --git a/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/BUILD.gn b/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/BUILD.gn
index d279038..13d86ceb 100644
--- a/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/BUILD.gn
+++ b/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/BUILD.gn
@@ -53,7 +53,6 @@
     "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_coordinator.mm b/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_coordinator.mm
index 8bf3e2f..2b32b10 100644
--- a/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_coordinator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_coordinator.mm
@@ -31,6 +31,7 @@
 #import "components/password_manager/core/common/password_manager_features.h"
 #import "components/password_manager/ios/password_generation_provider.h"
 #import "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h"
 #import "ios/chrome/browser/autofill/model/personal_data_manager_factory.h"
 #import "ios/chrome/browser/autofill/ui_bundled/autofill_credit_card_util.h"
 #import "ios/chrome/browser/autofill/ui_bundled/branding/branding_coordinator.h"
@@ -610,6 +611,30 @@
   [self.navigator openAddressSettings];
 }
 
+#pragma mark - PlusAddressCoordinatorDelegate
+
+// Opens the create plus address bottom sheet.
+- (void)openCreatePlusAddressSheet {
+  [self reset];
+
+  web::WebState* activeWebState = [self activeWebState];
+  if (!activeWebState) {
+    return;
+  }
+
+  __weak __typeof(self) weakSelf = self;
+  auto callback = base::BindOnce(^(const std::string& plusAddress) {
+    [weakSelf.injectionHandler
+        userDidPickContent:base::SysUTF8ToNSString(plusAddress)
+             passwordField:NO
+             requiresHTTPS:NO];
+  });
+
+  AutofillBottomSheetTabHelper* tabHelper =
+      AutofillBottomSheetTabHelper::FromWebState(activeWebState);
+  tabHelper->ShowPlusAddressesBottomSheet(std::move(callback));
+}
+
 #pragma mark - ExpandedManualFillCoordinatorDelegate
 
 - (void)stopExpandedManualFillCoordinator:
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/BUILD.gn b/ios/chrome/browser/autofill/ui_bundled/manual_fill/BUILD.gn
index 3663022f..6d534a2 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/BUILD.gn
@@ -39,6 +39,7 @@
     "manual_fill_password_mediator.mm",
     "manual_fill_plus_address_mediator.h",
     "manual_fill_plus_address_mediator.mm",
+    "plus_address_coordinator_delegate.h",
   ]
   deps = [
     ":manual_fill_constants",
@@ -68,7 +69,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list:web_state_list",
     "//ios/chrome/browser/shared/public/commands",
@@ -149,6 +149,7 @@
     "password_list_navigator.h",
     "password_view_controller.h",
     "password_view_controller.mm",
+    "plus_address_list_navigator.h",
   ]
   deps = [
     ":manual_fill_constants",
@@ -279,7 +280,6 @@
     "//components/autofill/ios/browser",
     "//ios/chrome/browser/autofill/model",
     "//ios/chrome/browser/autofill/ui_bundled:bridges",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list:web_state_list",
     "//ios/web/public:public",
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_coordinator.h b/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_coordinator.h
index fd27ad9b..48be802 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_coordinator.h
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_coordinator.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_MANUAL_FILL_ADDRESS_COORDINATOR_H_
 
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/fallback_coordinator.h"
+#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_coordinator_delegate.h"
 
 @class ManualFillPlusAddressMediator;
 
@@ -15,7 +16,8 @@
 
 // Delegate for the coordinator actions.
 // TODO(crbug.com/40577448): revise delegate method names.
-@protocol AddressCoordinatorDelegate<FallbackCoordinatorDelegate>
+@protocol AddressCoordinatorDelegate <FallbackCoordinatorDelegate,
+                                      PlusAddressCoordinatorDelegate>
 
 // Opens the details of the given address in edit mode. `offerMigrateToAccount`
 // indicates whether or not the option to migrate the address to the account
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_coordinator.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_coordinator.mm
index 2a371dc..eee9a94c 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_coordinator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_coordinator.mm
@@ -17,13 +17,14 @@
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_address_mediator.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_injection_handler.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.h"
+#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_list_navigator.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/ui/table_view/table_view_navigation_controller.h"
 #import "ios/chrome/browser/signin/model/authentication_service_factory.h"
 #import "ui/base/device_form_factor.h"
 
-@interface AddressCoordinator () <AddressListDelegate>
+@interface AddressCoordinator () <AddressListDelegate, PlusAddressListNavigator>
 
 // The view controller presented above the keyboard where the user can select
 // a field from one of their addresses.
@@ -76,6 +77,7 @@
     _addressMediator.consumer = _addressViewController;
     if (manualFillPlusAddressMediator) {
       manualFillPlusAddressMediator.consumer = _addressViewController;
+      manualFillPlusAddressMediator.navigator = self;
     }
   }
   return self;
@@ -113,4 +115,13 @@
   }];
 }
 
+#pragma mark - PlusAddressListNavigator
+
+- (void)openCreatePlusAddressSheet {
+  __weak __typeof(self) weakSelf = self;
+  [self dismissIfNecessaryThenDoCompletion:^{
+    [weakSelf.delegate openCreatePlusAddressSheet];
+  }];
+}
+
 @end
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/expanded_manual_fill_coordinator.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/expanded_manual_fill_coordinator.mm
index e6f4c80..81ec1b2 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/expanded_manual_fill_coordinator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/expanded_manual_fill_coordinator.mm
@@ -242,7 +242,8 @@
   _manualFillPlusAddressMediator = [[ManualFillPlusAddressMediator alloc]
       initWithFaviconLoader:faviconLoader
          plusAddressService:plusAddressService
-                        URL:URL];
+                        URL:URL
+             isOffTheRecord:browserState->IsOffTheRecord()];
 
   return _manualFillPlusAddressMediator;
 }
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.h b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.h
index dc497ee..ffbb4f7d 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.h
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.h
@@ -74,6 +74,9 @@
 // Accessibility identifier for the manage plus address action.
 extern NSString* const kManagePlusAddressAccessibilityIdentifier;
 
+// Accessibility identifier for the create plus address action.
+extern NSString* const kCreatePlusAddressAccessibilityIdentifier;
+
 // Miscellaneous
 
 // Accessibility identifier for the expanded manual fill view.
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.mm
index 703273b..157b86f6 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.mm
@@ -66,6 +66,9 @@
 NSString* const kManagePlusAddressAccessibilityIdentifier =
     @"ManagePlusAddressAccessibilityIdentifier";
 
+NSString* const kCreatePlusAddressAccessibilityIdentifier =
+    @"CreatePlusAddressAccessibilityIdentifier";
+
 // Miscellaneous
 
 NSString* const kExpandedManualFillViewID = @"ExpandedManualFillViewID";
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_coordinator.h b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_coordinator.h
index cffac4b..0565885 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_coordinator.h
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_coordinator.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_MANUAL_FILL_MANUAL_FILL_PASSWORD_COORDINATOR_H_
 
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/fallback_coordinator.h"
+#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_coordinator_delegate.h"
 
 class GURL;
 
@@ -16,7 +17,8 @@
 @class ManualFillPlusAddressMediator;
 
 // Delegate for the coordinator actions.
-@protocol PasswordCoordinatorDelegate <FallbackCoordinatorDelegate>
+@protocol PasswordCoordinatorDelegate <FallbackCoordinatorDelegate,
+                                       PlusAddressCoordinatorDelegate>
 
 // Opens the password manager.
 - (void)openPasswordManager;
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_coordinator.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_coordinator.mm
index df81828..0a16775 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_coordinator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_coordinator.mm
@@ -13,6 +13,7 @@
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/password_list_navigator.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/password_view_controller.h"
+#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_list_navigator.h"
 #import "ios/chrome/browser/favicon/model/favicon_loader.h"
 #import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h"
 #import "ios/chrome/browser/passwords/model/ios_chrome_account_password_store_factory.h"
@@ -22,7 +23,8 @@
 #import "ios/chrome/browser/sync/model/sync_service_factory.h"
 #import "ui/base/device_form_factor.h"
 
-@interface ManualFillPasswordCoordinator () <PasswordListNavigator>
+@interface ManualFillPasswordCoordinator () <PasswordListNavigator,
+                                             PlusAddressListNavigator>
 
 // Fetches and filters the passwords for the view controller.
 @property(nonatomic, strong) ManualFillPasswordMediator* passwordMediator;
@@ -91,6 +93,7 @@
 
     if (manualFillPlusAddressMediator) {
       manualFillPlusAddressMediator.consumer = _passwordViewController;
+      manualFillPlusAddressMediator.navigator = self;
     }
   }
   return self;
@@ -155,4 +158,13 @@
   }];
 }
 
+#pragma mark - PlusAddressListNavigator
+
+- (void)openCreatePlusAddressSheet {
+  __weak __typeof(self) weakSelf = self;
+  [self dismissIfNecessaryThenDoCompletion:^{
+    [weakSelf.delegate openCreatePlusAddressSheet];
+  }];
+}
+
 @end
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.h b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.h
index 4c6596d..c2624937 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.h
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.h
@@ -11,6 +11,7 @@
 
 @protocol ManualFillContentInjector;
 @protocol ManualFillPlusAddressConsumer;
+@protocol PlusAddressListNavigator;
 
 class FaviconLoader;
 class GURL;
@@ -27,11 +28,14 @@
 @property(nonatomic, weak) id<ManualFillPlusAddressConsumer> consumer;
 // The delegate in charge of using the content selected by the user.
 @property(nonatomic, weak) id<ManualFillContentInjector> contentInjector;
+// The delegate in charge of navigation.
+@property(nonatomic, weak) id<PlusAddressListNavigator> navigator;
 
 - (instancetype)initWithFaviconLoader:(FaviconLoader*)faviconLoader
                    plusAddressService:
                        (plus_addresses::PlusAddressService*)plusAddressService
                                   URL:(const GURL&)URL
+                       isOffTheRecord:(BOOL)isOffTheRecord
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)init NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.mm
index 7ba79009..02dd1115 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_mediator.h"
 
 #import "base/i18n/message_formatter.h"
+#import "base/metrics/user_metrics.h"
 #import "base/ranges/algorithm.h"
 #import "base/strings/sys_string_conversions.h"
 #import "components/plus_addresses/plus_address_service.h"
@@ -15,6 +16,7 @@
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_cell.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_consumer.h"
+#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_list_navigator.h"
 #import "ios/chrome/browser/favicon/model/favicon_loader.h"
 #import "ios/chrome/browser/net/model/crurl.h"
 #import "ios/chrome/grit/ios_strings.h"
@@ -35,18 +37,30 @@
 
   // The origin to which all operations should be scoped.
   url::Origin _mainFrameOrigin;
+
+  // If YES, create plus address action is shown.
+  BOOL _isPlusAddressCreationFallbackEnabled;
+
+  // IF YES, the plus addresses have been fetched for the domain (along with its
+  // affiliations).
+  BOOL _plusAddressesAreFetched;
 }
 
 - (instancetype)initWithFaviconLoader:(FaviconLoader*)faviconLoader
                    plusAddressService:
                        (plus_addresses::PlusAddressService*)plusAddressService
-                                  URL:(const GURL&)URL {
+                                  URL:(const GURL&)URL
+                       isOffTheRecord:(BOOL)isOffTheRecord {
   self = [super init];
   if (self) {
     _faviconLoader = faviconLoader;
     _plusAddressService = plusAddressService;
     _URL = URL;
     _mainFrameOrigin = url::Origin::Create(URL);
+    _isPlusAddressCreationFallbackEnabled =
+        _plusAddressService->IsPlusAddressCreationEnabled(_mainFrameOrigin,
+                                                          isOffTheRecord);
+    _plusAddressesAreFetched = NO;
   }
 
   return self;
@@ -58,7 +72,7 @@
   }
   _consumer = consumer;
   [self postPlusAddressesToConsumer];
-  [self postActionsToConsumer];
+  [self postActionsToConsumer:NO];
 }
 
 #pragma mark - TableViewFaviconDataSource
@@ -102,6 +116,7 @@
 // Presents the fetched `plusProfiles` to the consumer.
 - (void)onPlusAddressesFetched:
     (const std::vector<plus_addresses::PlusProfile>&)plusProfiles {
+  _plusAddressesAreFetched = YES;
   int plusAddressesCount = (int)plusProfiles.size();
   NSMutableArray* items =
       [[NSMutableArray alloc] initWithCapacity:plusAddressesCount];
@@ -125,6 +140,7 @@
   }
 
   [self.consumer presentPlusAddresses:items];
+  [self postActionsToConsumer:(items.count > 0)];
 }
 
 // Creates `ManualFillPlusAddress` from `plusAddress`.
@@ -146,19 +162,51 @@
 }
 
 // Sends actions to the consumer.
-- (void)postActionsToConsumer {
+- (void)postActionsToConsumer:(BOOL)hasPlusAddresses {
   if (!self.consumer) {
     return;
   }
 
-  NSString* managePlusAddressesTitle = l10n_util::GetNSString(
-      IDS_PLUS_ADDRESS_MANUAL_FALLBACK_MANAGE_ACTION_TEXT_IOS);
-  ManualFillActionItem* managePlusAddressItem =
-      [[ManualFillActionItem alloc] initWithTitle:managePlusAddressesTitle
-                                           action:nil];
-  managePlusAddressItem.accessibilityIdentifier =
-      manual_fill::kManagePlusAddressAccessibilityIdentifier;
-  [self.consumer presentPlusAddressActions:@[ managePlusAddressItem ]];
+  NSMutableArray<ManualFillActionItem*>* actions =
+      [[NSMutableArray alloc] init];
+
+  if (_plusAddressesAreFetched && hasPlusAddresses) {
+    NSString* managePlusAddressesTitle = l10n_util::GetNSString(
+        IDS_PLUS_ADDRESS_MANUAL_FALLBACK_MANAGE_ACTION_TEXT_IOS);
+    ManualFillActionItem* managePlusAddressItem = [[ManualFillActionItem alloc]
+        initWithTitle:managePlusAddressesTitle
+               action:^{
+                 base::RecordAction(base::UserMetricsAction(
+                     "ManualFallback_PlusAddress_OpenManagePlusAddress"));
+               }];
+    managePlusAddressItem.accessibilityIdentifier =
+        manual_fill::kManagePlusAddressAccessibilityIdentifier;
+    [actions addObject:managePlusAddressItem];
+  }
+
+  __weak __typeof(self) weakSelf = self;
+  // Offer plus address creation if it's supported for the current user session
+  // and if the user doesn't have any plus addresses created for the current
+  // domain.
+  if (_isPlusAddressCreationFallbackEnabled && _plusAddressesAreFetched &&
+      !hasPlusAddresses) {
+    NSString* createPlusAddressesTitle = l10n_util::GetNSString(
+        IDS_PLUS_ADDRESS_MANUAL_FALLBACK_CREATE_ACTION_TEXT_IOS);
+    ManualFillActionItem* createPlusAddressItem = [[ManualFillActionItem alloc]
+        initWithTitle:createPlusAddressesTitle
+               action:^{
+                 base::RecordAction(base::UserMetricsAction(
+                     "ManualFallback_PlusAddress_OpenCreatePlusAddress"));
+                 [weakSelf.navigator openCreatePlusAddressSheet];
+               }];
+    createPlusAddressItem.accessibilityIdentifier =
+        manual_fill::kCreatePlusAddressAccessibilityIdentifier;
+    [actions addObject:createPlusAddressItem];
+  }
+
+  if (actions.count > 0) {
+    [self.consumer presentPlusAddressActions:actions];
+  }
 }
 
 @end
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_coordinator_delegate.h b/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_coordinator_delegate.h
new file mode 100644
index 0000000..35d0f64
--- /dev/null
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_coordinator_delegate.h
@@ -0,0 +1,16 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_MANUAL_FILL_PLUS_ADDRESS_COORDINATOR_DELEGATE_H_
+#define IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_MANUAL_FILL_PLUS_ADDRESS_COORDINATOR_DELEGATE_H_
+
+// Delegate for the plus address fallback actions.
+@protocol PlusAddressCoordinatorDelegate <NSObject>
+
+// Opens the bottom sheet to create plus address.
+- (void)openCreatePlusAddressSheet;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_MANUAL_FILL_PLUS_ADDRESS_COORDINATOR_DELEGATE_H_
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_list_navigator.h b/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_list_navigator.h
new file mode 100644
index 0000000..606d012
--- /dev/null
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_list_navigator.h
@@ -0,0 +1,16 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_MANUAL_FILL_PLUS_ADDRESS_LIST_NAVIGATOR_H_
+#define IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_MANUAL_FILL_PLUS_ADDRESS_LIST_NAVIGATOR_H_
+
+// Object to navigate different views in manual fallback's plus address list.
+@protocol PlusAddressListNavigator
+
+// Requests to open the bottom sheet to create a new plus address.
+- (void)openCreatePlusAddressSheet;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_MANUAL_FILL_PLUS_ADDRESS_LIST_NAVIGATOR_H_
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_manual_fill_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_manual_fill_egtest.mm
index 007fdbb..a10b902f 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_manual_fill_egtest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/plus_address_manual_fill_egtest.mm
@@ -10,6 +10,7 @@
 #import "components/plus_addresses/fake_plus_address_service.h"
 #import "components/plus_addresses/features.h"
 #import "components/plus_addresses/plus_address_test_utils.h"
+#import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/autofill/ui_bundled/autofill_app_interface.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_constants.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_matchers.h"
@@ -105,9 +106,17 @@
   net::test_server::RegisterDefaultHandlers(self.testServer);
   GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
 
+  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
+
   [AutofillAppInterface saveExampleAccountProfile];
 }
 
+- (void)tearDown {
+  [SigninEarlGrey signOut];
+
+  [super tearDown];
+}
+
 // Opens the expanded manual fill view for a given `dataType`. `fieldToFill` is
 // the ID of the form field that should be focused prior to opening the expanded
 // manual fill view.
@@ -139,8 +148,6 @@
     EARL_GREY_TEST_SKIPPED(@"Test fails for iPad");
   }
 
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-
   // Open the expanded manual fill view for an address field.
   [self openExpandedManualFillViewForDataType:ManualFillDataType::kAddress
                                   fieldToFill:kNameFieldID];
@@ -160,19 +167,15 @@
           manual_fill::ChipButton(
               plus_addresses::FakePlusAddressService::kFakePlusAddress16)]
       assertWithMatcher:grey_sufficientlyVisible()];
-
-  [SigninEarlGrey signOut];
 }
 
-// Tests that the plus address actions are shown in the address and password
-// segments.
-- (void)testPlusAddressActions {
+// Tests that the plus address manage action are shown in the address and
+// password segments.
+- (void)testPlusAddressManageAction {
   if ([ChromeEarlGrey isIPadIdiom]) {
     EARL_GREY_TEST_SKIPPED(@"Test fails for iPad");
   }
 
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-
   [self openExpandedManualFillViewForDataType:ManualFillDataType::kAddress
                                   fieldToFill:kNameFieldID];
 
@@ -190,8 +193,72 @@
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:managePlusAddressMatcher]
       assertWithMatcher:grey_sufficientlyVisible()];
+}
 
-  [SigninEarlGrey signOut];
+// Tests that tapping on the create plus address action in the address manual
+// fill view opens up the bottomsheet to create one.
+- (void)testPlusAddressCreateActionFromAddressView {
+  if ([ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"Test fails for iPad");
+  }
+
+  [PlusAddressAppInterface setShouldOfferPlusAddressCreation:YES];
+  [PlusAddressAppInterface setShouldReturnNoAffiliatedPlusProfiles:YES];
+
+  [self openExpandedManualFillViewForDataType:ManualFillDataType::kAddress
+                                  fieldToFill:kNameFieldID];
+
+  id<GREYMatcher> createPlusAddressMatcher = grey_accessibilityID(
+      manual_fill::kCreatePlusAddressAccessibilityIdentifier);
+
+  [[EarlGrey selectElementWithMatcher:manual_fill::ProfilesTableViewMatcher()]
+      performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
+  [[EarlGrey selectElementWithMatcher:createPlusAddressMatcher]
+      performAction:grey_tap()];
+
+  id<GREYMatcher> createPlusAddressBottomSheetButton =
+      chrome_test_util::ButtonWithAccessibilityLabelId(
+          IDS_PLUS_ADDRESS_BOTTOMSHEET_OK_TEXT_IOS);
+  [[EarlGrey selectElementWithMatcher:createPlusAddressBottomSheetButton]
+      performAction:grey_tap()];
+
+  NSString* condition = [NSString
+      stringWithFormat:@"window.document.getElementById('%s').value === '%@'",
+                       kNameFieldID, @"plus+remote@plus.plus"];
+  [ChromeEarlGrey waitForJavaScriptCondition:condition];
+}
+
+// Tests that tapping on the create plus address action in the password manual
+// fill view opens up the bottomsheet to create one.
+- (void)testPlusAddressCreateActionFromPasswordView {
+  if ([ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"Test fails for iPad");
+  }
+
+  [PlusAddressAppInterface setShouldOfferPlusAddressCreation:YES];
+  [PlusAddressAppInterface setShouldReturnNoAffiliatedPlusProfiles:YES];
+
+  [self openExpandedManualFillViewForDataType:ManualFillDataType::kAddress
+                                  fieldToFill:kNameFieldID];
+
+  id<GREYMatcher> createPlusAddressMatcher = grey_accessibilityID(
+      manual_fill::kCreatePlusAddressAccessibilityIdentifier);
+
+  [[EarlGrey selectElementWithMatcher:manual_fill::ProfilesTableViewMatcher()]
+      performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
+
+  // Switch over to passwords.
+  [[EarlGrey
+      selectElementWithMatcher:manual_fill::SegmentedControlPasswordTab()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:createPlusAddressMatcher]
+      performAction:grey_tap()];
+
+  id<GREYMatcher> createPlusAddressBottomSheetCancelButton =
+      chrome_test_util::ButtonWithAccessibilityLabelId(
+          IDS_PLUS_ADDRESS_MODAL_CANCEL_TEXT);
+  [[EarlGrey selectElementWithMatcher:createPlusAddressBottomSheetCancelButton]
+      performAction:grey_tap()];
 }
 
 @end
diff --git a/ios/chrome/browser/badges/ui_bundled/BUILD.gn b/ios/chrome/browser/badges/ui_bundled/BUILD.gn
index 2a3e32ae..e5f3fad 100644
--- a/ios/chrome/browser/badges/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/badges/ui_bundled/BUILD.gn
@@ -67,7 +67,6 @@
     "//ios/chrome/browser/overlays/model/public/common/infobars",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/badges/ui_bundled/badge_view_controller.mm b/ios/chrome/browser/badges/ui_bundled/badge_view_controller.mm
index 1aea8a1..56aba620 100644
--- a/ios/chrome/browser/badges/ui_bundled/badge_view_controller.mm
+++ b/ios/chrome/browser/badges/ui_bundled/badge_view_controller.mm
@@ -99,6 +99,9 @@
         badgeButtonForBadgeType:fullscreenBadgeItem.badgeType
                    usingInfoBar:nil];
   }
+
+  BOOL badgeHidden = !displayedBadgeItem && !fullscreenBadgeItem;
+  [self.visibilityDelegate setBadgeViewHidden:badgeHidden];
 }
 
 - (void)updateDisplayedBadge:(id<BadgeItem>)displayedBadgeItem
@@ -136,6 +139,9 @@
   } else {
     self.displayedBadge = nil;
   }
+
+  BOOL badgeHidden = !displayedBadgeItem && !fullscreenBadgeItem;
+  [self.visibilityDelegate setBadgeViewHidden:badgeHidden];
 }
 
 - (void)markDisplayedBadgeAsRead:(BOOL)read {
@@ -207,7 +213,6 @@
   if (!badgeButton) {
     _displayedBadge = nil;
     self.unreadIndicatorView = nil;
-    [self.visibilityDelegate setBadgeViewHidden:YES];
     return;
   }
   _displayedBadge = badgeButton;
@@ -226,7 +231,6 @@
                      self.view.transform = CGAffineTransformIdentity;
                    }
                    completion:nil];
-  [self.visibilityDelegate setBadgeViewHidden:NO];
 }
 
 - (void)setFullScreenBadge:(BadgeButton*)fullScreenBadge {
diff --git a/ios/chrome/browser/bookmarks/model/BUILD.gn b/ios/chrome/browser/bookmarks/model/BUILD.gn
index dda2521..98f60f4 100644
--- a/ios/chrome/browser/bookmarks/model/BUILD.gn
+++ b/ios/chrome/browser/bookmarks/model/BUILD.gn
@@ -47,6 +47,7 @@
     "//ios/chrome/browser/signin/model:system_identity",
     "//ios/chrome/browser/signin/model:system_identity_manager",
     "//ios/web",
+    "//ios/web/public/thread",
   ]
   public_deps = [ ":model_utils" ]
   allow_circular_includes_from = [
@@ -66,7 +67,6 @@
     "//base",
     "//components/bookmarks/browser",
     "//components/prefs",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
   ]
diff --git a/ios/chrome/browser/bookmarks/ui_bundled/BUILD.gn b/ios/chrome/browser/bookmarks/ui_bundled/BUILD.gn
index 3b6c289..609b45e 100644
--- a/ios/chrome/browser/bookmarks/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/bookmarks/ui_bundled/BUILD.gn
@@ -63,7 +63,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/bookmarks/model",
     "//ios/chrome/browser/ntp/shared/metrics:home_metrics",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/chrome/browser/shared/ui/symbols",
@@ -178,7 +177,6 @@
     "//components/strings",
     "//ios/chrome/browser/bookmarks/model",
     "//ios/chrome/browser/bookmarks/ui_bundled/home",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model:fake_system_identity",
diff --git a/ios/chrome/browser/bookmarks/ui_bundled/editor/BUILD.gn b/ios/chrome/browser/bookmarks/ui_bundled/editor/BUILD.gn
index 5b735bc..50c7995 100644
--- a/ios/chrome/browser/bookmarks/ui_bundled/editor/BUILD.gn
+++ b/ios/chrome/browser/bookmarks/ui_bundled/editor/BUILD.gn
@@ -25,7 +25,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/BUILD.gn b/ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/BUILD.gn
index 94e2c787..46b820d 100644
--- a/ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/BUILD.gn
+++ b/ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/BUILD.gn
@@ -39,7 +39,6 @@
     "//ios/chrome/browser/bookmarks/ui_bundled:core",
     "//ios/chrome/browser/bookmarks/ui_bundled/folder_editor:coordinator_headers",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/sync/model:model",
diff --git a/ios/chrome/browser/bookmarks/ui_bundled/folder_editor/BUILD.gn b/ios/chrome/browser/bookmarks/ui_bundled/folder_editor/BUILD.gn
index 28d4737..17f79cda 100644
--- a/ios/chrome/browser/bookmarks/ui_bundled/folder_editor/BUILD.gn
+++ b/ios/chrome/browser/bookmarks/ui_bundled/folder_editor/BUILD.gn
@@ -39,7 +39,6 @@
     "//ios/chrome/browser/bookmarks/ui_bundled:core",
     "//ios/chrome/browser/bookmarks/ui_bundled/folder_chooser:coordinator_headers",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/signin/model",
@@ -69,7 +68,6 @@
     "//ios/chrome/browser/keyboard/ui_bundled",
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/symbols:icons",
diff --git a/ios/chrome/browser/bookmarks/ui_bundled/home/BUILD.gn b/ios/chrome/browser/bookmarks/ui_bundled/home/BUILD.gn
index f26aa89..2fa5e41 100644
--- a/ios/chrome/browser/bookmarks/ui_bundled/home/BUILD.gn
+++ b/ios/chrome/browser/bookmarks/ui_bundled/home/BUILD.gn
@@ -54,7 +54,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/bring_android_tabs/model/BUILD.gn b/ios/chrome/browser/bring_android_tabs/model/BUILD.gn
index 7986d35..894ef2da 100644
--- a/ios/chrome/browser/bring_android_tabs/model/BUILD.gn
+++ b/ios/chrome/browser/bring_android_tabs/model/BUILD.gn
@@ -22,7 +22,6 @@
     "//components/sync_device_info",
     "//components/url_formatter",
     "//ios/chrome/browser/segmentation_platform/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/utils",
@@ -82,7 +81,6 @@
     "//ios/chrome/browser/first_run/model",
     "//ios/chrome/browser/segmentation_platform/model",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
diff --git a/ios/chrome/browser/bring_android_tabs/model/bring_android_tabs_to_ios_service_factory.mm b/ios/chrome/browser/bring_android_tabs/model/bring_android_tabs_to_ios_service_factory.mm
index 6b065d8..dcd95b3 100644
--- a/ios/chrome/browser/bring_android_tabs/model/bring_android_tabs_to_ios_service_factory.mm
+++ b/ios/chrome/browser/bring_android_tabs/model/bring_android_tabs_to_ios_service_factory.mm
@@ -73,7 +73,7 @@
       browser_state ? browser_state->GetPrefs() : nullptr;
   return std::make_unique<BringAndroidTabsToIOSService>(
       segmentation_platform::SegmentationPlatformServiceFactory::
-          GetDispatcherForBrowserState(browser_state),
+          GetDispatcherForProfile(browser_state),
       SyncServiceFactory::GetForBrowserState(browser_state),
       SessionSyncServiceFactory::GetForBrowserState(browser_state),
       browser_state_prefs);
diff --git a/ios/chrome/browser/bring_android_tabs/ui_bundled/bring_android_tabs_prompt_mediator_unittest.mm b/ios/chrome/browser/bring_android_tabs/ui_bundled/bring_android_tabs_prompt_mediator_unittest.mm
index bd78dd1..cf503437 100644
--- a/ios/chrome/browser/bring_android_tabs/ui_bundled/bring_android_tabs_prompt_mediator_unittest.mm
+++ b/ios/chrome/browser/bring_android_tabs/ui_bundled/bring_android_tabs_prompt_mediator_unittest.mm
@@ -40,7 +40,7 @@
     // Create the BringAndroidTabsToIOSService.
     segmentation_platform::DeviceSwitcherResultDispatcher* dispatcher =
         segmentation_platform::SegmentationPlatformServiceFactory::
-            GetDispatcherForBrowserState(browser_state_.get());
+            GetDispatcherForProfile(browser_state_.get());
     syncer::SyncService* sync_service =
         SyncServiceFactory::GetForBrowserState(browser_state_.get());
     sync_sessions::SessionSyncService* session_sync_service =
diff --git a/ios/chrome/browser/bring_android_tabs/ui_bundled/tab_list_from_android_mediator_unittest.mm b/ios/chrome/browser/bring_android_tabs/ui_bundled/tab_list_from_android_mediator_unittest.mm
index 561a533..b9a3043 100644
--- a/ios/chrome/browser/bring_android_tabs/ui_bundled/tab_list_from_android_mediator_unittest.mm
+++ b/ios/chrome/browser/bring_android_tabs/ui_bundled/tab_list_from_android_mediator_unittest.mm
@@ -63,7 +63,7 @@
   void CreateBringAndroidTabsToIOSService() {
     segmentation_platform::DeviceSwitcherResultDispatcher* dispatcher =
         segmentation_platform::SegmentationPlatformServiceFactory::
-            GetDispatcherForBrowserState(browser_state_.get());
+            GetDispatcherForProfile(browser_state_.get());
     syncer::SyncService* sync_service =
         SyncServiceFactory::GetForBrowserState(browser_state_.get());
     sync_sessions::SessionSyncService* session_sync_service =
diff --git a/ios/chrome/browser/browser_state/model/BUILD.gn b/ios/chrome/browser/browser_state/model/BUILD.gn
index 6a57e077..9c2efd5 100644
--- a/ios/chrome/browser/browser_state/model/BUILD.gn
+++ b/ios/chrome/browser/browser_state/model/BUILD.gn
@@ -135,7 +135,6 @@
     "//ios/chrome/browser/share_extension/model",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
diff --git a/ios/chrome/browser/browser_state_metrics/model/BUILD.gn b/ios/chrome/browser/browser_state_metrics/model/BUILD.gn
index 00c7b421..50b4887 100644
--- a/ios/chrome/browser/browser_state_metrics/model/BUILD.gn
+++ b/ios/chrome/browser/browser_state_metrics/model/BUILD.gn
@@ -16,8 +16,8 @@
     "//ios/chrome/app/application_delegate:observing_app_state_agent",
     "//ios/chrome/app/profile",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
+    "//ios/web/public",
   ]
 }
diff --git a/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn b/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn
index cd2a5bf3..8aec2e5e 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn
@@ -164,7 +164,6 @@
     "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
index 0f3b73e0..7d4ae5d 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
@@ -153,7 +153,6 @@
 #import "ios/chrome/browser/shared/public/commands/feed_commands.h"
 #import "ios/chrome/browser/shared/public/commands/find_in_page_commands.h"
 #import "ios/chrome/browser/shared/public/commands/help_commands.h"
-#import "ios/chrome/browser/shared/public/commands/lens_overlay_commands.h"
 #import "ios/chrome/browser/shared/public/commands/load_query_commands.h"
 #import "ios/chrome/browser/shared/public/commands/mini_map_commands.h"
 #import "ios/chrome/browser/shared/public/commands/new_tab_page_commands.h"
@@ -1671,17 +1670,6 @@
 }
 
 - (void)sharePage {
-  if (IsLensOverlayAvailable()) {
-    // Remap share button as Lens overlay entrypoint if the feature is enabled
-    // otherwise fallback to the share page logic. This is a temporary override
-    // and a dedicated button will be used before releasing this feature.
-    [HandlerForProtocol(self.dispatcher, LensOverlayCommands)
-        createAndShowLensUI:YES
-                 entrypoint:LensOverlayEntrypoint::kLocationBar];
-
-    return;
-  }
-
   // Defocus Find-In-Page before opening the share sheet. This will result in
   // closing the Find-In-Page for some OS versions.
   [self defocusFindInPage];
diff --git a/ios/chrome/browser/browsing_data/model/BUILD.gn b/ios/chrome/browser/browsing_data/model/BUILD.gn
index bf422e5..ef8b063 100644
--- a/ios/chrome/browser/browsing_data/model/BUILD.gn
+++ b/ios/chrome/browser/browsing_data/model/BUILD.gn
@@ -110,7 +110,6 @@
     "//components/browsing_data/core",
     "//ios/chrome/browser/sessions/model:session_restoration_service",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/web/public",
diff --git a/ios/chrome/browser/bubble/ui_bundled/BUILD.gn b/ios/chrome/browser/bubble/ui_bundled/BUILD.gn
index b92ade3..c84c003 100644
--- a/ios/chrome/browser/bubble/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/bubble/ui_bundled/BUILD.gn
@@ -38,7 +38,6 @@
     "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
diff --git a/ios/chrome/browser/bubble/ui_bundled/bubble_presenter_coordinator.mm b/ios/chrome/browser/bubble/ui_bundled/bubble_presenter_coordinator.mm
index 689dcb5e..629623c 100644
--- a/ios/chrome/browser/bubble/ui_bundled/bubble_presenter_coordinator.mm
+++ b/ios/chrome/browser/bubble/ui_bundled/bubble_presenter_coordinator.mm
@@ -88,7 +88,7 @@
   if (!browserState->IsOffTheRecord()) {
     deviceSwitcherResultDispatcher =
         segmentation_platform::SegmentationPlatformServiceFactory::
-            GetDispatcherForBrowserState(browserState);
+            GetDispatcherForProfile(browserState);
   }
   CommandDispatcher* commandDispatcher = self.browser->GetCommandDispatcher();
 
diff --git a/ios/chrome/browser/commerce/model/BUILD.gn b/ios/chrome/browser/commerce/model/BUILD.gn
index 6a6debf..7794f41 100644
--- a/ios/chrome/browser/commerce/model/BUILD.gn
+++ b/ios/chrome/browser/commerce/model/BUILD.gn
@@ -19,7 +19,6 @@
     "//components/unified_consent:unified_consent",
     "//ios/chrome/browser/optimization_guide/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/commerce/model/price_notifications/BUILD.gn b/ios/chrome/browser/commerce/model/price_notifications/BUILD.gn
index 4e59beca..363b219 100644
--- a/ios/chrome/browser/commerce/model/price_notifications/BUILD.gn
+++ b/ios/chrome/browser/commerce/model/price_notifications/BUILD.gn
@@ -14,7 +14,6 @@
     "//ios/chrome/browser/commerce/model:shopping_service",
     "//ios/chrome/browser/commerce/model/push_notification",
     "//ios/chrome/browser/feature_engagement/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/web/public",
diff --git a/ios/chrome/browser/commerce/model/push_notification/BUILD.gn b/ios/chrome/browser/commerce/model/push_notification/BUILD.gn
index 465be9e..72cfc6da 100644
--- a/ios/chrome/browser/commerce/model/push_notification/BUILD.gn
+++ b/ios/chrome/browser/commerce/model/push_notification/BUILD.gn
@@ -25,7 +25,6 @@
     "//ios/chrome/browser/optimization_guide/model",
     "//ios/chrome/browser/push_notification/model:push_notification_client",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//url:url",
   ]
@@ -55,7 +54,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/url_loading/model",
     "//ios/chrome/browser/url_loading/model:test_support",
diff --git a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.h b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.h
index 658e25d..90acd3c 100644
--- a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.h
+++ b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.h
@@ -29,9 +29,9 @@
   ~CommercePushNotificationClient() override;
 
   // Override PushNotificationClient::
-  void HandleNotificationInteraction(
+  bool HandleNotificationInteraction(
       UNNotificationResponse* notification_response) override;
-  UIBackgroundFetchResult HandleNotificationReception(
+  std::optional<UIBackgroundFetchResult> HandleNotificationReception(
       NSDictionary<NSString*, id>* notification) override;
   NSArray<UNNotificationCategory*>* RegisterActionableNotifications() override;
 
@@ -48,7 +48,7 @@
 
   // Handle the interaction from the user be it tapping the notification or
   // long pressing and then presing 'Visit Site' or 'Untrack Price'.
-  void HandleNotificationInteraction(NSString* action_identifier,
+  bool HandleNotificationInteraction(NSString* action_identifier,
                                      NSDictionary* user_info,
                                      base::OnceClosure completion);
 };
diff --git a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.mm b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.mm
index 566172a..4d2293a 100644
--- a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.mm
+++ b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.mm
@@ -72,33 +72,34 @@
   return hint_notification_payload;
 }
 
-void CommercePushNotificationClient::HandleNotificationInteraction(
+bool CommercePushNotificationClient::HandleNotificationInteraction(
     UNNotificationResponse* notification_response) {
   NSDictionary* user_info =
       notification_response.notification.request.content.userInfo;
   DCHECK(user_info);
-  HandleNotificationInteraction(notification_response.actionIdentifier,
-                                user_info, base::DoNothing());
+  return HandleNotificationInteraction(notification_response.actionIdentifier,
+                                       user_info, base::DoNothing());
 }
 
-UIBackgroundFetchResult
+std::optional<UIBackgroundFetchResult>
 CommercePushNotificationClient::HandleNotificationReception(
     NSDictionary<NSString*, id>* notification) {
-  base::RecordAction(base::UserMetricsAction(
-      "Commerce.PriceTracking.PushNotification.Received"));
   OptimizationGuideService* optimization_guide_service =
       OptimizationGuideServiceFactory::GetForBrowserState(GetAnyProfile());
   std::unique_ptr<optimization_guide::proto::HintNotificationPayload>
       hint_notification_payload = ParseHintNotificationPayload(
           [notification objectForKey:kSerializedPayloadKey]);
   if (hint_notification_payload) {
+    base::RecordAction(base::UserMetricsAction(
+        "Commerce.PriceTracking.PushNotification.Received"));
     optimization_guide::PushNotificationManager* push_notification_manager =
         optimization_guide_service->GetHintsManager()
             ->push_notification_manager();
     push_notification_manager->OnNewPushNotification(
         *hint_notification_payload);
+    return UIBackgroundFetchResultNoData;
   }
-  return UIBackgroundFetchResultNoData;
+  return std::nullopt;
 }
 
 NSArray<UNNotificationCategory*>*
@@ -128,7 +129,7 @@
   return ios::BookmarkModelFactory::GetForBrowserState(GetAnyProfile());
 }
 
-void CommercePushNotificationClient::HandleNotificationInteraction(
+bool CommercePushNotificationClient::HandleNotificationInteraction(
     NSString* action_identifier,
     NSDictionary* user_info,
     base::OnceClosure completion) {
@@ -138,7 +139,7 @@
               [user_info objectForKey:kSerializedPayloadKey]);
   if (!hint_notification_payload) {
     std::move(completion).Run();
-    return;
+    return false;
   }
 
   commerce::PriceDropNotificationPayload price_drop_notification;
@@ -146,7 +147,7 @@
       !price_drop_notification.ParseFromString(
           hint_notification_payload->payload().value())) {
     std::move(completion).Run();
-    return;
+    return false;
   }
 
   // TODO(crbug.com/40238314) handle the user tapping 'untrack price'.
@@ -173,7 +174,7 @@
                               bookmark != nil);
     if (!bookmark) {
       std::move(completion).Run();
-      return;
+      return true;
     }
     commerce::SetPriceTrackingStateForBookmark(
         GetShoppingService(), GetBookmarkModel(), bookmark, false,
@@ -182,4 +183,5 @@
                                     success);
         }).Then(std::move(completion)));
   }
+  return true;
 }
diff --git a/ios/chrome/browser/consent_auditor/model/BUILD.gn b/ios/chrome/browser/consent_auditor/model/BUILD.gn
index 1bc7360..8969c43 100644
--- a/ios/chrome/browser/consent_auditor/model/BUILD.gn
+++ b/ios/chrome/browser/consent_auditor/model/BUILD.gn
@@ -15,7 +15,6 @@
     "//components/sync/model",
     "//components/version_info",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/sync/model:data_type_store_service_factory",
     "//ios/chrome/common",
diff --git a/ios/chrome/browser/content_notification/model/BUILD.gn b/ios/chrome/browser/content_notification/model/BUILD.gn
index 83f9795..818abea 100644
--- a/ios/chrome/browser/content_notification/model/BUILD.gn
+++ b/ios/chrome/browser/content_notification/model/BUILD.gn
@@ -24,7 +24,6 @@
     ":content_notification_service",
     "//components/keyed_service/ios",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
     "//ios/public/provider/chrome/browser/content_notification:content_notification_api",
@@ -82,7 +81,6 @@
     "//ios/chrome/browser/push_notification/model:constants",
     "//ios/chrome/browser/push_notification/model:push_notification_client",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/utils",
diff --git a/ios/chrome/browser/content_notification/model/content_notification_client.h b/ios/chrome/browser/content_notification/model/content_notification_client.h
index aa37f3e..c4e168b 100644
--- a/ios/chrome/browser/content_notification/model/content_notification_client.h
+++ b/ios/chrome/browser/content_notification/model/content_notification_client.h
@@ -17,8 +17,8 @@
   ~ContentNotificationClient() override;
 
   // Override PushNotificationClient::
-  void HandleNotificationInteraction(UNNotificationResponse* response) override;
-  UIBackgroundFetchResult HandleNotificationReception(
+  bool HandleNotificationInteraction(UNNotificationResponse* response) override;
+  std::optional<UIBackgroundFetchResult> HandleNotificationReception(
       NSDictionary<NSString*, id>* payload) override;
   NSArray<UNNotificationCategory*>* RegisterActionableNotifications() override;
 };
diff --git a/ios/chrome/browser/content_notification/model/content_notification_client.mm b/ios/chrome/browser/content_notification/model/content_notification_client.mm
index 9096d20..e76cc76 100644
--- a/ios/chrome/browser/content_notification/model/content_notification_client.mm
+++ b/ios/chrome/browser/content_notification/model/content_notification_client.mm
@@ -20,13 +20,13 @@
 
 ContentNotificationClient::~ContentNotificationClient() = default;
 
-void ContentNotificationClient::HandleNotificationInteraction(
+bool ContentNotificationClient::HandleNotificationInteraction(
     UNNotificationResponse* response) {
   // Need to check if it is a content notification first to avoid conflicts with
   // other clients.
   if (![response.notification.request.content.categoryIdentifier
           isEqualToString:kContentNotificationFeedbackCategoryIdentifier]) {
-    return;
+    return false;
   }
   // In order to send delivered NAUs, the payload has been modified for it to be
   // processed on `HandleNotificationReception()`. Before reusing the payload,
@@ -66,7 +66,7 @@
     if (url.is_empty()) {
       base::UmaHistogramBoolean("ContentNotifications.OpenURLAction.HasURL",
                                 false);
-      return;
+      return true;
     }
     base::UmaHistogramBoolean("ContentNotifications.OpenURLAction.HasURL",
                               true);
@@ -82,14 +82,16 @@
   // TODO(crbug.com/337871560): Three way patch NAU and adding completion
   // handler.
   contentNotificationService->SendNAUForConfiguration(config);
+  return true;
 }
 
 // TODO(crbug.com/338875261): Add background refresh support.
 // Delivered NAUs are currently being sent from the push_notification_delegate,
 // and in the future they should be here once background refresh is available.
-UIBackgroundFetchResult ContentNotificationClient::HandleNotificationReception(
+std::optional<UIBackgroundFetchResult>
+ContentNotificationClient::HandleNotificationReception(
     NSDictionary<NSString*, id>* payload) {
-  return UIBackgroundFetchResultNoData;
+  return std::nullopt;
 }
 
 NSArray<UNNotificationCategory*>*
diff --git a/ios/chrome/browser/content_notification/model/content_notification_client_unittest.mm b/ios/chrome/browser/content_notification/model/content_notification_client_unittest.mm
index 113feb5..c1572a3a 100644
--- a/ios/chrome/browser/content_notification/model/content_notification_client_unittest.mm
+++ b/ios/chrome/browser/content_notification/model/content_notification_client_unittest.mm
@@ -95,7 +95,7 @@
 // Tests that HandleNotificationReception does nothing and returns "NoData".
 TEST_F(ContentNotificationClientTest, HandleNotificationReception) {
   EXPECT_EQ(client_->HandleNotificationReception(CreatePayload(NO)),
-            UIBackgroundFetchResultNoData);
+            std::nullopt);
 }
 
 // Tests the appropriate secondary actions are registered.
diff --git a/ios/chrome/browser/context_menu/ui_bundled/BUILD.gn b/ios/chrome/browser/context_menu/ui_bundled/BUILD.gn
index 4bfd3c9..5001d69 100644
--- a/ios/chrome/browser/context_menu/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/context_menu/ui_bundled/BUILD.gn
@@ -26,7 +26,6 @@
     "//ios/chrome/browser/search_engines/model:search_engines_util",
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/context_menu/ui_bundled/link_preview/BUILD.gn b/ios/chrome/browser/context_menu/ui_bundled/link_preview/BUILD.gn
index b5efa49..17a7eff 100644
--- a/ios/chrome/browser/context_menu/ui_bundled/link_preview/BUILD.gn
+++ b/ios/chrome/browser/context_menu/ui_bundled/link_preview/BUILD.gn
@@ -17,7 +17,6 @@
     "//ios/chrome/browser/ntp/ui_bundled:feature_flags",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/tabs/model",
diff --git a/ios/chrome/browser/contextual_panel/model/BUILD.gn b/ios/chrome/browser/contextual_panel/model/BUILD.gn
index 7e7a96f..55762fb 100644
--- a/ios/chrome/browser/contextual_panel/model/BUILD.gn
+++ b/ios/chrome/browser/contextual_panel/model/BUILD.gn
@@ -22,7 +22,6 @@
     "//ios/chrome/browser/price_insights/model",
     "//ios/chrome/browser/price_insights/model:feature",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/contextual_panel/sample/model/BUILD.gn b/ios/chrome/browser/contextual_panel/sample/model/BUILD.gn
index 573404e..9f7aaef 100644
--- a/ios/chrome/browser/contextual_panel/sample/model/BUILD.gn
+++ b/ios/chrome/browser/contextual_panel/sample/model/BUILD.gn
@@ -17,10 +17,11 @@
     "//components/keyed_service/ios",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/contextual_panel/model:public",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/ui/symbols:buildflags",
+    "//ios/web/public",
+    "//ui/base",
   ]
 }
 
diff --git a/ios/chrome/browser/crash_report/model/BUILD.gn b/ios/chrome/browser/crash_report/model/BUILD.gn
index 5b16c48b..17082f4ed 100644
--- a/ios/chrome/browser/crash_report/model/BUILD.gn
+++ b/ios/chrome/browser/crash_report/model/BUILD.gn
@@ -34,7 +34,6 @@
     "//components/upload_list",
     "//ios/chrome/app:tests_hook",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/common",
     "//ios/chrome/common/app_group",
@@ -65,7 +64,6 @@
     "//ios/chrome/browser/infobars/model:public",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/features",
@@ -94,7 +92,6 @@
     "//components/previous_session_info",
     "//ios/chrome/browser/crash_report/model/breadcrumbs",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/shared/model/web_state_list:web_state_list",
     "//ios/chrome/browser/shared/model/web_state_list/test:test_support",
diff --git a/ios/chrome/browser/crash_report/model/breadcrumbs/BUILD.gn b/ios/chrome/browser/crash_report/model/breadcrumbs/BUILD.gn
index ffee56df..f0d1bebf 100644
--- a/ios/chrome/browser/crash_report/model/breadcrumbs/BUILD.gn
+++ b/ios/chrome/browser/crash_report/model/breadcrumbs/BUILD.gn
@@ -14,7 +14,6 @@
     "//ios/chrome/browser/overlays/model",
     "//ios/chrome/browser/overlays/model/public/web_content_area",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/credential_provider/model/BUILD.gn b/ios/chrome/browser/credential_provider/model/BUILD.gn
index c14aa86..06e45310 100644
--- a/ios/chrome/browser/credential_provider/model/BUILD.gn
+++ b/ios/chrome/browser/credential_provider/model/BUILD.gn
@@ -48,7 +48,6 @@
       "//ios/chrome/browser/affiliations/model",
       "//ios/chrome/browser/favicon/model",
       "//ios/chrome/browser/passwords/model",
-      "//ios/chrome/browser/shared/model/browser_state",
       "//ios/chrome/browser/shared/model/profile",
       "//ios/chrome/browser/signin/model",
       "//ios/chrome/browser/signin/model:system_identity",
diff --git a/ios/chrome/browser/credential_provider_promo/ui_bundled/BUILD.gn b/ios/chrome/browser/credential_provider_promo/ui_bundled/BUILD.gn
index 0fbe228f..5eca780b 100644
--- a/ios/chrome/browser/credential_provider_promo/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/credential_provider_promo/ui_bundled/BUILD.gn
@@ -30,7 +30,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:observing_scene_agent",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/data_sharing/model/BUILD.gn b/ios/chrome/browser/data_sharing/model/BUILD.gn
index fe2dcdf..8fc8a04b 100644
--- a/ios/chrome/browser/data_sharing/model/BUILD.gn
+++ b/ios/chrome/browser/data_sharing/model/BUILD.gn
@@ -8,6 +8,8 @@
     "data_sharing_sdk_delegate_ios.mm",
     "data_sharing_service_factory.h",
     "data_sharing_service_factory.mm",
+    "data_sharing_tab_helper.h",
+    "data_sharing_tab_helper.mm",
     "data_sharing_ui_delegate_ios.h",
     "data_sharing_ui_delegate_ios.mm",
   ]
diff --git a/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.h b/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.h
index ab608b2..04be291 100644
--- a/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.h
+++ b/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.h
@@ -15,6 +15,10 @@
 
 // Used by DataSharingService to provide access to SDK.
 class DataSharingSDKDelegateIOS : public DataSharingSDKDelegate {
+ public:
+  explicit DataSharingSDKDelegateIOS();
+  ~DataSharingSDKDelegateIOS() override;
+
   DataSharingSDKDelegateIOS(const DataSharingSDKDelegateIOS&) = delete;
   DataSharingSDKDelegateIOS& operator=(const DataSharingSDKDelegateIOS&) =
       delete;
diff --git a/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.mm b/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.mm
index fe32b64..d361389 100644
--- a/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.mm
+++ b/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.mm
@@ -9,6 +9,9 @@
 
 namespace data_sharing {
 
+DataSharingSDKDelegateIOS::DataSharingSDKDelegateIOS() = default;
+DataSharingSDKDelegateIOS::~DataSharingSDKDelegateIOS() = default;
+
 void DataSharingSDKDelegateIOS::Initialize(
     DataSharingNetworkLoader* data_sharing_network_loader) {
   NOTIMPLEMENTED();
diff --git a/ios/chrome/browser/data_sharing/model/data_sharing_service_factory.mm b/ios/chrome/browser/data_sharing/model/data_sharing_service_factory.mm
index 2174d09..1cd5386 100644
--- a/ios/chrome/browser/data_sharing/model/data_sharing_service_factory.mm
+++ b/ios/chrome/browser/data_sharing/model/data_sharing_service_factory.mm
@@ -15,6 +15,8 @@
 #import "components/keyed_service/core/keyed_service_export.h"
 #import "components/keyed_service/ios/browser_state_dependency_manager.h"
 #import "components/sync/model/data_type_store_service.h"
+#import "ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.h"
+#import "ios/chrome/browser/data_sharing/model/data_sharing_ui_delegate_ios.h"
 #import "ios/chrome/browser/shared/model/browser_state/browser_state_otr_helper.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/signin/model/identity_manager_factory.h"
@@ -41,13 +43,17 @@
       ChromeBrowserState::FromBrowserState(browser_state);
   DCHECK(chrome_browser_state);
 
+  std::unique_ptr<DataSharingUIDelegate> ui_delegate =
+      std::make_unique<DataSharingUIDelegateIOS>();
+  std::unique_ptr<DataSharingSDKDelegate> sdk_delegate =
+      std::make_unique<DataSharingSDKDelegateIOS>();
+
   return std::make_unique<DataSharingServiceImpl>(
       browser_state->GetSharedURLLoaderFactory(),
       IdentityManagerFactory::GetForBrowserState(chrome_browser_state),
       DataTypeStoreServiceFactory::GetForBrowserState(chrome_browser_state)
           ->GetStoreFactory(),
-      ::GetChannel(),
-      /*sdk_delegate=*/nullptr, /*ui_delegate=*/nullptr);
+      ::GetChannel(), std::move(sdk_delegate), std::move(ui_delegate));
 }
 
 }  // namespace
diff --git a/ios/chrome/browser/data_sharing/model/data_sharing_tab_helper.h b/ios/chrome/browser/data_sharing/model/data_sharing_tab_helper.h
new file mode 100644
index 0000000..725062e
--- /dev/null
+++ b/ios/chrome/browser/data_sharing/model/data_sharing_tab_helper.h
@@ -0,0 +1,33 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_DATA_SHARING_MODEL_DATA_SHARING_TAB_HELPER_H_
+#define IOS_CHROME_BROWSER_DATA_SHARING_MODEL_DATA_SHARING_TAB_HELPER_H_
+
+#import "ios/web/public/navigation/web_state_policy_decider.h"
+#import "ios/web/public/web_state_user_data.h"
+
+// A tab helper that intercepts the navigation if the request's URL is for data
+// sharing.
+class DataSharingTabHelper
+    : public web::WebStatePolicyDecider,
+      public web::WebStateUserData<DataSharingTabHelper> {
+ public:
+  ~DataSharingTabHelper() override;
+  DataSharingTabHelper(const DataSharingTabHelper&) = delete;
+  DataSharingTabHelper& operator=(const DataSharingTabHelper&) = delete;
+
+  // web::WebStatePolicyDecider implementation
+  void ShouldAllowRequest(
+      NSURLRequest* request,
+      web::WebStatePolicyDecider::RequestInfo request_info,
+      web::WebStatePolicyDecider::PolicyDecisionCallback callback) override;
+
+ private:
+  explicit DataSharingTabHelper(web::WebState* web_state);
+  friend class web::WebStateUserData<DataSharingTabHelper>;
+  WEB_STATE_USER_DATA_KEY_DECL();
+};
+
+#endif  // IOS_CHROME_BROWSER_DATA_SHARING_MODEL_DATA_SHARING_TAB_HELPER_H_
diff --git a/ios/chrome/browser/data_sharing/model/data_sharing_tab_helper.mm b/ios/chrome/browser/data_sharing/model/data_sharing_tab_helper.mm
new file mode 100644
index 0000000..be1a713
--- /dev/null
+++ b/ios/chrome/browser/data_sharing/model/data_sharing_tab_helper.mm
@@ -0,0 +1,46 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/data_sharing/model/data_sharing_tab_helper.h"
+
+#import "components/data_sharing/public/data_sharing_service.h"
+#import "ios/chrome/browser/data_sharing/model/data_sharing_service_factory.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
+#import "net/base/apple/url_conversions.h"
+#import "url/gurl.h"
+
+DataSharingTabHelper::DataSharingTabHelper(web::WebState* web_state)
+    : web::WebStatePolicyDecider(web_state) {}
+
+DataSharingTabHelper::~DataSharingTabHelper() = default;
+
+void DataSharingTabHelper::ShouldAllowRequest(
+    NSURLRequest* request,
+    web::WebStatePolicyDecider::RequestInfo request_info,
+    web::WebStatePolicyDecider::PolicyDecisionCallback callback) {
+  ChromeBrowserState* chrome_browser_state =
+      ChromeBrowserState::FromBrowserState(web_state()->GetBrowserState());
+  data_sharing::DataSharingService* data_sharing_service =
+      data_sharing::DataSharingServiceFactory::GetForBrowserState(
+          chrome_browser_state);
+
+  GURL url = net::GURLWithNSURL(request.URL);
+  if (data_sharing_service &&
+      data_sharing_service->ShouldInterceptNavigationForShareURL(url)) {
+    data_sharing_service->HandleShareURLNavigationIntercepted(url);
+    std::move(callback).Run(PolicyDecision::Cancel());
+
+    // Close the tab if the url interception ends with an empty page.
+    const GURL& last_committed_url = web_state()->GetLastCommittedURL();
+    if (!last_committed_url.is_valid() || last_committed_url.IsAboutBlank() ||
+        last_committed_url.is_empty()) {
+      web_state()->CloseWebState();
+    }
+    return;
+  }
+
+  std::move(callback).Run(PolicyDecision::Allow());
+}
+
+WEB_STATE_USER_DATA_KEY_IMPL(DataSharingTabHelper)
diff --git a/ios/chrome/browser/data_sharing/model/data_sharing_ui_delegate_ios.mm b/ios/chrome/browser/data_sharing/model/data_sharing_ui_delegate_ios.mm
index 627257a..6ea1f265 100644
--- a/ios/chrome/browser/data_sharing/model/data_sharing_ui_delegate_ios.mm
+++ b/ios/chrome/browser/data_sharing/model/data_sharing_ui_delegate_ios.mm
@@ -8,6 +8,9 @@
 
 namespace data_sharing {
 
+DataSharingUIDelegateIOS::DataSharingUIDelegateIOS() = default;
+DataSharingUIDelegateIOS::~DataSharingUIDelegateIOS() = default;
+
 void DataSharingUIDelegateIOS::HandleShareURLIntercepted(const GURL& url) {
   NOTIMPLEMENTED();
 }
diff --git a/ios/chrome/browser/default_promo/ui_bundled/generic/BUILD.gn b/ios/chrome/browser/default_promo/ui_bundled/generic/BUILD.gn
index a8aa292..423b11d 100644
--- a/ios/chrome/browser/default_promo/ui_bundled/generic/BUILD.gn
+++ b/ios/chrome/browser/default_promo/ui_bundled/generic/BUILD.gn
@@ -28,7 +28,6 @@
     "//ios/chrome/browser/segmentation_platform/model:segmented_default_browser",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/default_promo/ui_bundled/generic/default_browser_generic_promo_coordinator.mm b/ios/chrome/browser/default_promo/ui_bundled/generic/default_browser_generic_promo_coordinator.mm
index 39b485b8b..0dd01c7 100644
--- a/ios/chrome/browser/default_promo/ui_bundled/generic/default_browser_generic_promo_coordinator.mm
+++ b/ios/chrome/browser/default_promo/ui_bundled/generic/default_browser_generic_promo_coordinator.mm
@@ -58,10 +58,10 @@
   if (IsSegmentedDefaultBrowserPromoEnabled()) {
     segmentation_platform::SegmentationPlatformService* segmentationService =
         segmentation_platform::SegmentationPlatformServiceFactory::
-            GetForBrowserState(browserState);
+            GetForProfile(browserState);
     segmentation_platform::DeviceSwitcherResultDispatcher* dispatcher =
         segmentation_platform::SegmentationPlatformServiceFactory::
-            GetDispatcherForBrowserState(browserState);
+            GetDispatcherForProfile(browserState);
 
     _mediator = [[DefaultBrowserGenericPromoMediator alloc]
            initWithSegmentationService:segmentationService
diff --git a/ios/chrome/browser/device_reauth/BUILD.gn b/ios/chrome/browser/device_reauth/BUILD.gn
index 7625716..f9a0be1 100644
--- a/ios/chrome/browser/device_reauth/BUILD.gn
+++ b/ios/chrome/browser/device_reauth/BUILD.gn
@@ -14,7 +14,7 @@
     "//components/device_reauth:device_reauth",
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
-    "//ios/chrome/browser/shared/model/browser_state:browser_state",
+    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/common/ui/reauthentication",
     "//ui/base:base",
diff --git a/ios/chrome/browser/device_sharing/model/BUILD.gn b/ios/chrome/browser/device_sharing/model/BUILD.gn
index a2a9d8a3..a9a5e77 100644
--- a/ios/chrome/browser/device_sharing/model/BUILD.gn
+++ b/ios/chrome/browser/device_sharing/model/BUILD.gn
@@ -23,6 +23,7 @@
     "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
+    "//ios/web/public:web_state_observer",
     "//net",
     "//url",
   ]
diff --git a/ios/chrome/browser/discover_feed/model/BUILD.gn b/ios/chrome/browser/discover_feed/model/BUILD.gn
index 2eda959..cbf1183 100644
--- a/ios/chrome/browser/discover_feed/model/BUILD.gn
+++ b/ios/chrome/browser/discover_feed/model/BUILD.gn
@@ -52,7 +52,6 @@
     "//ios/chrome/browser/ntp/shared/metrics",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/sync/model",
diff --git a/ios/chrome/browser/dom_distiller/model/BUILD.gn b/ios/chrome/browser/dom_distiller/model/BUILD.gn
index ab17988..438205a 100644
--- a/ios/chrome/browser/dom_distiller/model/BUILD.gn
+++ b/ios/chrome/browser/dom_distiller/model/BUILD.gn
@@ -18,6 +18,9 @@
     "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web",
+    "//ios/web/public",
+    "//ios/web/public/thread",
+    "//services/network/public/cpp",
     "//ui/gfx",
     "//url",
   ]
diff --git a/ios/chrome/browser/download/model/background_service/BUILD.gn b/ios/chrome/browser/download/model/background_service/BUILD.gn
index 9634322..e69218db 100644
--- a/ios/chrome/browser/download/model/background_service/BUILD.gn
+++ b/ios/chrome/browser/download/model/background_service/BUILD.gn
@@ -18,7 +18,6 @@
     "//components/leveldb_proto",
     "//components/optimization_guide/core",
     "//ios/chrome/browser/optimization_guide/model:download_client_header",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
   ]
 }
diff --git a/ios/chrome/browser/download/ui_bundled/BUILD.gn b/ios/chrome/browser/download/ui_bundled/BUILD.gn
index 18e16c1..d620aab 100644
--- a/ios/chrome/browser/download/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/download/ui_bundled/BUILD.gn
@@ -52,7 +52,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/layout_guide",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list:web_state_list",
diff --git a/ios/chrome/browser/drag_and_drop/model/BUILD.gn b/ios/chrome/browser/drag_and_drop/model/BUILD.gn
index e1b89011..71ad3369 100644
--- a/ios/chrome/browser/drag_and_drop/model/BUILD.gn
+++ b/ios/chrome/browser/drag_and_drop/model/BUILD.gn
@@ -13,7 +13,6 @@
   ]
   deps = [
     "//base",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/window_activities/model",
diff --git a/ios/chrome/browser/drive_file_picker/coordinator/BUILD.gn b/ios/chrome/browser/drive_file_picker/coordinator/BUILD.gn
index 041359a..5a06f55 100644
--- a/ios/chrome/browser/drive_file_picker/coordinator/BUILD.gn
+++ b/ios/chrome/browser/drive_file_picker/coordinator/BUILD.gn
@@ -21,7 +21,6 @@
     "//ios/chrome/browser/drive_file_picker/ui",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/util",
diff --git a/ios/chrome/browser/enterprise/model/idle/BUILD.gn b/ios/chrome/browser/enterprise/model/idle/BUILD.gn
index 7cc1b7bc..cef6e5b 100644
--- a/ios/chrome/browser/enterprise/model/idle/BUILD.gn
+++ b/ios/chrome/browser/enterprise/model/idle/BUILD.gn
@@ -31,7 +31,6 @@
     "//ios/chrome/browser/discover_feed/model:model",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list:web_state_list",
     "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/external_files/model/BUILD.gn b/ios/chrome/browser/external_files/model/BUILD.gn
index 845c4bf..cfdef7aa 100644
--- a/ios/chrome/browser/external_files/model/BUILD.gn
+++ b/ios/chrome/browser/external_files/model/BUILD.gn
@@ -19,7 +19,6 @@
     "//ios/chrome/browser/bookmarks/model",
     "//ios/chrome/browser/sessions/model",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/find_bar/ui_bundled/BUILD.gn b/ios/chrome/browser/find_bar/ui_bundled/BUILD.gn
index c6591cc..63c0bcb7 100644
--- a/ios/chrome/browser/find_bar/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/find_bar/ui_bundled/BUILD.gn
@@ -27,7 +27,6 @@
     "//ios/chrome/browser/keyboard/ui_bundled",
     "//ios/chrome/browser/main/model",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/find_in_page/model/BUILD.gn b/ios/chrome/browser/find_in_page/model/BUILD.gn
index 9c95a185..85461c9 100644
--- a/ios/chrome/browser/find_in_page/model/BUILD.gn
+++ b/ios/chrome/browser/find_in_page/model/BUILD.gn
@@ -27,7 +27,6 @@
     "//components/ukm/ios:ukm_url_recorder",
     "//ios/chrome/browser/ntp/model",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/ui/fullscreen",
diff --git a/ios/chrome/browser/first_run/ui_bundled/BUILD.gn b/ios/chrome/browser/first_run/ui_bundled/BUILD.gn
index fcc015f7..a469efc5 100644
--- a/ios/chrome/browser/first_run/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/first_run/ui_bundled/BUILD.gn
@@ -47,7 +47,6 @@
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -87,7 +86,6 @@
     "//ios/chrome/browser/search_engine_choice/model",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/ui/screen:screen_provider",
@@ -113,7 +111,6 @@
     "//ios/chrome/browser/crash_report/model",
     "//ios/chrome/browser/first_run/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_screen_coordinator.mm b/ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_screen_coordinator.mm
index 78874268..7746abf0 100644
--- a/ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_screen_coordinator.mm
+++ b/ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_screen_coordinator.mm
@@ -60,11 +60,11 @@
   if (IsSegmentedDefaultBrowserPromoEnabled()) {
     segmentation_platform::SegmentationPlatformService* segmentationService =
         segmentation_platform::SegmentationPlatformServiceFactory::
-            GetForBrowserState(browserState);
+            GetForProfile(browserState);
 
     segmentation_platform::DeviceSwitcherResultDispatcher* dispatcher =
         segmentation_platform::SegmentationPlatformServiceFactory::
-            GetDispatcherForBrowserState(browserState);
+            GetDispatcherForProfile(browserState);
 
     _mediator = [[DefaultBrowserScreenMediator alloc]
            initWithSegmentationService:segmentationService
diff --git a/ios/chrome/browser/first_run/ui_bundled/omnibox_position/BUILD.gn b/ios/chrome/browser/first_run/ui_bundled/omnibox_position/BUILD.gn
index 1a35c9b..a4d75b9 100644
--- a/ios/chrome/browser/first_run/ui_bundled/omnibox_position/BUILD.gn
+++ b/ios/chrome/browser/first_run/ui_bundled/omnibox_position/BUILD.gn
@@ -20,7 +20,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
@@ -46,7 +45,6 @@
     "//ios/chrome/browser/first_run/ui_bundled:constants",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/utils",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/first_run/ui_bundled/omnibox_position/omnibox_position_choice_coordinator.mm b/ios/chrome/browser/first_run/ui_bundled/omnibox_position/omnibox_position_choice_coordinator.mm
index 207171e..29a522f 100644
--- a/ios/chrome/browser/first_run/ui_bundled/omnibox_position/omnibox_position_choice_coordinator.mm
+++ b/ios/chrome/browser/first_run/ui_bundled/omnibox_position/omnibox_position_choice_coordinator.mm
@@ -36,7 +36,7 @@
   if (!self.browser->GetBrowserState()->IsOffTheRecord()) {
     _mediator.deviceSwitcherResultDispatcher =
         segmentation_platform::SegmentationPlatformServiceFactory::
-            GetDispatcherForBrowserState(self.browser->GetBrowserState());
+            GetDispatcherForProfile(self.browser->GetProfile());
   }
 
   _viewController = [[OmniboxPositionChoiceViewController alloc] init];
diff --git a/ios/chrome/browser/first_run/ui_bundled/signin/BUILD.gn b/ios/chrome/browser/first_run/ui_bundled/signin/BUILD.gn
index 6a220e8a..42a7ca473 100644
--- a/ios/chrome/browser/first_run/ui_bundled/signin/BUILD.gn
+++ b/ios/chrome/browser/first_run/ui_bundled/signin/BUILD.gn
@@ -23,7 +23,6 @@
     "//ios/chrome/browser/main/model",
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/signin/model:system_identity",
diff --git a/ios/chrome/browser/first_run/ui_bundled/tos/BUILD.gn b/ios/chrome/browser/first_run/ui_bundled/tos/BUILD.gn
index d8f886a2..6a12c2a 100644
--- a/ios/chrome/browser/first_run/ui_bundled/tos/BUILD.gn
+++ b/ios/chrome/browser/first_run/ui_bundled/tos/BUILD.gn
@@ -16,7 +16,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/util:terms_util",
diff --git a/ios/chrome/browser/follow/model/BUILD.gn b/ios/chrome/browser/follow/model/BUILD.gn
index 01a5b786..10872e2 100644
--- a/ios/chrome/browser/follow/model/BUILD.gn
+++ b/ios/chrome/browser/follow/model/BUILD.gn
@@ -29,7 +29,6 @@
     "//components/prefs",
     "//ios/chrome/browser/ntp/model:util",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
@@ -67,7 +66,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/feature_engagement/model",
     "//ios/chrome/browser/history/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/public/commands",
@@ -110,7 +108,6 @@
     "//components/keyed_service/ios",
     "//components/pref_registry:pref_registry",
     "//ios/chrome/browser/discover_feed/model:discover_feed_factory",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/public/provider/chrome/browser/follow:follow_api",
@@ -141,7 +138,6 @@
     "//ios/chrome/browser/ntp/shared/metrics",
     "//ios/chrome/browser/ntp/shared/metrics:constants",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/gcm/model/BUILD.gn b/ios/chrome/browser/gcm/model/BUILD.gn
index 9d4eeb35..1b979d8 100644
--- a/ios/chrome/browser/gcm/model/BUILD.gn
+++ b/ios/chrome/browser/gcm/model/BUILD.gn
@@ -13,10 +13,12 @@
     "//components/gcm_driver",
     "//components/keyed_service/ios",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/common",
     "//ios/web",
+    "//ios/web/public/thread",
+    "//services/network/public/cpp",
+    "//services/network/public/mojom",
   ]
 }
diff --git a/ios/chrome/browser/gcm/model/instance_id/BUILD.gn b/ios/chrome/browser/gcm/model/instance_id/BUILD.gn
index 50ca4a6..b2179a7 100644
--- a/ios/chrome/browser/gcm/model/instance_id/BUILD.gn
+++ b/ios/chrome/browser/gcm/model/instance_id/BUILD.gn
@@ -12,7 +12,6 @@
     "//components/gcm_driver",
     "//components/keyed_service/ios",
     "//ios/chrome/browser/gcm/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web",
   ]
diff --git a/ios/chrome/browser/history/model/BUILD.gn b/ios/chrome/browser/history/model/BUILD.gn
index 891f3c9d..5b6bd34 100644
--- a/ios/chrome/browser/history/model/BUILD.gn
+++ b/ios/chrome/browser/history/model/BUILD.gn
@@ -38,6 +38,7 @@
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/signin/model",
+    "//ios/web/public/thread",
 
     # TODO(crbug.com/325586862): This should depend on
     # //ios/chrome/browser/sync/model but doing so creates a circular
@@ -78,7 +79,6 @@
     "//components/translate/core/common",
     "//ios/chrome/browser/complex_tasks/model",
     "//ios/chrome/browser/sessions/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/translate/model",
diff --git a/ios/chrome/browser/history/ui_bundled/BUILD.gn b/ios/chrome/browser/history/ui_bundled/BUILD.gn
index a210a55..aa9b5fa2 100644
--- a/ios/chrome/browser/history/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/history/ui_bundled/BUILD.gn
@@ -26,7 +26,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/table_view",
@@ -87,7 +86,6 @@
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/home_customization/coordinator/BUILD.gn b/ios/chrome/browser/home_customization/coordinator/BUILD.gn
index 818291f..4b637bf 100644
--- a/ios/chrome/browser/home_customization/coordinator/BUILD.gn
+++ b/ios/chrome/browser/home_customization/coordinator/BUILD.gn
@@ -20,11 +20,11 @@
     "//ios/chrome/browser/parcel_tracking:features",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/ui/content_suggestions/set_up_list:utils",
     "//ios/chrome/browser/url_loading/model",
+    "//url",
   ]
   frameworks = [ "UIKit.framework" ]
 }
diff --git a/ios/chrome/browser/https_upgrades/model/BUILD.gn b/ios/chrome/browser/https_upgrades/model/BUILD.gn
index fa6027c..17250f0 100644
--- a/ios/chrome/browser/https_upgrades/model/BUILD.gn
+++ b/ios/chrome/browser/https_upgrades/model/BUILD.gn
@@ -88,7 +88,6 @@
     "//base",
     "//components/content_settings/core/browser",
     "//ios/chrome/browser/content_settings/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/test/app:test_support",
     "//ios/components/security_interstitials/https_only_mode",
diff --git a/ios/chrome/browser/intents/BUILD.gn b/ios/chrome/browser/intents/BUILD.gn
index 63d9517..91d9f1f 100644
--- a/ios/chrome/browser/intents/BUILD.gn
+++ b/ios/chrome/browser/intents/BUILD.gn
@@ -53,7 +53,6 @@
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/url_loading/model",
diff --git a/ios/chrome/browser/invalidation/model/BUILD.gn b/ios/chrome/browser/invalidation/model/BUILD.gn
index 0ecf216..28d87c8 100644
--- a/ios/chrome/browser/invalidation/model/BUILD.gn
+++ b/ios/chrome/browser/invalidation/model/BUILD.gn
@@ -17,7 +17,6 @@
     "//components/prefs",
     "//ios/chrome/browser/gcm/model",
     "//ios/chrome/browser/gcm/model/instance_id",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
     "//ios/web",
diff --git a/ios/chrome/browser/lens/model/BUILD.gn b/ios/chrome/browser/lens/model/BUILD.gn
index 95c888e6..23f968e3 100644
--- a/ios/chrome/browser/lens/model/BUILD.gn
+++ b/ios/chrome/browser/lens/model/BUILD.gn
@@ -13,7 +13,6 @@
     "//components/search_engines",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn b/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn
index 39b2c11..b84ff397 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn
+++ b/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn
@@ -56,7 +56,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
index 0deb80da..8628a06 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
@@ -23,7 +23,6 @@
 #import "ios/chrome/browser/lens_overlay/ui/lens_toolbar_consumer.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
diff --git a/ios/chrome/browser/lens_overlay/ui/BUILD.gn b/ios/chrome/browser/lens_overlay/ui/BUILD.gn
index 6252141f..a9f12e7 100644
--- a/ios/chrome/browser/lens_overlay/ui/BUILD.gn
+++ b/ios/chrome/browser/lens_overlay/ui/BUILD.gn
@@ -61,6 +61,19 @@
   ]
 }
 
+source_set("entry_point") {
+  sources = [
+    "lens_overlay_entrypoint_view.h",
+    "lens_overlay_entrypoint_view.mm",
+  ]
+  deps = [
+    "//ios/chrome/browser/shared/public/commands",
+    "//ios/chrome/browser/shared/ui/symbols",
+    "//ios/chrome/common/ui/colors",
+    "//ios/chrome/common/ui/util",
+  ]
+}
+
 source_set("lens_overlay_accessibility_identifier_constants") {
   sources = [
     "lens_overlay_accessibility_identifier_constants.h",
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.h b/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.h
new file mode 100644
index 0000000..ce2540d8
--- /dev/null
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.h
@@ -0,0 +1,17 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_ENTRYPOINT_VIEW_H_
+#define IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_ENTRYPOINT_VIEW_H_
+
+#import <UIKit/UIKit.h>
+
+namespace LensOverlay {
+
+// Returns the location bar lens overlay entrypoint UIButton.
+UIButton* NewEntrypointButton();
+
+}  // namespace LensOverlay
+
+#endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_ENTRYPOINT_VIEW_H_
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.mm
new file mode 100644
index 0000000..422e054
--- /dev/null
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.mm
@@ -0,0 +1,42 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.h"
+
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/util/constraints_ui_util.h"
+#import "ios/chrome/common/ui/util/pointer_interaction_util.h"
+
+namespace LensOverlay {
+
+const CGFloat kLensCameraSymbolPointSize = 18.0;
+
+UIButton* NewEntrypointButton() {
+  UIButton* button = [UIButton buttonWithType:UIButtonTypeSystem];
+
+  button.pointerInteractionEnabled = YES;
+  button.pointerStyleProvider = CreateDefaultEffectCirclePointerStyleProvider();
+  button.tintColor = [UIColor colorNamed:kToolbarButtonColor];
+
+  UIImageSymbolConfiguration* symbolConfig = [UIImageSymbolConfiguration
+      configurationWithPointSize:kLensCameraSymbolPointSize
+                          weight:UIImageSymbolWeightRegular
+                           scale:UIImageSymbolScaleMedium];
+  [button setPreferredSymbolConfiguration:symbolConfig
+                          forImageInState:UIControlStateNormal];
+
+  [button setImage:CustomSymbolWithPointSize(kCameraLensSymbol,
+                                             kLensCameraSymbolPointSize)
+          forState:UIControlStateNormal];
+  button.imageView.contentMode = UIViewContentModeScaleAspectFit;
+
+  [NSLayoutConstraint
+      activateConstraints:@[ [button.widthAnchor
+                              constraintEqualToAnchor:button.heightAnchor] ]];
+
+  return button;
+}
+
+}  // namespace LensOverlay
diff --git a/ios/chrome/browser/location_bar/ui_bundled/BUILD.gn b/ios/chrome/browser/location_bar/ui_bundled/BUILD.gn
index 0a490a3..38995f61 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/location_bar/ui_bundled/BUILD.gn
@@ -48,6 +48,7 @@
     "//ios/chrome/browser/infobars/model:badge",
     "//ios/chrome/browser/infobars/model:public",
     "//ios/chrome/browser/lens_overlay/coordinator:lens_overlay_availability",
+    "//ios/chrome/browser/lens_overlay/ui:entry_point",
     "//ios/chrome/browser/main/model",
     "//ios/chrome/browser/ntp/model",
     "//ios/chrome/browser/ntp/model:util",
@@ -60,7 +61,6 @@
     "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
@@ -125,7 +125,6 @@
     "//ios/chrome/browser/autocomplete/model",
     "//ios/chrome/browser/reading_list/model",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/location_bar/ui_bundled/DEPS b/ios/chrome/browser/location_bar/ui_bundled/DEPS
index ddb3a3e9..ff423236 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/DEPS
+++ b/ios/chrome/browser/location_bar/ui_bundled/DEPS
@@ -22,4 +22,5 @@
   "+ios/chrome/browser/web/model/web_navigation_util.h",
   "+ios/chrome/browser/contextual_panel/entrypoint/ui/contextual_panel_entrypoint_visibility_delegate.h",
   "+ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h",
+  "+ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.h",
 ]
diff --git a/ios/chrome/browser/location_bar/ui_bundled/badges_container_view.mm b/ios/chrome/browser/location_bar/ui_bundled/badges_container_view.mm
index f2a8a42..fb1a4fd 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/badges_container_view.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/badges_container_view.mm
@@ -70,6 +70,7 @@
   _badgeView.translatesAutoresizingMaskIntoConstraints = NO;
   _badgeView.isAccessibilityElement = NO;
   [_containerStackView addArrangedSubview:_badgeView];
+  _badgeView.hidden = YES;
 
   [NSLayoutConstraint activateConstraints:@[
     [_badgeView.heightAnchor
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm b/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
index 423d9a0..9d8a5cc 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
@@ -33,6 +33,7 @@
 #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
 #import "ios/chrome/browser/infobars/model/infobar_metrics_recorder.h"
 #import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h"
+#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.h"
 #import "ios/chrome/browser/location_bar/ui_bundled/location_bar_constants.h"
 #import "ios/chrome/browser/location_bar/ui_bundled/location_bar_consumer.h"
 #import "ios/chrome/browser/location_bar/ui_bundled/location_bar_mediator.h"
@@ -53,6 +54,7 @@
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/commands/help_commands.h"
 #import "ios/chrome/browser/shared/public/commands/lens_commands.h"
+#import "ios/chrome/browser/shared/public/commands/lens_overlay_commands.h"
 #import "ios/chrome/browser/shared/public/commands/load_query_commands.h"
 #import "ios/chrome/browser/shared/public/commands/search_image_with_lens_command.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
@@ -232,8 +234,13 @@
   }
 
   if (!isIncognito && IsLensOverlayAvailable()) {
-    UIView* placeholderView = [[UIView alloc] init];
-    [self.viewController setPlaceholderView:placeholderView];
+    UIButton* lensOverlayEntrypoint = LensOverlay::NewEntrypointButton();
+
+    [lensOverlayEntrypoint addTarget:self
+                              action:@selector(openLensOverlay)
+                    forControlEvents:UIControlEventTouchUpInside];
+
+    [self.viewController setPlaceholderView:lensOverlayEntrypoint];
   }
 
   // Create button factory that wil be used by the ViewController to get
@@ -649,4 +656,11 @@
   [self cancelOmniboxEdit];
 }
 
+// Creates and shows the lens overlay UI.
+- (void)openLensOverlay {
+  [HandlerForProtocol(self.browser->GetCommandDispatcher(), LensOverlayCommands)
+      createAndShowLensUI:YES
+               entrypoint:LensOverlayEntrypoint::kLocationBar];
+}
+
 @end
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.h b/ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.h
index e3cd71c47..fd18ceed 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.h
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.h
@@ -11,6 +11,7 @@
 
 @protocol BadgeViewVisibilityDelegate;
 @protocol ContextualPanelEntrypointVisibilityDelegate;
+@class LocationBarBadgesContainerView;
 
 // A color scheme used for the steady view elements.
 @interface LocationBarSteadyViewColorScheme : NSObject
@@ -42,10 +43,6 @@
 // Sets the location label's text and styles it as if it were placeholder text.
 - (void)setLocationLabelPlaceholderText:(NSString*)string;
 
-// Displays the location badge view if `display` is YES, hides it if
-// `display` is NO. Will animate change if `animated` is YES.
-- (void)displayBadgeView:(BOOL)display animated:(BOOL)animated;
-
 // Reorients the badgeView's position depending on FullScreen mode.
 - (void)setFullScreenCollapsedMode:(BOOL)isFullScreenCollapsed;
 
@@ -90,6 +87,9 @@
 @property(nonatomic, copy) NSString* securityLevelAccessibilityString;
 // Current in-use color scheme.
 @property(nonatomic, strong) LocationBarSteadyViewColorScheme* colorScheme;
+// The view containing the infobar badge and contextual panel entrypoint.
+@property(nonatomic, strong)
+    LocationBarBadgesContainerView* badgesContainerView;
 
 @end
 
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.mm b/ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.mm
index 9e63335..88526e6 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.mm
@@ -36,8 +36,6 @@
 // location bar.
 const CGFloat kShareButtonTrailingSpacing = -11;
 const CGFloat kVoiceSearchButtonTrailingSpacing = -7;
-// Duration of display and hide animation of the badge view, in seconds.
-const CGFloat kbadgeViewAnimationDuration = 0.2;
 // Location label vertical offset.
 const CGFloat kLocationLabelVerticalOffset = -1;
 // The margin from the leading side when not centered.
@@ -56,10 +54,6 @@
 // view.
 @property(nonatomic, strong) UIView* locationContainerView;
 
-// The view containing the infobar badge and contextual panel entrypoint.
-@property(nonatomic, strong)
-    LocationBarBadgesContainerView* badgesContainerView;
-
 // Leading constraint for locationContainerView when there is no BadgeView to
 // its left.
 @property(nonatomic, strong)
@@ -495,19 +489,6 @@
   }
 }
 
-- (void)displayBadgeView:(BOOL)display animated:(BOOL)animated {
-  void (^changeHiddenState)() = ^{
-    self.badgesContainerView.badgeView.hidden = !display;
-    [self updateAccessibility];
-  };
-  if (animated) {
-    [UIView animateWithDuration:kbadgeViewAnimationDuration
-                     animations:changeHiddenState];
-  } else {
-    changeHiddenState();
-  }
-}
-
 - (void)enableTrailingButton:(BOOL)enabled {
   self.trailingButton.enabled = enabled;
   [self updateAccessibility];
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_view_controller.mm b/ios/chrome/browser/location_bar/ui_bundled/location_bar_view_controller.mm
index 8fc15497..bee194d 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_view_controller.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_view_controller.mm
@@ -14,6 +14,7 @@
 #import "components/prefs/pref_service.h"
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h"
+#import "ios/chrome/browser/location_bar/ui_bundled/badges_container_view.h"
 #import "ios/chrome/browser/location_bar/ui_bundled/location_bar_constants.h"
 #import "ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.h"
 #import "ios/chrome/browser/orchestrator/ui_bundled/location_bar_offset_provider.h"
@@ -371,13 +372,11 @@
 }
 
 - (void)hideSteadyViewBadgeAndEntrypointViews {
-  [self.locationBarSteadyView displayBadgeView:NO animated:NO];
-  [self.delegate displayContextualPanelEntrypointView:NO];
+  self.locationBarSteadyView.badgesContainerView.hidden = YES;
 }
 
 - (void)showSteadyViewBadgeAndEntrypointViews {
-  [self.locationBarSteadyView displayBadgeView:YES animated:NO];
-  [self.delegate displayContextualPanelEntrypointView:YES];
+  self.locationBarSteadyView.badgesContainerView.hidden = NO;
 }
 
 - (void)setEditViewFaded:(BOOL)hidden {
diff --git a/ios/chrome/browser/main/model/BUILD.gn b/ios/chrome/browser/main/model/BUILD.gn
index bd62ba1..b94ca9e 100644
--- a/ios/chrome/browser/main/model/BUILD.gn
+++ b/ios/chrome/browser/main/model/BUILD.gn
@@ -35,7 +35,6 @@
     "//ios/chrome/browser/sessions/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/menu/ui_bundled/BUILD.gn b/ios/chrome/browser/menu/ui_bundled/BUILD.gn
index 78a1bf5..ab49146 100644
--- a/ios/chrome/browser/menu/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/menu/ui_bundled/BUILD.gn
@@ -24,7 +24,6 @@
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/metrics/model/BUILD.gn b/ios/chrome/browser/metrics/model/BUILD.gn
index d6d2d335..5bbd864 100644
--- a/ios/chrome/browser/metrics/model/BUILD.gn
+++ b/ios/chrome/browser/metrics/model/BUILD.gn
@@ -12,7 +12,6 @@
     "//components/metrics/demographics",
     "//components/network_time",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/sync/model",
   ]
@@ -137,6 +136,7 @@
     "//ios/chrome/browser/sessions/model:session_restoration_service_factory",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
+    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/browser_state:incognito_session_tracker",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
@@ -253,12 +253,13 @@
     "//ios/chrome/browser/sessions/model:session_restoration_service",
     "//ios/chrome/browser/sessions/model:session_restoration_service_factory",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/components/webui:url_constants",
     "//ios/web",
     "//ios/web/common:user_agent",
+    "//ios/web/public",
+    "//ios/web/public:web_state_observer",
     "//services/metrics/public/cpp:ukm_builders",
     "//ui/base",
     "//url",
diff --git a/ios/chrome/browser/metrics/model/constants.h b/ios/chrome/browser/metrics/model/constants.h
index 8af70b4..d6a1d2a7 100644
--- a/ios/chrome/browser/metrics/model/constants.h
+++ b/ios/chrome/browser/metrics/model/constants.h
@@ -26,6 +26,9 @@
 // Histogram name for the Safety Check Notification Client Status filter.
 extern const char kSafetyCheckNotifClientStatusByProviderHistogram[];
 
+// Histogram name for the Send Tab Notification Client Status filter.
+extern const char kSendTabNotifClientStatusByProviderHistogram[];
+
 // Histogram name for the feed enabled metric.
 extern const char kFeedEnabledHistogram[];
 
diff --git a/ios/chrome/browser/metrics/model/constants.mm b/ios/chrome/browser/metrics/model/constants.mm
index ada555d..ef0d140 100644
--- a/ios/chrome/browser/metrics/model/constants.mm
+++ b/ios/chrome/browser/metrics/model/constants.mm
@@ -17,4 +17,6 @@
     "IOS.Notifications.Tips.ClientStatus.Enabled.ByProvider";
 const char kSafetyCheckNotifClientStatusByProviderHistogram[] =
     "IOS.Notifications.SafetyCheck.ClientStatus.Enabled.ByProvider";
+const char kSendTabNotifClientStatusByProviderHistogram[] =
+    "IOS.Notifications.SendTab.ClientStatus.Enabled.ByProvider";
 const char kFeedEnabledHistogram[] = "ContentSuggestions.Feed.CanBeShown";
diff --git a/ios/chrome/browser/metrics/model/ios_push_notifications_metrics_provider.mm b/ios/chrome/browser/metrics/model/ios_push_notifications_metrics_provider.mm
index 6b14b9a..049f7d94 100644
--- a/ios/chrome/browser/metrics/model/ios_push_notifications_metrics_provider.mm
+++ b/ios/chrome/browser/metrics/model/ios_push_notifications_metrics_provider.mm
@@ -58,6 +58,11 @@
         .client_id = PushNotificationClientId::kSafetyCheck,
         .requires_signed_in_identity = false,
     },
+    {
+        .histogram_name = kSendTabNotifClientStatusByProviderHistogram,
+        .client_id = PushNotificationClientId::kSendTab,
+        .requires_signed_in_identity = true,
+    },
 };
 
 // Records for histogram for `info` for an user signed-in with `gaia_id`.
diff --git a/ios/chrome/browser/metrics/model/ios_push_notifications_metrics_provider_unittest.mm b/ios/chrome/browser/metrics/model/ios_push_notifications_metrics_provider_unittest.mm
index 063a32e40..53bcb279 100644
--- a/ios/chrome/browser/metrics/model/ios_push_notifications_metrics_provider_unittest.mm
+++ b/ios/chrome/browser/metrics/model/ios_push_notifications_metrics_provider_unittest.mm
@@ -136,6 +136,10 @@
   EXPECT_THAT(histogram_tester().GetAllSamples(
                   kSafetyCheckNotifClientStatusByProviderHistogram),
               ::testing::ElementsAre());
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  kSendTabNotifClientStatusByProviderHistogram),
+              ::testing::ElementsAre());
 }
 
 // Tests that ProvideCurrentSessionData(...) records the status of the
@@ -174,6 +178,10 @@
   EXPECT_THAT(histogram_tester().GetAllSamples(
                   kSafetyCheckNotifClientStatusByProviderHistogram),
               ::testing::ElementsAre(base::Bucket(0, 1)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  kSendTabNotifClientStatusByProviderHistogram),
+              ::testing::ElementsAre());
 }
 
 // Tests that ProvideCurrentSessionData(...) records the status of the
@@ -214,4 +222,8 @@
   EXPECT_THAT(histogram_tester().GetAllSamples(
                   kSafetyCheckNotifClientStatusByProviderHistogram),
               ::testing::ElementsAre(base::Bucket(0, 3)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  kSendTabNotifClientStatusByProviderHistogram),
+              ::testing::ElementsAre(base::Bucket(0, 1)));
 }
diff --git a/ios/chrome/browser/mini_map/ui_bundled/BUILD.gn b/ios/chrome/browser/mini_map/ui_bundled/BUILD.gn
index b15e9494..0c2a7330 100644
--- a/ios/chrome/browser/mini_map/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/mini_map/ui_bundled/BUILD.gn
@@ -18,7 +18,6 @@
     "//ios/chrome/browser/mini_map/ui_bundled/resources",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/net/model/BUILD.gn b/ios/chrome/browser/net/model/BUILD.gn
index dd5679a2..7bba8d0 100644
--- a/ios/chrome/browser/net/model/BUILD.gn
+++ b/ios/chrome/browser/net/model/BUILD.gn
@@ -26,12 +26,15 @@
     "//components/pref_registry",
     "//components/prefs",
     "//components/update_client",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/net",
     "//ios/web/common",
+    "//ios/web/public",
+    "//ios/web/public/thread",
     "//net",
     "//net:extras",
+    "//services/network/public/cpp",
+    "//services/network/public/cpp:cpp_base",
     "//url",
   ]
   public_deps = [
diff --git a/ios/chrome/browser/ntp/model/BUILD.gn b/ios/chrome/browser/ntp/model/BUILD.gn
index d93e0fa..b134580 100644
--- a/ios/chrome/browser/ntp/model/BUILD.gn
+++ b/ios/chrome/browser/ntp/model/BUILD.gn
@@ -18,7 +18,6 @@
     "//components/strings",
     "//components/url_formatter",
     "//ios/chrome/browser/discover_feed/model:constants",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
diff --git a/ios/chrome/browser/ntp/ui_bundled/BUILD.gn b/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
index 54449cee..e10245a1 100644
--- a/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
@@ -66,7 +66,6 @@
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
@@ -132,7 +131,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_observer",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
@@ -282,7 +280,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/ntp/ui_bundled/feed_management/BUILD.gn b/ios/chrome/browser/ntp/ui_bundled/feed_management/BUILD.gn
index 75c267b..f085e21 100644
--- a/ios/chrome/browser/ntp/ui_bundled/feed_management/BUILD.gn
+++ b/ios/chrome/browser/ntp/ui_bundled/feed_management/BUILD.gn
@@ -13,11 +13,10 @@
     "//ios/chrome/browser/follow/model:browser_agent",
     "//ios/chrome/browser/follow/ui_bundled",
     "//ios/chrome/browser/net/model:crurl",
+    "//ios/chrome/browser/ntp/shared/metrics",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/ui/table_view",
-    "//ios/chrome/browser/ntp/shared/metrics",
   ]
   public_deps = [ ":navigation_delegate" ]
 }
diff --git a/ios/chrome/browser/ntp/ui_bundled/feed_top_section/BUILD.gn b/ios/chrome/browser/ntp/ui_bundled/feed_top_section/BUILD.gn
index b723cb5..bb2c866 100644
--- a/ios/chrome/browser/ntp/ui_bundled/feed_top_section/BUILD.gn
+++ b/ios/chrome/browser/ntp/ui_bundled/feed_top_section/BUILD.gn
@@ -43,7 +43,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
@@ -98,7 +97,6 @@
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/push_notification/model:push_notification_client",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile/test",
diff --git a/ios/chrome/browser/ntp_tiles/model/BUILD.gn b/ios/chrome/browser/ntp_tiles/model/BUILD.gn
index e02716c..59928c6 100644
--- a/ios/chrome/browser/ntp_tiles/model/BUILD.gn
+++ b/ios/chrome/browser/ntp_tiles/model/BUILD.gn
@@ -22,7 +22,6 @@
     "//ios/chrome/browser/history/model",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/supervised_user/model",
diff --git a/ios/chrome/browser/omaha/model/BUILD.gn b/ios/chrome/browser/omaha/model/BUILD.gn
index fcfb3b1..318ffb7 100644
--- a/ios/chrome/browser/omaha/model/BUILD.gn
+++ b/ios/chrome/browser/omaha/model/BUILD.gn
@@ -21,13 +21,15 @@
     "//ios/chrome/app:tests_hook",
     "//ios/chrome/browser/browser_state_metrics/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/common",
     "//ios/public/provider/chrome/browser/omaha:omaha_api",
     "//ios/public/provider/chrome/browser/raccoon:raccoon_api",
     "//ios/web",
+    "//ios/web/public/thread",
     "//net",
+    "//services/network/public/cpp",
+    "//services/network/public/cpp:cpp_base",
     "//third_party/libxml:xml_writer",
     "//url",
   ]
diff --git a/ios/chrome/browser/optimization_guide/model/BUILD.gn b/ios/chrome/browser/optimization_guide/model/BUILD.gn
index 9235147..b85a5be 100644
--- a/ios/chrome/browser/optimization_guide/model/BUILD.gn
+++ b/ios/chrome/browser/optimization_guide/model/BUILD.gn
@@ -43,6 +43,8 @@
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/signin/model",
     "//ios/web",
+    "//ios/web/public",
+    "//ios/web/public:web_state_observer",
   ]
   frameworks = [ "UIKit.framework" ]
   public_deps = [
diff --git a/ios/chrome/browser/overlays/ui_bundled/infobar_modal/autofill_address_profile/BUILD.gn b/ios/chrome/browser/overlays/ui_bundled/infobar_modal/autofill_address_profile/BUILD.gn
index 22500e8..359e3c77d 100644
--- a/ios/chrome/browser/overlays/ui_bundled/infobar_modal/autofill_address_profile/BUILD.gn
+++ b/ios/chrome/browser/overlays/ui_bundled/infobar_modal/autofill_address_profile/BUILD.gn
@@ -28,7 +28,6 @@
     "//ios/chrome/browser/overlays/ui_bundled/infobar_modal:coordinators",
     "//ios/chrome/browser/overlays/ui_bundled/infobar_modal:mediators",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/ui/util",
diff --git a/ios/chrome/browser/overlays/ui_bundled/infobar_modal/translate/BUILD.gn b/ios/chrome/browser/overlays/ui_bundled/infobar_modal/translate/BUILD.gn
index 3449c24..e63501e 100644
--- a/ios/chrome/browser/overlays/ui_bundled/infobar_modal/translate/BUILD.gn
+++ b/ios/chrome/browser/overlays/ui_bundled/infobar_modal/translate/BUILD.gn
@@ -27,7 +27,6 @@
     "//ios/chrome/browser/overlays/ui_bundled/infobar_modal:coordinators",
     "//ios/chrome/browser/overlays/ui_bundled/infobar_modal:mediators",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/ui/list_model",
     "//ios/chrome/browser/shared/ui/table_view/cells",
diff --git a/ios/chrome/browser/overscroll_actions/model/BUILD.gn b/ios/chrome/browser/overscroll_actions/model/BUILD.gn
index c6b952b..e4bb803 100644
--- a/ios/chrome/browser/overscroll_actions/model/BUILD.gn
+++ b/ios/chrome/browser/overscroll_actions/model/BUILD.gn
@@ -11,7 +11,6 @@
     "//base",
     "//components/keyed_service/ios",
     "//ios/chrome/browser/overscroll_actions/ui_bundled",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web/public",
   ]
diff --git a/ios/chrome/browser/page_content_annotations/model/BUILD.gn b/ios/chrome/browser/page_content_annotations/model/BUILD.gn
index 4255fa88..ce79e43 100644
--- a/ios/chrome/browser/page_content_annotations/model/BUILD.gn
+++ b/ios/chrome/browser/page_content_annotations/model/BUILD.gn
@@ -21,7 +21,6 @@
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/profile",
   ]
diff --git a/ios/chrome/browser/page_info/BUILD.gn b/ios/chrome/browser/page_info/BUILD.gn
index 8713e33..949cbb7 100644
--- a/ios/chrome/browser/page_info/BUILD.gn
+++ b/ios/chrome/browser/page_info/BUILD.gn
@@ -17,9 +17,11 @@
     "//ios/chrome/browser/optimization_guide/model",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile:profile_keyed_service_factory",
+    "//ios/web/public",
+    "//ios/web/public:web_state_observer",
+    "//ios/web/public/navigation",
   ]
 }
 
diff --git a/ios/chrome/browser/parcel_tracking/ui_bundled/BUILD.gn b/ios/chrome/browser/parcel_tracking/ui_bundled/BUILD.gn
index b61610a2..c490928 100644
--- a/ios/chrome/browser/parcel_tracking/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/parcel_tracking/ui_bundled/BUILD.gn
@@ -22,7 +22,6 @@
     "//ios/chrome/browser/parcel_tracking:util",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/passwords/model/BUILD.gn b/ios/chrome/browser/passwords/model/BUILD.gn
index 8820a30e..1141209b 100644
--- a/ios/chrome/browser/passwords/model/BUILD.gn
+++ b/ios/chrome/browser/passwords/model/BUILD.gn
@@ -227,7 +227,6 @@
     "//components/password_manager/core/browser/sharing",
     "//components/sync/base",
     "//components/sync/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/sync/model:data_type_store_service_factory",
     "//ios/chrome/common",
@@ -282,7 +281,6 @@
     "//ios/chrome/browser/passwords/model:store_factory",
     "//ios/chrome/browser/passwords/model/test",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/shared/public/commands",
@@ -352,7 +350,6 @@
     "//components/password_manager/core/browser",
     "//components/password_manager/core/common",
     "//components/prefs",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/test/app:test_support",
     "//ios/testing:nserror_support",
diff --git a/ios/chrome/browser/passwords/ui_bundled/BUILD.gn b/ios/chrome/browser/passwords/ui_bundled/BUILD.gn
index fd7e539..23bad37 100644
--- a/ios/chrome/browser/passwords/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/passwords/ui_bundled/BUILD.gn
@@ -40,7 +40,6 @@
     "//ios/chrome/browser/autofill/ui_bundled/form_input_accessory",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -81,7 +80,6 @@
   deps = [
     "//base",
     "//base/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/test/app:test_support",
   ]
diff --git a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/BUILD.gn b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/BUILD.gn
index c1e7fd8..025018f 100644
--- a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/BUILD.gn
+++ b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/BUILD.gn
@@ -34,7 +34,6 @@
     "//ios/chrome/browser/passwords/model:store_factory",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -133,7 +132,6 @@
     "//base",
     "//components/prefs",
     "//ios/chrome/browser/autofill/model/bottom_sheet",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/common/ui/reauthentication",
diff --git a/ios/chrome/browser/phone_number/ui_bundled/BUILD.gn b/ios/chrome/browser/phone_number/ui_bundled/BUILD.gn
index f6f26a0..25acb67 100644
--- a/ios/chrome/browser/phone_number/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/phone_number/ui_bundled/BUILD.gn
@@ -15,7 +15,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/web/model/annotations",
@@ -83,7 +82,6 @@
   deps = [
     "//base",
     "//base/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/test/app:test_support",
   ]
diff --git a/ios/chrome/browser/photos/model/BUILD.gn b/ios/chrome/browser/photos/model/BUILD.gn
index 73e9b7d1..41c775e 100644
--- a/ios/chrome/browser/photos/model/BUILD.gn
+++ b/ios/chrome/browser/photos/model/BUILD.gn
@@ -56,7 +56,6 @@
     "//components/prefs",
     "//components/signin/public/base",
     "//components/signin/public/identity_manager",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/plus_addresses/coordinator/BUILD.gn b/ios/chrome/browser/plus_addresses/coordinator/BUILD.gn
index dda4747..2c6fd408 100644
--- a/ios/chrome/browser/plus_addresses/coordinator/BUILD.gn
+++ b/ios/chrome/browser/plus_addresses/coordinator/BUILD.gn
@@ -20,7 +20,6 @@
     "//ios/chrome/browser/plus_addresses/ui:ui",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_coordinator.mm b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_coordinator.mm
index 260b2c5e..38a852b 100644
--- a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_coordinator.mm
+++ b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_coordinator.mm
@@ -72,10 +72,11 @@
   ];
   presentationController.preferredCornerRadius = kHalfSheetCornerRadius;
 
+  _mediator.consumer = _viewController;
+
   [self.baseViewController presentViewController:_viewController
                                         animated:YES
                                       completion:nil];
-  _mediator.consumer = _viewController;
 }
 
 - (void)stop {
diff --git a/ios/chrome/browser/plus_addresses/ui/BUILD.gn b/ios/chrome/browser/plus_addresses/ui/BUILD.gn
index b2bda91..376425c 100644
--- a/ios/chrome/browser/plus_addresses/ui/BUILD.gn
+++ b/ios/chrome/browser/plus_addresses/ui/BUILD.gn
@@ -95,7 +95,6 @@
     "//components/plus_addresses:test_support",
     "//components/plus_addresses:types",
     "//ios/chrome/browser/plus_addresses/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/test/app:test_support",
   ]
diff --git a/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.h b/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.h
index 2c4030a..f2347cc 100644
--- a/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.h
+++ b/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.h
@@ -12,8 +12,13 @@
 // app or test code.
 @interface PlusAddressAppInterface : NSObject
 
-// Saves an example plus profile.
-+ (void)saveExamplePlusProfile:(NSString*)url;
+// Setter to enable plus address creation in `FakePlusAddressService` in tests.
++ (void)setShouldOfferPlusAddressCreation:(BOOL)shouldOfferPlusAddressCreation;
+
+// Setter to return no affiliated plus profiles in call to
+// `FakePlusAddressService::GetAffiliatedPlusProfiles`.
++ (void)setShouldReturnNoAffiliatedPlusProfiles:
+    (BOOL)shouldReturnNoAffiliatedPlusProfiles;
 
 @end
 
diff --git a/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.mm b/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.mm
index d8e0e4b..8964e2f 100644
--- a/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.mm
+++ b/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.mm
@@ -6,27 +6,35 @@
 
 #import "base/strings/sys_string_conversions.h"
 #import "components/affiliations/core/browser/affiliation_utils.h"
-#import "components/plus_addresses/plus_address_service.h"
+#import "components/plus_addresses/fake_plus_address_service.h"
 #import "components/plus_addresses/plus_address_test_utils.h"
 #import "components/plus_addresses/plus_address_types.h"
 #import "ios/chrome/browser/plus_addresses/model/plus_address_service_factory.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 
-@implementation PlusAddressAppInterface
+namespace {
 
-+ (void)saveExamplePlusProfile:(NSString*)url {
+plus_addresses::FakePlusAddressService* GetFakePlusAddressService() {
   ChromeBrowserState* browserState =
       chrome_test_util::GetOriginalBrowserState();
-  plus_addresses::PlusAddressService* plusAddressService =
-      PlusAddressServiceFactory::GetForProfile(browserState);
+  return static_cast<plus_addresses::FakePlusAddressService*>(
+      PlusAddressServiceFactory::GetForProfile(browserState));
+}
 
-  plusAddressService->SavePlusProfile(plus_addresses::PlusProfile(
-      /*profile_id=*/"234",
-      affiliations::FacetURI::FromPotentiallyInvalidSpec(
-          base::SysNSStringToUTF8(url)),
-      plus_addresses::PlusAddress(plus_addresses::test::kFakePlusAddress),
-      /*is_confirmed=*/true));
+}  // namespace
+
+@implementation PlusAddressAppInterface
+
++ (void)setShouldOfferPlusAddressCreation:(BOOL)shouldOfferPlusAddressCreation {
+  GetFakePlusAddressService()->set_should_offer_plus_address_creation(
+      shouldOfferPlusAddressCreation);
+}
+
++ (void)setShouldReturnNoAffiliatedPlusProfiles:
+    (BOOL)shouldReturnNoAffiliatedPlusProfiles {
+  GetFakePlusAddressService()->set_should_return_no_affiliated_plus_profiles(
+      shouldReturnNoAffiliatedPlusProfiles);
 }
 
 @end
diff --git a/ios/chrome/browser/policy/model/BUILD.gn b/ios/chrome/browser/policy/model/BUILD.gn
index ae0e721..1783866 100644
--- a/ios/chrome/browser/policy/model/BUILD.gn
+++ b/ios/chrome/browser/policy/model/BUILD.gn
@@ -106,6 +106,7 @@
     "//ios/chrome/browser/signin/model:system_identity",
     "//ios/chrome/browser/ui/authentication/signin:signin_headers",
     "//ios/chrome/common",
+    "//ios/web/public/thread",
     "//services/network/public/cpp",
     "//url",
   ]
@@ -340,7 +341,6 @@
     "//google_apis",
     "//ios/chrome/browser/policy_url_blocking/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/policy/ui_bundled/BUILD.gn b/ios/chrome/browser/policy/ui_bundled/BUILD.gn
index 7120f415..852363f 100644
--- a/ios/chrome/browser/policy/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/policy/ui_bundled/BUILD.gn
@@ -14,7 +14,6 @@
     "//ios/chrome/browser/policy/model:constants",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/signin/model",
   ]
 }
@@ -65,7 +64,6 @@
     "//components/policy/core/common",
     "//ios/chrome/browser/policy/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/ui/authentication/signin",
   ]
diff --git a/ios/chrome/browser/policy_url_blocking/model/BUILD.gn b/ios/chrome/browser/policy_url_blocking/model/BUILD.gn
index 4477149..4635453 100644
--- a/ios/chrome/browser/policy_url_blocking/model/BUILD.gn
+++ b/ios/chrome/browser/policy_url_blocking/model/BUILD.gn
@@ -15,9 +15,9 @@
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
     "//components/policy/core/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web",
+    "//ios/web/public",
     "//net",
   ]
 }
diff --git a/ios/chrome/browser/power_bookmarks/model/BUILD.gn b/ios/chrome/browser/power_bookmarks/model/BUILD.gn
index 7f7ca4d..528f1cd 100644
--- a/ios/chrome/browser/power_bookmarks/model/BUILD.gn
+++ b/ios/chrome/browser/power_bookmarks/model/BUILD.gn
@@ -16,5 +16,6 @@
     "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web",
+    "//ios/web/public/thread",
   ]
 }
diff --git a/ios/chrome/browser/prerender/model/BUILD.gn b/ios/chrome/browser/prerender/model/BUILD.gn
index 95eb56f..ba5c96f 100644
--- a/ios/chrome/browser/prerender/model/BUILD.gn
+++ b/ios/chrome/browser/prerender/model/BUILD.gn
@@ -43,7 +43,6 @@
     "//ios/chrome/browser/sessions/model:session_restoration_service",
     "//ios/chrome/browser/sessions/model:session_restoration_service_factory",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/price_insights/coordinator/BUILD.gn b/ios/chrome/browser/price_insights/coordinator/BUILD.gn
index ee6ea40..bf8618b8 100644
--- a/ios/chrome/browser/price_insights/coordinator/BUILD.gn
+++ b/ios/chrome/browser/price_insights/coordinator/BUILD.gn
@@ -25,7 +25,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/price_insights/model/BUILD.gn b/ios/chrome/browser/price_insights/model/BUILD.gn
index 65ba82f..6d459ff9 100644
--- a/ios/chrome/browser/price_insights/model/BUILD.gn
+++ b/ios/chrome/browser/price_insights/model/BUILD.gn
@@ -22,9 +22,9 @@
     "//ios/chrome/browser/commerce/model:shopping_service",
     "//ios/chrome/browser/contextual_panel/model:public",
     "//ios/chrome/browser/price_insights/resources",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/ui/symbols",
+    "//ios/web/public",
     "//ui/base",
   ]
 }
@@ -64,7 +64,6 @@
     "//components/commerce/core:feature_list",
     "//components/commerce/core:shopping_service",
     "//ios/chrome/browser/commerce/model:shopping_service",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
   ]
 }
diff --git a/ios/chrome/browser/profile/model/BUILD.gn b/ios/chrome/browser/profile/model/BUILD.gn
index bcbc9df3..3d39acd 100644
--- a/ios/chrome/browser/profile/model/BUILD.gn
+++ b/ios/chrome/browser/profile/model/BUILD.gn
@@ -44,9 +44,7 @@
     "//ios/chrome/browser/net/model:network_delegate",
     "//ios/chrome/browser/optimization_guide/model",
     "//ios/chrome/browser/prefs/model",
-    "//ios/chrome/browser/segmentation_platform/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
@@ -86,7 +84,6 @@
     "//ios/chrome/browser/net/model:net_types",
     "//ios/chrome/browser/net/model:network_delegate",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/net",
diff --git a/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm b/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
index 07e272a..1d1b69e3 100644
--- a/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
+++ b/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
@@ -26,13 +26,14 @@
 #import "ios/chrome/browser/profile/model/constants.h"
 #import "ios/chrome/browser/profile/model/off_the_record_profile_ios_impl.h"
 #import "ios/chrome/browser/profile/model/profile_ios_impl.h"
-#import "ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #import "ios/chrome/browser/shared/model/profile/profile_attributes_ios.h"
 #import "ios/chrome/browser/shared/public/features/system_flags.h"
 #import "ios/chrome/browser/signin/model/account_consistency_service_factory.h"
 #import "ios/chrome/browser/signin/model/account_reconcilor_factory.h"
 #import "ios/chrome/browser/signin/model/identity_manager_factory.h"
+#import "ios/chrome/browser/supervised_user/model/child_account_service_factory.h"
+#import "ios/chrome/browser/supervised_user/model/list_family_members_service_factory.h"
 #import "ios/chrome/browser/supervised_user/model/supervised_user_service_factory.h"
 #import "ios/chrome/browser/unified_consent/model/unified_consent_service_factory.h"
 
@@ -496,8 +497,13 @@
     OptimizationGuideServiceFactory::GetForBrowserState(profile)->DoFinalInit(
         BackgroundDownloadServiceFactory::GetForBrowserState(profile));
   }
-  segmentation_platform::SegmentationPlatformServiceFactory::GetForBrowserState(
-      profile);
 
+  // Those services needs to be explicitly initialized and can't simply be
+  // marked as created with the profile as 1. they depend on initialisation
+  // performed in ProfileIOSImpl (thus can't work with TestProfileIOS), and
+  // 2. code do not expect them to be null (thus tests cannot be configured
+  // to have a null instance).
+  ChildAccountServiceFactory::GetForProfile(profile)->Init();
   SupervisedUserServiceFactory::GetForProfile(profile)->Init();
+  ListFamilyMembersServiceFactory::GetForProfile(profile)->Init();
 }
diff --git a/ios/chrome/browser/push_notification/model/BUILD.gn b/ios/chrome/browser/push_notification/model/BUILD.gn
index d78c2f8a..c46cf0f9 100644
--- a/ios/chrome/browser/push_notification/model/BUILD.gn
+++ b/ios/chrome/browser/push_notification/model/BUILD.gn
@@ -17,7 +17,6 @@
     "//components/signin/public/identity_manager",
     "//ios/chrome/browser/commerce/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/web/public/thread",
   ]
@@ -36,7 +35,6 @@
     "//base",
     "//ios/chrome/browser/commerce/model",
     "//ios/chrome/browser/commerce/model/push_notification",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile:profile_keyed_service_factory",
     "//ios/chrome/browser/signin/model",
@@ -87,10 +85,10 @@
     "//ios/chrome/browser/content_notification/model:util",
     "//ios/chrome/browser/safety_check_notifications/model:notification_client",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
+    "//ios/chrome/browser/send_tab_to_self/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features:features",
@@ -98,6 +96,7 @@
     "//ios/chrome/browser/sync/model:device_info_sync_service_factory",
     "//ios/chrome/browser/tips_notifications/model:client",
     "//ios/chrome/common/app_group",
+    "//ios/web/public/thread",
   ]
 }
 
@@ -109,7 +108,6 @@
   ]
   public_deps = [
     "//base",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//url:url",
   ]
   deps = [
@@ -154,7 +152,6 @@
     "//components/sync_preferences:test_support",
     "//ios/chrome/browser/push_notification/model:constants",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
diff --git a/ios/chrome/browser/push_notification/model/DEPS b/ios/chrome/browser/push_notification/model/DEPS
index 5ff9bf2..b268eea 100644
--- a/ios/chrome/browser/push_notification/model/DEPS
+++ b/ios/chrome/browser/push_notification/model/DEPS
@@ -6,6 +6,7 @@
   "+ios/chrome/browser/sync/model",
   "+ios/chrome/browser/tips_notifications/model",
   "+ios/chrome/browser/safety_check_notifications/model",
+  "+ios/chrome/browser/send_tab_to_self/model",
   "+ios/chrome/browser/url_loading",
 ]
 
diff --git a/ios/chrome/browser/push_notification/model/constants.h b/ios/chrome/browser/push_notification/model/constants.h
index d45f26bb..022d777 100644
--- a/ios/chrome/browser/push_notification/model/constants.h
+++ b/ios/chrome/browser/push_notification/model/constants.h
@@ -60,6 +60,9 @@
 // Key of tips notification used in pref `kFeaturePushNotificationPermissions`.
 extern const char kTipsNotificationKey[];
 
+// Key of tips notification used in pref `kFeaturePushNotificationPermissions`.
+extern const char kSendTabNotificationKey[];
+
 // Key of Safety Check notification used in pref
 // `kFeaturePushNotificationPermissions`.
 extern const char kSafetyCheckNotificationKey[];
diff --git a/ios/chrome/browser/push_notification/model/constants.mm b/ios/chrome/browser/push_notification/model/constants.mm
index d4e0d25..e0d69d2 100644
--- a/ios/chrome/browser/push_notification/model/constants.mm
+++ b/ios/chrome/browser/push_notification/model/constants.mm
@@ -9,6 +9,9 @@
 const char kTipsNotificationKey[] = "TIPS";
 const char kSportsNotificationKey[] = "SPORTS";
 const char kSafetyCheckNotificationKey[] = "SAFETY_CHECK";
+const char kSendTabNotificationKey[] = "SEND_TAB";
+
+NSString* const kSendTabNotificationCategoryIdentifier = @"SendTabNotification";
 
 NSString* const kContentNotificationFeedbackActionIdentifier = @"feedback";
 NSString* const kContentNotificationFeedbackCategoryIdentifier =
diff --git a/ios/chrome/browser/push_notification/model/push_notification_account_context_manager.mm b/ios/chrome/browser/push_notification/model/push_notification_account_context_manager.mm
index e3f8ea7..af3d05f 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_account_context_manager.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_account_context_manager.mm
@@ -191,7 +191,8 @@
   switch (clientID) {
     case PushNotificationClientId::kCommerce:
     case PushNotificationClientId::kContent:
-    case PushNotificationClientId::kSports: {
+    case PushNotificationClientId::kSports:
+    case PushNotificationClientId::kSendTab: {
       ChromeBrowserState* browserState = [self chromeBrowserStateFrom:gaiaID];
       if (!browserState) {
         // TODO:(crbug.com/1445551) Restore to DCHECK when signing into Chrome
diff --git a/ios/chrome/browser/push_notification/model/push_notification_client.h b/ios/chrome/browser/push_notification/model/push_notification_client.h
index 7088c61..278e074 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_client.h
+++ b/ios/chrome/browser/push_notification/model/push_notification_client.h
@@ -33,15 +33,17 @@
   virtual ~PushNotificationClient() = 0;
 
   // When the user interacts with a push notification, this function is called
-  // to route the user to the appropriate destination.
-  virtual void HandleNotificationInteraction(
+  // to route the user to the appropriate destination. Returns `true` if the
+  // interaction was handled or `false` if it is not relevant to this client.
+  virtual bool HandleNotificationInteraction(
       UNNotificationResponse* notification_response) = 0;
 
   // When the device receives a push notification, this function is called to
   // allow the client to process any logic needed at this point in time. The
   // function's return value represents the state of data that the
-  // PushNotificationClient fetched.
-  virtual UIBackgroundFetchResult HandleNotificationReception(
+  // PushNotificationClient fetched. Returns nullopt if this client did not
+  // handle the notification reception.
+  virtual std::optional<UIBackgroundFetchResult> HandleNotificationReception(
       NSDictionary<NSString*, id>* user_info) = 0;
 
   // Actionable Notifications are push notifications that provide the user
diff --git a/ios/chrome/browser/push_notification/model/push_notification_client.mm b/ios/chrome/browser/push_notification/model/push_notification_client.mm
index 6dc05ac..38af242 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_client.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_client.mm
@@ -52,6 +52,7 @@
         break;
       case PushNotificationClientId::kTips:
       case PushNotificationClientId::kCommerce:
+      case PushNotificationClientId::kSendTab:
       case PushNotificationClientId::kSafetyCheck:
         // Features do not support feedback.
         NOTREACHED_IN_MIGRATION();
diff --git a/ios/chrome/browser/push_notification/model/push_notification_client_id.h b/ios/chrome/browser/push_notification/model/push_notification_client_id.h
index 8782f6c..1fe7b17 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_client_id.h
+++ b/ios/chrome/browser/push_notification/model/push_notification_client_id.h
@@ -23,7 +23,8 @@
   kTips = 3,
   kSports = 4,
   kSafetyCheck = 5,
-  kMaxValue = kSafetyCheck,
+  kSendTab = 6,
+  kMaxValue = kSendTab,
 };
 // LINT.ThenChange(/tools/metrics/histograms/metadata/ios/enums.xml:PushNotificationClientId)
 
diff --git a/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm b/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm
index 2504ba2..30c0a02e 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm
@@ -8,14 +8,17 @@
 
 #import <vector>
 
+#import "base/feature_list.h"
 #import "base/task/sequenced_task_runner.h"
 #import "components/optimization_guide/core/optimization_guide_features.h"
+#import "components/send_tab_to_self/features.h"
 #import "ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.h"
 #import "ios/chrome/browser/commerce/model/push_notification/push_notification_feature.h"
 #import "ios/chrome/browser/content_notification/model/content_notification_client.h"
 #import "ios/chrome/browser/push_notification/model/constants.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_util.h"
 #import "ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client.h"
+#import "ios/chrome/browser/send_tab_to_self/model/send_tab_push_notification_client.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/tips_notifications/model/tips_notification_client.h"
 
@@ -38,6 +41,12 @@
     AddPushNotificationClient(std::make_unique<SafetyCheckNotificationClient>(
         base::SequencedTaskRunner::GetCurrentDefault()));
   }
+
+  if (base::FeatureList::IsEnabled(
+          send_tab_to_self::kSendTabToSelfIOSPushNotifications)) {
+    AddPushNotificationClient(
+        std::make_unique<SendTabPushNotificationClient>());
+  }
 }
 PushNotificationClientManager::~PushNotificationClientManager() = default;
 
@@ -72,18 +81,15 @@
 UIBackgroundFetchResult
 PushNotificationClientManager::HandleNotificationReception(
     NSDictionary<NSString*, id>* user_info) {
-  UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;
   for (auto& client : clients_) {
-    UIBackgroundFetchResult client_result =
+    std::optional<UIBackgroundFetchResult> client_result =
         client.second->HandleNotificationReception(user_info);
-    if (client_result == UIBackgroundFetchResultNewData) {
-      return UIBackgroundFetchResultNewData;
-    } else if (client_result == UIBackgroundFetchResultFailed) {
-      result = client_result;
+    if (client_result.has_value()) {
+      return client_result.value();
     }
   }
 
-  return result;
+  return UIBackgroundFetchResultNoData;
 }
 
 void PushNotificationClientManager::RegisterActionableNotifications() {
@@ -115,6 +121,10 @@
   if (IsSafetyCheckNotificationsEnabled()) {
     client_ids.push_back(PushNotificationClientId::kSafetyCheck);
   }
+  if (base::FeatureList::IsEnabled(
+          send_tab_to_self::kSendTabToSelfIOSPushNotifications)) {
+    client_ids.push_back(PushNotificationClientId::kSendTab);
+  }
   return client_ids;
 }
 
@@ -142,5 +152,8 @@
     case PushNotificationClientId::kSafetyCheck: {
       return kSafetyCheckNotificationKey;
     }
+    case PushNotificationClientId::kSendTab: {
+      return kSendTabNotificationKey;
+    }
   }
 }
diff --git a/ios/chrome/browser/push_notification/model/push_notification_settings_util.mm b/ios/chrome/browser/push_notification/model/push_notification_settings_util.mm
index 0a9e162..75823326 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_settings_util.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_settings_util.mm
@@ -98,6 +98,7 @@
       return ClientPermissionState::INDETERMINANT;
     }
     case PushNotificationClientId::kContent:
+    case PushNotificationClientId::kSendTab:
     case PushNotificationClientId::kTips:
     case PushNotificationClientId::kSafetyCheck:
     case PushNotificationClientId::kSports: {
diff --git a/ios/chrome/browser/push_notification/model/test_push_notification_client.h b/ios/chrome/browser/push_notification/model/test_push_notification_client.h
index 81f354c..00165f0 100644
--- a/ios/chrome/browser/push_notification/model/test_push_notification_client.h
+++ b/ios/chrome/browser/push_notification/model/test_push_notification_client.h
@@ -13,21 +13,21 @@
   ~TestPushNotificationClient() override;
 
   // Override PushNotificationClient::
-  void HandleNotificationInteraction(
+  bool HandleNotificationInteraction(
       UNNotificationResponse* notification_response) override;
-  UIBackgroundFetchResult HandleNotificationReception(
+  std::optional<UIBackgroundFetchResult> HandleNotificationReception(
       NSDictionary<NSString*, id>* notification) override;
   NSArray<UNNotificationCategory*>* RegisterActionableNotifications() override;
 
   // Indicates whether the client has been
   bool HasNotificationReceivedInteraction();
   // Sets the client's UIBackgroundFetchResult to given FetchResult.
-  void SetBackgroundFetchResult(UIBackgroundFetchResult result);
+  void SetBackgroundFetchResult(std::optional<UIBackgroundFetchResult> result);
   void OnSceneActiveForegroundBrowserReady() override;
   bool IsBrowserReady();
 
  private:
-  UIBackgroundFetchResult fetch_result_ = UIBackgroundFetchResultNoData;
+  std::optional<UIBackgroundFetchResult> fetch_result_ = std::nullopt;
   bool has_notification_received_interaction_ = false;
   bool is_browser_ready_ = false;
 };
diff --git a/ios/chrome/browser/push_notification/model/test_push_notification_client.mm b/ios/chrome/browser/push_notification/model/test_push_notification_client.mm
index 85f92545..548985e 100644
--- a/ios/chrome/browser/push_notification/model/test_push_notification_client.mm
+++ b/ios/chrome/browser/push_notification/model/test_push_notification_client.mm
@@ -11,12 +11,14 @@
 }
 TestPushNotificationClient::~TestPushNotificationClient() = default;
 
-void TestPushNotificationClient::HandleNotificationInteraction(
+bool TestPushNotificationClient::HandleNotificationInteraction(
     UNNotificationResponse* notification) {
   has_notification_received_interaction_ = true;
+  return false;
 }
 
-UIBackgroundFetchResult TestPushNotificationClient::HandleNotificationReception(
+std::optional<UIBackgroundFetchResult>
+TestPushNotificationClient::HandleNotificationReception(
     NSDictionary<NSString*, id>* notification) {
   return fetch_result_;
 }
@@ -32,7 +34,7 @@
 }
 
 void TestPushNotificationClient::SetBackgroundFetchResult(
-    UIBackgroundFetchResult result) {
+    std::optional<UIBackgroundFetchResult> result) {
   fetch_result_ = result;
 }
 
diff --git a/ios/chrome/browser/reading_list/model/BUILD.gn b/ios/chrome/browser/reading_list/model/BUILD.gn
index 151c3d1..3e671da1 100644
--- a/ios/chrome/browser/reading_list/model/BUILD.gn
+++ b/ios/chrome/browser/reading_list/model/BUILD.gn
@@ -88,7 +88,6 @@
     ":model",
     "//base",
     "//components/reading_list/core",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
   ]
   public_deps = [
diff --git a/ios/chrome/browser/rlz/BUILD.gn b/ios/chrome/browser/rlz/BUILD.gn
index 6ba16552..1b8c90b 100644
--- a/ios/chrome/browser/rlz/BUILD.gn
+++ b/ios/chrome/browser/rlz/BUILD.gn
@@ -16,7 +16,6 @@
     "//ios/chrome/browser/google/model",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/public/provider/chrome/browser/app_distribution:app_distribution_api",
     "//ios/web",
diff --git a/ios/chrome/browser/sad_tab/ui_bundled/BUILD.gn b/ios/chrome/browser/sad_tab/ui_bundled/BUILD.gn
index 6f2047a9..fc946ea 100644
--- a/ios/chrome/browser/sad_tab/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/sad_tab/ui_bundled/BUILD.gn
@@ -41,7 +41,6 @@
     "//ios/chrome/browser/overscroll_actions/ui_bundled",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/safe_browsing/model/tailored_security/BUILD.gn b/ios/chrome/browser/safe_browsing/model/tailored_security/BUILD.gn
index 9c0ce0f..5c44b950 100644
--- a/ios/chrome/browser/safe_browsing/model/tailored_security/BUILD.gn
+++ b/ios/chrome/browser/safe_browsing/model/tailored_security/BUILD.gn
@@ -23,7 +23,6 @@
     "//components/signin/public/identity_manager",
     "//ios/chrome/browser/infobars/model",
     "//ios/chrome/browser/overlays/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/sync/model",
@@ -42,7 +41,6 @@
     "//components/safe_browsing/core/browser/tailored_security_service",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//ios/chrome/app/strings",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/components/security_interstitials/safe_browsing",
     "//ios/web/public",
diff --git a/ios/chrome/browser/safety_check/model/BUILD.gn b/ios/chrome/browser/safety_check/model/BUILD.gn
index 6460a83..4b140009 100644
--- a/ios/chrome/browser/safety_check/model/BUILD.gn
+++ b/ios/chrome/browser/safety_check/model/BUILD.gn
@@ -27,7 +27,6 @@
     "//ios/chrome/browser/omaha/model",
     "//ios/chrome/browser/passwords/model:password_checkup_utils",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/safety_check_notifications/model/BUILD.gn b/ios/chrome/browser/safety_check_notifications/model/BUILD.gn
index 2e7857b7..115a4532 100644
--- a/ios/chrome/browser/safety_check_notifications/model/BUILD.gn
+++ b/ios/chrome/browser/safety_check_notifications/model/BUILD.gn
@@ -23,8 +23,8 @@
     "//ios/chrome/browser/safety_check_notifications/utils:constants",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
+    "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/ui/content_suggestions/safety_check",
   ]
   frameworks = [
diff --git a/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client.h b/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client.h
index 325a242..bd79aacc 100644
--- a/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client.h
+++ b/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client.h
@@ -30,9 +30,9 @@
   ~SafetyCheckNotificationClient() override;
 
   // `PushNotificationClient` overrides.
-  void HandleNotificationInteraction(
+  bool HandleNotificationInteraction(
       UNNotificationResponse* notification_response) override;
-  UIBackgroundFetchResult HandleNotificationReception(
+  std::optional<UIBackgroundFetchResult> HandleNotificationReception(
       NSDictionary<NSString*, id>* notification) override;
   NSArray<UNNotificationCategory*>* RegisterActionableNotifications() override;
   void OnSceneActiveForegroundBrowserReady() override;
diff --git a/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client.mm b/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client.mm
index c6a28c6f..6f945fa 100644
--- a/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client.mm
+++ b/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client.mm
@@ -17,8 +17,8 @@
 #import "ios/chrome/browser/safety_check_notifications/utils/utils.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 
 namespace {
 
@@ -49,15 +49,16 @@
 
 SafetyCheckNotificationClient::~SafetyCheckNotificationClient() = default;
 
-void SafetyCheckNotificationClient::HandleNotificationInteraction(
+bool SafetyCheckNotificationClient::HandleNotificationInteraction(
     UNNotificationResponse* response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // TODO(crbug.com/356624000): Implement `HandleNotificationInteraction()` to
   // process user interactions with notifications (e.g., taps, dismissals).
+  return false;
 }
 
-UIBackgroundFetchResult
+std::optional<UIBackgroundFetchResult>
 SafetyCheckNotificationClient::HandleNotificationReception(
     NSDictionary<NSString*, id>* notification) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -65,7 +66,7 @@
   // TODO(crbug.com/356626805): Implement `HandleNotificationReception()` to log
   // notification receipt for analytics/debugging.
 
-  return UIBackgroundFetchResultNoData;
+  return std::nullopt;
 }
 
 NSArray<UNNotificationCategory*>*
diff --git a/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client_unittest.mm b/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client_unittest.mm
index e3423386..b32e9936 100644
--- a/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client_unittest.mm
+++ b/ios/chrome/browser/safety_check_notifications/model/safety_check_notification_client_unittest.mm
@@ -181,7 +181,7 @@
 TEST_F(SafetyCheckNotificationClientTest,
        HandleNotificationReceptionReturnsNoData) {
   EXPECT_EQ(notification_client_->HandleNotificationReception(nil),
-            UIBackgroundFetchResultNoData);
+            std::nullopt);
 }
 
 // Tests that RegisterActionalableNotifications returns an empty array.
diff --git a/ios/chrome/browser/saved_tab_groups/model/BUILD.gn b/ios/chrome/browser/saved_tab_groups/model/BUILD.gn
index 355c7c21..00a16d3d 100644
--- a/ios/chrome/browser/saved_tab_groups/model/BUILD.gn
+++ b/ios/chrome/browser/saved_tab_groups/model/BUILD.gn
@@ -33,7 +33,6 @@
     "//ios/chrome/browser/sessions/model:session_restoration_service_factory",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/model/web_state_list:utils",
diff --git a/ios/chrome/browser/screen_time/model/BUILD.gn b/ios/chrome/browser/screen_time/model/BUILD.gn
index 829b2d66..2998e36 100644
--- a/ios/chrome/browser/screen_time/model/BUILD.gn
+++ b/ios/chrome/browser/screen_time/model/BUILD.gn
@@ -32,7 +32,6 @@
       "//components/keyed_service/core",
       "//components/keyed_service/ios",
       "//ios/chrome/browser/history/model",
-      "//ios/chrome/browser/shared/model/browser_state",
       "//net",
     ]
   }
diff --git a/ios/chrome/browser/search_engine_choice/model/BUILD.gn b/ios/chrome/browser/search_engine_choice/model/BUILD.gn
index 8765da4..100a2ac 100644
--- a/ios/chrome/browser/search_engine_choice/model/BUILD.gn
+++ b/ios/chrome/browser/search_engine_choice/model/BUILD.gn
@@ -14,7 +14,6 @@
     "//ios/chrome/browser/policy/model",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/chrome/browser/ui/search_engine_choice:ui_util",
diff --git a/ios/chrome/browser/segmentation_platform/model/BUILD.gn b/ios/chrome/browser/segmentation_platform/model/BUILD.gn
index d1e7620..2d258df 100644
--- a/ios/chrome/browser/segmentation_platform/model/BUILD.gn
+++ b/ios/chrome/browser/segmentation_platform/model/BUILD.gn
@@ -15,7 +15,6 @@
     ":ukm_client",
     "//base",
     "//components/keyed_service/core",
-    "//components/keyed_service/ios",
     "//components/leveldb_proto",
     "//components/optimization_guide/core",
     "//components/optimization_guide/core:features",
@@ -40,6 +39,7 @@
     "//ios/chrome/browser/shared/model/browser_state:incognito_session_tracker",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/profile",
+    "//ios/chrome/browser/shared/model/profile:profile_keyed_service_factory",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/sync/model",
     "//ios/chrome/browser/sync/model:device_info_sync_service_factory",
diff --git a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.h b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.h
index fabd9e7d..97e48ed 100644
--- a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.h
+++ b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.h
@@ -6,8 +6,8 @@
 #define IOS_CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_SEGMENTATION_PLATFORM_SERVICE_FACTORY_H_
 
 #import "base/no_destructor.h"
-#import "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios_forward.h"
+#import "ios/chrome/browser/shared/model/profile/profile_keyed_service_factory_ios.h"
 
 namespace segmentation_platform {
 
@@ -16,22 +16,16 @@
 
 // Factory for SegmentationPlatformService.
 class SegmentationPlatformServiceFactory
-    : public BrowserStateKeyedServiceFactory {
+    : public ProfileKeyedServiceFactoryIOS {
  public:
-  static SegmentationPlatformService* GetForBrowserState(
-      ChromeBrowserState* context);
+  static SegmentationPlatformService* GetForProfile(ProfileIOS* profile);
 
   static SegmentationPlatformServiceFactory* GetInstance();
 
-  SegmentationPlatformServiceFactory(SegmentationPlatformServiceFactory&) =
-      delete;
-  SegmentationPlatformServiceFactory& operator=(
-      SegmentationPlatformServiceFactory&) = delete;
-
   // Returns the dispatcher used to retrieve or store the classification result
-  // for the user in the given browser state. Do not call for OTR context.
-  static DeviceSwitcherResultDispatcher* GetDispatcherForBrowserState(
-      ChromeBrowserState* context);
+  // for the user in the given profile. Do not call for OTR profiles.
+  static DeviceSwitcherResultDispatcher* GetDispatcherForProfile(
+      ProfileIOS* profile);
 
   // Returns the default factory used to build SegmentationPlatformService. Can
   // be registered with SetTestingFactory to use real instances during testing.
@@ -48,7 +42,6 @@
       web::BrowserState* context) const override;
   void RegisterBrowserStatePrefs(
       user_prefs::PrefRegistrySyncable* registry) override;
-  bool ServiceIsNULLWhileTesting() const override;
 };
 
 }  // namespace segmentation_platform
diff --git a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.mm b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.mm
index 278d721e..ece6293d 100644
--- a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.mm
+++ b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.mm
@@ -13,7 +13,6 @@
 #import "base/task/thread_pool.h"
 #import "base/time/default_clock.h"
 #import "components/keyed_service/core/service_access_type.h"
-#import "components/keyed_service/ios/browser_state_dependency_manager.h"
 #import "components/pref_registry/pref_registry_syncable.h"
 #import "components/segmentation_platform/embedder/default_model/device_switcher_result_dispatcher.h"
 #import "components/segmentation_platform/embedder/home_modules/home_modules_card_registry.h"
@@ -35,7 +34,6 @@
 #import "ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.h"
 #import "ios/chrome/browser/segmentation_platform/model/ukm_database_client.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
-#import "ios/chrome/browser/shared/model/browser_state/browser_state_otr_helper.h"
 #import "ios/chrome/browser/shared/model/browser_state/incognito_session_tracker.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/sync/model/device_info_sync_service_factory.h"
@@ -92,6 +90,16 @@
   base::CallbackListSubscription subscription_;
 };
 
+// Returns the ShoppingService for `weak_profile`.
+ShoppingService* GetShoppingService(base::WeakPtr<ProfileIOS> weak_profile) {
+  if (ProfileIOS* profile = weak_profile.get()) {
+    return commerce::ShoppingServiceFactory::GetForBrowserStateIfExists(
+        profile);
+  }
+
+  return nullptr;
+}
+
 std::unique_ptr<KeyedService> BuildSegmentationPlatformService(
     web::BrowserState* context) {
   DCHECK(context);
@@ -100,26 +108,25 @@
     return nullptr;
   }
 
-  ChromeBrowserState* chrome_browser_state =
-      ChromeBrowserState::FromBrowserState(context);
-  DCHECK(chrome_browser_state);
-  const base::FilePath profile_path = chrome_browser_state->GetStatePath();
+  ProfileIOS* profile = ProfileIOS::FromBrowserState(context);
+  DCHECK(profile);
+  const base::FilePath profile_path = profile->GetStatePath();
   auto* optimization_guide =
-      OptimizationGuideServiceFactory::GetForBrowserState(chrome_browser_state);
+      OptimizationGuideServiceFactory::GetForBrowserState(profile);
 
-  auto* protodb_provider = chrome_browser_state->GetProtoDatabaseProvider();
+  auto* protodb_provider = profile->GetProtoDatabaseProvider();
   if (!protodb_provider) {
     return nullptr;
   }
   sync_sessions::SessionSyncService* session_sync_service =
-      SessionSyncServiceFactory::GetForBrowserState(chrome_browser_state);
+      SessionSyncServiceFactory::GetForBrowserState(profile);
   auto tab_fetcher = std::make_unique<TabFetcher>(session_sync_service);
 
   auto params = std::make_unique<SegmentationPlatformServiceImpl::InitParams>();
   params->profile_id = params->profile_id =
       base::NumberToString(base::PersistentHash(profile_path.value()));
   params->history_service = ios::HistoryServiceFactory::GetForBrowserState(
-      chrome_browser_state, ServiceAccessType::IMPLICIT_ACCESS);
+      profile, ServiceAccessType::IMPLICIT_ACCESS);
   base::TaskPriority priority = base::TaskPriority::BEST_EFFORT;
   if (base::FeatureList::IsEnabled(features::kSegmentationPlatformUserVisibleTaskRunner)) {
     priority = base::TaskPriority::USER_VISIBLE;
@@ -132,13 +139,12 @@
   params->clock = base::DefaultClock::GetInstance();
 
   params->ukm_data_manager =
-      UkmDatabaseClientHolder::GetClientInstance(chrome_browser_state)
-          .GetUkmDataManager();
-  params->profile_prefs = chrome_browser_state->GetPrefs();
+      UkmDatabaseClientHolder::GetClientInstance(profile).GetUkmDataManager();
+  params->profile_prefs = profile->GetPrefs();
 
   auto home_modules_card_registry =
       std::make_unique<home_modules::HomeModulesCardRegistry>(
-          chrome_browser_state->GetPrefs());
+          profile->GetPrefs());
   params->configs =
       GetSegmentationPlatformConfig(home_modules_card_registry.get());
   params->model_provider = std::make_unique<ModelProviderFactoryImpl>(
@@ -148,24 +154,14 @@
   // Guaranteed to outlive the SegmentationPlatformService, which depends on the
   // DeviceInfoSynceService.
   params->device_info_tracker =
-      DeviceInfoSyncServiceFactory::GetForBrowserState(chrome_browser_state)
+      DeviceInfoSyncServiceFactory::GetForBrowserState(profile)
           ->GetDeviceInfoTracker();
   params->input_delegate_holder = SetUpInputDelegates(
       params->configs, session_sync_service, tab_fetcher.get());
 
   // Set up Shopping Service input delegate.
-  auto shopping_service_callback = base::BindRepeating(
-      [](base::WeakPtr<ChromeBrowserState> chrome_browser_state)
-          -> ShoppingService* {
-        ChromeBrowserState* chrome_browser_state_ptr =
-            chrome_browser_state.get();
-        if (!chrome_browser_state_ptr) {
-          return nullptr;
-        }
-        return commerce::ShoppingServiceFactory::GetForBrowserStateIfExists(
-            chrome_browser_state_ptr);
-      },
-      chrome_browser_state->AsWeakPtr());
+  auto shopping_service_callback =
+      base::BindRepeating(&GetShoppingService, profile->AsWeakPtr());
 
   params->input_delegate_holder->SetDelegate(
       proto::CustomInput::FILL_FROM_SHOPPING_SERVICE,
@@ -195,9 +191,9 @@
       kSegmentationDeviceSwitcherUserDataKey,
       std::make_unique<DeviceSwitcherResultDispatcher>(
           service.get(),
-          DeviceInfoSyncServiceFactory::GetForBrowserState(chrome_browser_state)
+          DeviceInfoSyncServiceFactory::GetForBrowserState(profile)
               ->GetDeviceInfoTracker(),
-          chrome_browser_state->GetPrefs(), field_trial_register));
+          profile->GetPrefs(), field_trial_register));
   service->SetUserData(
       kSegmentationTabRankDispatcherUserDataKey,
       std::make_unique<TabRankDispatcher>(service.get(), session_sync_service,
@@ -210,14 +206,13 @@
 }  // namespace
 
 // static
-SegmentationPlatformService*
-SegmentationPlatformServiceFactory::GetForBrowserState(
-    ChromeBrowserState* context) {
+SegmentationPlatformService* SegmentationPlatformServiceFactory::GetForProfile(
+    ProfileIOS* profile) {
   if (!base::FeatureList::IsEnabled(features::kSegmentationPlatformFeature)) {
     return nullptr;
   }
-  return static_cast<SegmentationPlatformService*>(
-      GetInstance()->GetServiceForBrowserState(context, /*create=*/true));
+  return GetInstance()->GetServiceForProfileAs<SegmentationPlatformService>(
+      profile, /*create=*/true);
 }
 
 // static
@@ -228,9 +223,9 @@
 }
 
 SegmentationPlatformServiceFactory::SegmentationPlatformServiceFactory()
-    : BrowserStateKeyedServiceFactory(
-          "SegmentationPlatformService",
-          BrowserStateDependencyManager::GetInstance()) {
+    : ProfileKeyedServiceFactoryIOS("SegmentationPlatformService",
+                                    ServiceCreation::kCreateWithProfile,
+                                    TestingCreation::kNoServiceForTests) {
   DependsOn(OptimizationGuideServiceFactory::GetInstance());
   DependsOn(ios::HistoryServiceFactory::GetInstance());
   DependsOn(DeviceInfoSyncServiceFactory::GetInstance());
@@ -242,10 +237,10 @@
 
 // static
 DeviceSwitcherResultDispatcher*
-SegmentationPlatformServiceFactory::GetDispatcherForBrowserState(
-    ChromeBrowserState* context) {
-  CHECK(!context->IsOffTheRecord());
-  SegmentationPlatformService* service = GetForBrowserState(context);
+SegmentationPlatformServiceFactory::GetDispatcherForProfile(
+    ProfileIOS* profile) {
+  CHECK(!profile->IsOffTheRecord());
+  SegmentationPlatformService* service = GetForProfile(profile);
   if (!service) {
     return nullptr;
   }
@@ -270,8 +265,4 @@
   home_modules::HomeModulesCardRegistry::RegisterProfilePrefs(registry);
 }
 
-bool SegmentationPlatformServiceFactory::ServiceIsNULLWhileTesting() const {
-  return true;
-}
-
 }  // namespace segmentation_platform
diff --git a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory_unittest.mm b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory_unittest.mm
index ec0b41fe..71fbf012 100644
--- a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory_unittest.mm
+++ b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory_unittest.mm
@@ -78,8 +78,8 @@
                 {TestChromeBrowserState::TestingFactory{
                     SegmentationPlatformServiceFactory::GetInstance(),
                     SegmentationPlatformServiceFactory::GetDefaultFactory()}});
-    ASSERT_FALSE(SegmentationPlatformServiceFactory::GetForBrowserState(
-        otr_browser_state));
+    ASSERT_FALSE(
+        SegmentationPlatformServiceFactory::GetForProfile(otr_browser_state));
   }
 
   void TearDown() override {
@@ -145,17 +145,14 @@
   struct ProfileData {
     explicit ProfileData(UkmDataManagerTestUtils* test_utils,
                          const std::string& result_pref)
-        : test_utils(test_utils) {
+        : result_pref(result_pref), test_utils(test_utils) {
       TestChromeBrowserState::Builder builder;
       builder.AddTestingFactory(
           SegmentationPlatformServiceFactory::GetInstance(),
-          SegmentationPlatformServiceFactory::GetDefaultFactory());
+          base::BindOnce(&ProfileData::SetUpEnvironment, base::Unretained(this))
+              .Then(SegmentationPlatformServiceFactory::GetDefaultFactory()));
       browser_state = std::move(builder).Build();
-
-      browser_state->GetPrefs()->SetString(kSegmentationClientResultPrefs,
-                                           result_pref);
-      test_utils->SetupForProfile(browser_state.get());
-      service = SegmentationPlatformServiceFactory::GetForBrowserState(
+      service = SegmentationPlatformServiceFactory::GetForProfile(
           browser_state.get());
     }
 
@@ -163,6 +160,16 @@
 
     ProfileData(ProfileData&) = delete;
 
+    // Setup environment required to create the SegmentationPlatformService.
+    web::BrowserState* SetUpEnvironment(web::BrowserState* context) {
+      ProfileIOS* profile = ProfileIOS::FromBrowserState(context);
+      profile->GetPrefs()->SetString(kSegmentationClientResultPrefs,
+                                     result_pref);
+      test_utils->SetupForProfile(profile);
+      return context;
+    }
+
+    const std::string result_pref;
     const raw_ptr<UkmDataManagerTestUtils> test_utils;
     std::unique_ptr<TestChromeBrowserState> browser_state;
     raw_ptr<SegmentationPlatformService> service;
diff --git a/ios/chrome/browser/segmentation_platform/model/ukm_data_manager_test_utils.mm b/ios/chrome/browser/segmentation_platform/model/ukm_data_manager_test_utils.mm
index eac9b454..c8374a7 100644
--- a/ios/chrome/browser/segmentation_platform/model/ukm_data_manager_test_utils.mm
+++ b/ios/chrome/browser/segmentation_platform/model/ukm_data_manager_test_utils.mm
@@ -118,7 +118,7 @@
   history_service_ = ios::HistoryServiceFactory::GetForBrowserState(
       profile, ServiceAccessType::EXPLICIT_ACCESS);
   // Create the platform to kick off initialization.
-  segmentation_platform::SegmentationPlatformServiceFactory::GetForBrowserState(
+  segmentation_platform::SegmentationPlatformServiceFactory::GetForProfile(
       profile);
 }
 
diff --git a/ios/chrome/browser/send_tab_to_self/model/BUILD.gn b/ios/chrome/browser/send_tab_to_self/model/BUILD.gn
index a1ee0a1..3a501387 100644
--- a/ios/chrome/browser/send_tab_to_self/model/BUILD.gn
+++ b/ios/chrome/browser/send_tab_to_self/model/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "ios_send_tab_to_self_infobar_delegate.h",
     "ios_send_tab_to_self_infobar_delegate.mm",
+    "send_tab_push_notification_client.h",
+    "send_tab_push_notification_client.mm",
     "send_tab_to_self_browser_agent.h",
     "send_tab_to_self_browser_agent.mm",
   ]
@@ -21,6 +23,8 @@
     "//ios/chrome/app/theme:theme_grit",
     "//ios/chrome/browser/infobars/model",
     "//ios/chrome/browser/infobars/model:public",
+    "//ios/chrome/browser/push_notification/model:constants",
+    "//ios/chrome/browser/push_notification/model:push_notification_client",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/send_tab_to_self/model/DEPS b/ios/chrome/browser/send_tab_to_self/model/DEPS
index d08d37bc..5e02cf5 100644
--- a/ios/chrome/browser/send_tab_to_self/model/DEPS
+++ b/ios/chrome/browser/send_tab_to_self/model/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+ios/chrome/browser/infobars/model",
+  "+ios/chrome/browser/push_notification/model",
   "+ios/chrome/browser/sync/model",
 ]
diff --git a/ios/chrome/browser/send_tab_to_self/model/send_tab_push_notification_client.h b/ios/chrome/browser/send_tab_to_self/model/send_tab_push_notification_client.h
new file mode 100644
index 0000000..270f967
--- /dev/null
+++ b/ios/chrome/browser/send_tab_to_self/model/send_tab_push_notification_client.h
@@ -0,0 +1,26 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_SEND_TAB_TO_SELF_MODEL_SEND_TAB_PUSH_NOTIFICATION_CLIENT_H_
+#define IOS_CHROME_BROWSER_SEND_TAB_TO_SELF_MODEL_SEND_TAB_PUSH_NOTIFICATION_CLIENT_H_
+
+#import <Foundation/Foundation.h>
+#import <UserNotifications/UserNotifications.h>
+
+#import "ios/chrome/browser/push_notification/model/push_notification_client.h"
+
+// Client for handling send tab notifications.
+class SendTabPushNotificationClient : public PushNotificationClient {
+ public:
+  SendTabPushNotificationClient();
+  ~SendTabPushNotificationClient() override;
+
+  // Override PushNotificationClient.
+  bool HandleNotificationInteraction(UNNotificationResponse* response) override;
+  std::optional<UIBackgroundFetchResult> HandleNotificationReception(
+      NSDictionary<NSString*, id>* notification) override;
+  NSArray<UNNotificationCategory*>* RegisterActionableNotifications() override;
+};
+
+#endif  // IOS_CHROME_BROWSER_SEND_TAB_TO_SELF_MODEL_SEND_TAB_PUSH_NOTIFICATION_CLIENT_H_
diff --git a/ios/chrome/browser/send_tab_to_self/model/send_tab_push_notification_client.mm b/ios/chrome/browser/send_tab_to_self/model/send_tab_push_notification_client.mm
new file mode 100644
index 0000000..7c93c49
--- /dev/null
+++ b/ios/chrome/browser/send_tab_to_self/model/send_tab_push_notification_client.mm
@@ -0,0 +1,32 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/send_tab_to_self/model/send_tab_push_notification_client.h"
+
+#import "ios/chrome/browser/push_notification/model/constants.h"
+#import "ios/chrome/browser/send_tab_to_self/model/send_tab_to_self_browser_agent.h"
+
+SendTabPushNotificationClient::SendTabPushNotificationClient()
+    : PushNotificationClient(PushNotificationClientId::kSendTab) {}
+
+SendTabPushNotificationClient::~SendTabPushNotificationClient() = default;
+
+bool SendTabPushNotificationClient::HandleNotificationInteraction(
+    UNNotificationResponse* response) {
+  // TODO(crbug.com/343495218): Load URL in new tab and dismiss Send Tab
+  // infobar.
+  return false;
+}
+
+std::optional<UIBackgroundFetchResult>
+SendTabPushNotificationClient::HandleNotificationReception(
+    NSDictionary<NSString*, id>* notification) {
+  // TODO(crbug.com/343495218): Handle notification reception.
+  return UIBackgroundFetchResultNoData;
+}
+
+NSArray<UNNotificationCategory*>*
+SendTabPushNotificationClient::RegisterActionableNotifications() {
+  return @[];
+}
diff --git a/ios/chrome/browser/sessions/model/BUILD.gn b/ios/chrome/browser/sessions/model/BUILD.gn
index 494efdd3..90fd633 100644
--- a/ios/chrome/browser/sessions/model/BUILD.gn
+++ b/ios/chrome/browser/sessions/model/BUILD.gn
@@ -21,7 +21,6 @@
     "//components/keyed_service/ios",
     "//components/tab_groups",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -57,7 +56,6 @@
     ":session_state",
     "//components/previous_session_info",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/web/public",
@@ -128,7 +126,6 @@
     "//components/sessions",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web/public",
     "//ios/web/public/navigation",
@@ -145,7 +142,6 @@
     ":session_restoration_service_factory",
     "//ios/chrome/browser/shared/coordinator/scene:observing_scene_agent",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/web_state_list/model/web_usage_enabler",
@@ -215,7 +211,6 @@
     "//base",
     "//ios/chrome/browser/sessions/model/proto",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/web/public",
@@ -240,7 +235,6 @@
     ":session_service",
     ":session_state",
     "//base",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/web/public",
@@ -270,6 +264,7 @@
     "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
+    "//ios/web/public",
   ]
 }
 
@@ -333,7 +328,6 @@
     "//components/keyed_service/ios",
     "//ios/chrome/browser/sessions/model/proto",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web/public",
     "//ios/web/public/session/proto",
diff --git a/ios/chrome/browser/settings/model/sync/utils/BUILD.gn b/ios/chrome/browser/settings/model/sync/utils/BUILD.gn
index 455fe672..bb3cb4a 100644
--- a/ios/chrome/browser/settings/model/sync/utils/BUILD.gn
+++ b/ios/chrome/browser/settings/model/sync/utils/BUILD.gn
@@ -22,7 +22,6 @@
     "//ios/chrome/browser/infobars/model",
     "//ios/chrome/browser/infobars/model:public",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/symbols",
diff --git a/ios/chrome/browser/share_extension/model/BUILD.gn b/ios/chrome/browser/share_extension/model/BUILD.gn
index 2ac8ea6..6f824d5 100644
--- a/ios/chrome/browser/share_extension/model/BUILD.gn
+++ b/ios/chrome/browser/share_extension/model/BUILD.gn
@@ -24,6 +24,7 @@
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/chrome/common/app_group",
     "//ios/web",
+    "//ios/web/public/thread",
     "//net",
     "//url",
   ]
diff --git a/ios/chrome/browser/shared/coordinator/layout_guide/BUILD.gn b/ios/chrome/browser/shared/coordinator/layout_guide/BUILD.gn
index fd7b1cb5..7014a1a 100644
--- a/ios/chrome/browser/shared/coordinator/layout_guide/BUILD.gn
+++ b/ios/chrome/browser/shared/coordinator/layout_guide/BUILD.gn
@@ -23,7 +23,6 @@
   deps = [
     ":layout_guide_scene_agent",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/ui/util:util_swift",
   ]
diff --git a/ios/chrome/browser/shared/coordinator/scene/BUILD.gn b/ios/chrome/browser/shared/coordinator/scene/BUILD.gn
index ac9b40e..c585aff1 100644
--- a/ios/chrome/browser/shared/coordinator/scene/BUILD.gn
+++ b/ios/chrome/browser/shared/coordinator/scene/BUILD.gn
@@ -131,7 +131,6 @@
     "//ios/chrome/browser/shared/coordinator/default_browser_promo",
     "//ios/chrome/browser/shared/coordinator/layout_guide:layout_guide_scene_agent",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/shared/coordinator/scene/test/BUILD.gn b/ios/chrome/browser/shared/coordinator/scene/test/BUILD.gn
index 3a65b41..d2f040fd 100644
--- a/ios/chrome/browser/shared/coordinator/scene/test/BUILD.gn
+++ b/ios/chrome/browser/shared/coordinator/scene/test/BUILD.gn
@@ -17,7 +17,6 @@
   deps = [
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/ui/main",
diff --git a/ios/chrome/browser/shared/model/browser/test/BUILD.gn b/ios/chrome/browser/shared/model/browser/test/BUILD.gn
index 059aba6..8cfbb23 100644
--- a/ios/chrome/browser/shared/model/browser/test/BUILD.gn
+++ b/ios/chrome/browser/shared/model/browser/test/BUILD.gn
@@ -16,7 +16,6 @@
     "//base",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/model/web_state_list/test:test_support",
diff --git a/ios/chrome/browser/shared/model/browser_state/BUILD.gn b/ios/chrome/browser/shared/model/browser_state/BUILD.gn
index 9f8da0e..76b91c6 100644
--- a/ios/chrome/browser/shared/model/browser_state/BUILD.gn
+++ b/ios/chrome/browser/shared/model/browser_state/BUILD.gn
@@ -7,7 +7,6 @@
     "browser_state_info_cache.h",
     "browser_state_otr_helper.cc",
     "browser_state_otr_helper.h",
-    "chrome_browser_state.h",
     "chrome_browser_state_manager_observer.h",
   ]
 
diff --git a/ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h b/ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h
deleted file mode 100644
index efcaef3..0000000
--- a/ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_SHARED_MODEL_BROWSER_STATE_CHROME_BROWSER_STATE_H_
-#define IOS_CHROME_BROWSER_SHARED_MODEL_BROWSER_STATE_CHROME_BROWSER_STATE_H_
-
-#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
-
-using ProfileIOS = ChromeBrowserState;
-
-#endif  // IOS_CHROME_BROWSER_SHARED_MODEL_BROWSER_STATE_CHROME_BROWSER_STATE_H_
diff --git a/ios/chrome/browser/shared/model/prefs/BUILD.gn b/ios/chrome/browser/shared/model/prefs/BUILD.gn
index 74e4b90..e51ccc2 100644
--- a/ios/chrome/browser/shared/model/prefs/BUILD.gn
+++ b/ios/chrome/browser/shared/model/prefs/BUILD.gn
@@ -107,7 +107,6 @@
     "//ios/chrome/browser/push_notification/model:push_notification_service_header",
     "//ios/chrome/browser/safety_check/model:constants",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/shared/model/profile/test/BUILD.gn b/ios/chrome/browser/shared/model/profile/test/BUILD.gn
index 8f8c193..f35e956 100644
--- a/ios/chrome/browser/shared/model/profile/test/BUILD.gn
+++ b/ios/chrome/browser/shared/model/profile/test/BUILD.gn
@@ -28,11 +28,11 @@
     "//ios/chrome/browser/policy/model",
     "//ios/chrome/browser/prefs/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/supervised_user/model:sync_settings_factory",
     "//ios/chrome/test:test_support",
+    "//ios/web/public/thread",
     "//net:test_support",
   ]
 }
diff --git a/ios/chrome/browser/shared/model/web_state_list/BUILD.gn b/ios/chrome/browser/shared/model/web_state_list/BUILD.gn
index 31edad36..0e9a697 100644
--- a/ios/chrome/browser/shared/model/web_state_list/BUILD.gn
+++ b/ios/chrome/browser/shared/model/web_state_list/BUILD.gn
@@ -53,7 +53,6 @@
   deps = [
     ":web_state_list",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/snapshots/model",
diff --git a/ios/chrome/browser/shared/model/web_state_list/test/BUILD.gn b/ios/chrome/browser/shared/model/web_state_list/test/BUILD.gn
index 5bf4e04..77ff12f 100644
--- a/ios/chrome/browser/shared/model/web_state_list/test/BUILD.gn
+++ b/ios/chrome/browser/shared/model/web_state_list/test/BUILD.gn
@@ -13,7 +13,6 @@
   deps = [
     "//base",
     "//components/tab_groups",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/shared/ui/util/identity_snackbar/identity_snackbar_message_view.mm b/ios/chrome/browser/shared/ui/util/identity_snackbar/identity_snackbar_message_view.mm
index bba4291..4bc6338 100644
--- a/ios/chrome/browser/shared/ui/util/identity_snackbar/identity_snackbar_message_view.mm
+++ b/ios/chrome/browser/shared/ui/util/identity_snackbar/identity_snackbar_message_view.mm
@@ -23,11 +23,11 @@
 #endif
 }
 
-const CGFloat kSnackbarSize = 68.;
 const CGFloat kAvatarSize = 32.;
-const CGFloat kGoogleSize = 32;
-const CGFloat kAvatarMargin = (kSnackbarSize - kAvatarSize) / 2;
-const CGFloat kGoogleMargin = (kSnackbarSize - kGoogleSize) / 2;
+const CGFloat kGoogleSize = 32.;
+const CGFloat kVerticalPadding = 12.;
+const CGFloat kHorizontalPadding = 16.;
+const CGFloat kHorizontalGap = 16.;
 // The offset between both texts.
 const CGFloat kTextOffset = 6.;
 
@@ -89,12 +89,12 @@
     _signedInAsView.text =
         l10n_util::GetNSStringF(IDS_IOS_ACCOUNT_MENU_SWITCH_CONFIRMATION_TITLE,
                                 base::SysNSStringToUTF16(snackbarMessage.name));
-    _signedInAsView.numberOfLines = 0;
+    _signedInAsView.numberOfLines = 1;
 
     _emailView = [[UILabel alloc] init];
     _emailView.adjustsFontForContentSizeCategory = YES;
     _emailView.translatesAutoresizingMaskIntoConstraints = NO;
-    _emailView.numberOfLines = 0;
+    _emailView.numberOfLines = 1;
     _emailView.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
     _emailView.adjustsFontSizeToFitWidth = NO;
@@ -125,58 +125,59 @@
     [self addSubview:_accountBadgeView];
 
     [NSLayoutConstraint activateConstraints:@[
+      // Size constraints
 
-      // Avatar view
       [_avatarView.heightAnchor constraintEqualToConstant:kAvatarSize],
       [_avatarView.widthAnchor constraintEqualToConstant:kAvatarSize],
-      [_avatarView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor
-                                                constant:kAvatarMargin],
-      [_avatarView.topAnchor
-          constraintGreaterThanOrEqualToAnchor:self.topAnchor
-                                      constant:-kAvatarMargin],
-      [_avatarView.bottomAnchor
-          constraintLessThanOrEqualToAnchor:self.bottomAnchor
-                                   constant:kAvatarMargin],
-
-      // Text Views
-      [_textViews.leadingAnchor
-          constraintEqualToAnchor:_avatarView.trailingAnchor
-                         constant:kAvatarMargin],
-      [_textViews.topAnchor
-          constraintGreaterThanOrEqualToAnchor:self.topAnchor],
-      [_textViews.bottomAnchor
-          constraintLessThanOrEqualToAnchor:self.bottomAnchor],
-
-      [_textViews.topAnchor
-          constraintLessThanOrEqualToAnchor:_signedInAsView.topAnchor],
-      [_emailView.topAnchor constraintEqualToAnchor:_signedInAsView.bottomAnchor
-                                           constant:kTextOffset],
-
-      [_textViews.bottomAnchor
-          constraintGreaterThanOrEqualToAnchor:_emailView.bottomAnchor],
-
-      // Google Symbol view
-      [_accountBadgeView.leadingAnchor
-          constraintEqualToAnchor:_textViews.trailingAnchor
-                         constant:kGoogleMargin],
 
       [_accountBadgeView.heightAnchor constraintEqualToConstant:kGoogleSize],
       [_accountBadgeView.widthAnchor constraintEqualToConstant:kGoogleSize],
-      [_accountBadgeView.trailingAnchor
-          constraintEqualToAnchor:self.trailingAnchor
-                         constant:-kGoogleMargin],
+      [_emailView.heightAnchor
+          constraintEqualToConstant:[_emailView intrinsicContentSize].height],
+      [_signedInAsView.heightAnchor
+          constraintEqualToConstant:[_signedInAsView intrinsicContentSize]
+                                        .height],
 
+      // Vertical counstraints from top to bottom.
+      [_avatarView.topAnchor
+          constraintGreaterThanOrEqualToAnchor:self.topAnchor
+                                      constant:kVerticalPadding],
       [_accountBadgeView.topAnchor
           constraintGreaterThanOrEqualToAnchor:self.topAnchor
-                                      constant:-kGoogleMargin],
-      [_accountBadgeView.bottomAnchor
-          constraintLessThanOrEqualToAnchor:self.bottomAnchor
-                                   constant:kGoogleMargin],
+                                      constant:kVerticalPadding],
+      [_textViews.topAnchor constraintEqualToAnchor:self.topAnchor
+                                           constant:kVerticalPadding],
+      [_signedInAsView.topAnchor constraintEqualToAnchor:_textViews.topAnchor],
+      [_emailView.topAnchor constraintEqualToAnchor:_signedInAsView.bottomAnchor
+                                           constant:kTextOffset],
+      [_textViews.bottomAnchor constraintEqualToAnchor:_emailView.bottomAnchor],
+      [self.bottomAnchor constraintEqualToAnchor:_textViews.bottomAnchor
+                                        constant:kVerticalPadding],
+      [self.bottomAnchor
+          constraintGreaterThanOrEqualToAnchor:_avatarView.bottomAnchor
+                                      constant:kVerticalPadding],
+      [self.bottomAnchor
+          constraintGreaterThanOrEqualToAnchor:_accountBadgeView.bottomAnchor
+                                      constant:kVerticalPadding],
 
+      // Horizontal constraints from left to right.
+
+      [_avatarView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor
+                                                constant:kHorizontalPadding],
+      [_textViews.leadingAnchor
+          constraintEqualToAnchor:_avatarView.trailingAnchor
+                         constant:kHorizontalGap],
+
+      [_accountBadgeView.leadingAnchor
+          constraintEqualToAnchor:_textViews.trailingAnchor
+                         constant:kHorizontalGap],
+
+      [self.trailingAnchor
+          constraintEqualToAnchor:_accountBadgeView.trailingAnchor
+                         constant:kHorizontalPadding],
     ]];
 
     AddSameCenterYConstraint(self, _avatarView);
-    AddSameCenterYConstraint(self, _textViews);
     AddSameCenterYConstraint(self, _accountBadgeView);
   }
   return self;
diff --git a/ios/chrome/browser/sharing/model/BUILD.gn b/ios/chrome/browser/sharing/model/BUILD.gn
index b4b20ac..f43defb 100644
--- a/ios/chrome/browser/sharing/model/BUILD.gn
+++ b/ios/chrome/browser/sharing/model/BUILD.gn
@@ -12,7 +12,6 @@
   deps = [
     "//base",
     "//ios/chrome/app/strings",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/features",
     "//ios/web/public",
     "//ui/base:base",
diff --git a/ios/chrome/browser/sharing_message/model/BUILD.gn b/ios/chrome/browser/sharing_message/model/BUILD.gn
index 1712fef7..3813095 100644
--- a/ios/chrome/browser/sharing_message/model/BUILD.gn
+++ b/ios/chrome/browser/sharing_message/model/BUILD.gn
@@ -31,11 +31,11 @@
     "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/gcm/model",
     "//ios/chrome/browser/gcm/model/instance_id",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/sync/model",
     "//ios/chrome/browser/sync/model:device_info_sync_service_factory",
     "//ios/chrome/common",
+    "//ios/web/public/thread",
   ]
 }
 
@@ -51,7 +51,6 @@
     "//components/sharing_message",
     "//components/sync/base",
     "//components/sync/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/common",
   ]
diff --git a/ios/chrome/browser/side_swipe/ui_bundled/BUILD.gn b/ios/chrome/browser/side_swipe/ui_bundled/BUILD.gn
index 492681e3..3da9ffc2 100644
--- a/ios/chrome/browser/side_swipe/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/side_swipe/ui_bundled/BUILD.gn
@@ -26,7 +26,6 @@
     "//ios/chrome/browser/ntp/ui_bundled",
     "//ios/chrome/browser/reading_list/model",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/signin/model/BUILD.gn b/ios/chrome/browser/signin/model/BUILD.gn
index c25b76df..f820c2cb 100644
--- a/ios/chrome/browser/signin/model/BUILD.gn
+++ b/ios/chrome/browser/signin/model/BUILD.gn
@@ -91,7 +91,6 @@
     "//ios/chrome/browser/profile/model:constants",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -349,7 +348,6 @@
     "//components/keyed_service/core",
     "//components/signin/public/base:test_support",
     "//components/signin/public/identity_manager:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/sync/model",
   ]
@@ -411,7 +409,6 @@
     "//ios/chrome/browser/profile/model:constants",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/snapshots/model/BUILD.gn b/ios/chrome/browser/snapshots/model/BUILD.gn
index 6a8df56..380c1a67 100644
--- a/ios/chrome/browser/snapshots/model/BUILD.gn
+++ b/ios/chrome/browser/snapshots/model/BUILD.gn
@@ -33,7 +33,6 @@
     "//base",
     "//build:blink_buildflags",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/spotlight_debugger/ui_bundled/BUILD.gn b/ios/chrome/browser/spotlight_debugger/ui_bundled/BUILD.gn
index 578862f0..a38e319a 100644
--- a/ios/chrome/browser/spotlight_debugger/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/spotlight_debugger/ui_bundled/BUILD.gn
@@ -29,7 +29,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/elements",
diff --git a/ios/chrome/browser/ssl/model/BUILD.gn b/ios/chrome/browser/ssl/model/BUILD.gn
index c7e186e0..4303fd1 100644
--- a/ios/chrome/browser/ssl/model/BUILD.gn
+++ b/ios/chrome/browser/ssl/model/BUILD.gn
@@ -27,7 +27,6 @@
     "//components/url_formatter",
     "//ios/chrome/browser/safe_browsing/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/browser/tab_insertion/model",
@@ -93,7 +92,6 @@
     "//base",
     "//components/content_settings/core/browser",
     "//components/security_interstitials/core:insecure_form_util",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/test/app:test_support",
     "//ios/testing/earl_grey:eg_app_support+eg2",
     "//ios/third_party/earl_grey2:app_framework+link",
diff --git a/ios/chrome/browser/supervised_user/model/BUILD.gn b/ios/chrome/browser/supervised_user/model/BUILD.gn
index 24132c5f..389acac5 100644
--- a/ios/chrome/browser/supervised_user/model/BUILD.gn
+++ b/ios/chrome/browser/supervised_user/model/BUILD.gn
@@ -72,7 +72,6 @@
     "//components/supervised_user/core/browser",
     "//components/supervised_user/core/common",
     "//components/supervised_user/core/common:features",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/signin/model:capabilities_types",
diff --git a/ios/chrome/browser/supervised_user/model/child_account_service_factory.mm b/ios/chrome/browser/supervised_user/model/child_account_service_factory.mm
index 85e574ef..0392aafd 100644
--- a/ios/chrome/browser/supervised_user/model/child_account_service_factory.mm
+++ b/ios/chrome/browser/supervised_user/model/child_account_service_factory.mm
@@ -26,8 +26,7 @@
 }
 
 ChildAccountServiceFactory::ChildAccountServiceFactory()
-    : ProfileKeyedServiceFactoryIOS("ChildAccountService",
-                                    ServiceCreation::kCreateWithProfile) {
+    : ProfileKeyedServiceFactoryIOS("ChildAccountService") {
   DependsOn(IdentityManagerFactory::GetInstance());
   DependsOn(ListFamilyMembersServiceFactory::GetInstance());
 }
@@ -36,13 +35,11 @@
 ChildAccountServiceFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
   ProfileIOS* profile = ProfileIOS::FromBrowserState(context);
-  auto service = std::make_unique<supervised_user::ChildAccountService>(
+  return std::make_unique<supervised_user::ChildAccountService>(
       CHECK_DEREF(profile->GetPrefs()),
       IdentityManagerFactory::GetForBrowserState(profile),
       profile->GetSharedURLLoaderFactory(),
       // Callback relevant only for Chrome OS.
       /*check_user_child_status_callback=*/base::DoNothing(),
       CHECK_DEREF(ListFamilyMembersServiceFactory::GetForProfile(profile)));
-  service->Init();
-  return service;
 }
diff --git a/ios/chrome/browser/supervised_user/model/list_family_members_service_factory.mm b/ios/chrome/browser/supervised_user/model/list_family_members_service_factory.mm
index d7755a5..cd3cf073 100644
--- a/ios/chrome/browser/supervised_user/model/list_family_members_service_factory.mm
+++ b/ios/chrome/browser/supervised_user/model/list_family_members_service_factory.mm
@@ -26,8 +26,7 @@
 }
 
 ListFamilyMembersServiceFactory::ListFamilyMembersServiceFactory()
-    : ProfileKeyedServiceFactoryIOS("ListFamilyMembersService",
-                                    ServiceCreation::kCreateWithProfile) {
+    : ProfileKeyedServiceFactoryIOS("ListFamilyMembersService") {
   DependsOn(IdentityManagerFactory::GetInstance());
 }
 
@@ -35,9 +34,7 @@
 ListFamilyMembersServiceFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
   ProfileIOS* profile = ProfileIOS::FromBrowserState(context);
-  auto service = std::make_unique<supervised_user::ListFamilyMembersService>(
+  return std::make_unique<supervised_user::ListFamilyMembersService>(
       IdentityManagerFactory::GetForBrowserState(profile),
       profile->GetSharedURLLoaderFactory(), CHECK_DEREF(profile->GetPrefs()));
-  service->Init();
-  return service;
 }
diff --git a/ios/chrome/browser/supervised_user/model/supervised_user_url_filter_tab_helper_unittest.mm b/ios/chrome/browser/supervised_user/model/supervised_user_url_filter_tab_helper_unittest.mm
index f285d98..d50e654 100644
--- a/ios/chrome/browser/supervised_user/model/supervised_user_url_filter_tab_helper_unittest.mm
+++ b/ios/chrome/browser/supervised_user/model/supervised_user_url_filter_tab_helper_unittest.mm
@@ -74,6 +74,9 @@
         account, identity_manager, is_subject_to_parental_controls);
 
     // Initialize supervised_user services.
+    ChildAccountServiceFactory::GetForProfile(chrome_browser_state_.get())
+        ->Init();
+
     supervised_user::SupervisedUserService* supervised_user_service =
         SupervisedUserServiceFactory::GetForProfile(
             chrome_browser_state_.get());
diff --git a/ios/chrome/browser/sync/model/BUILD.gn b/ios/chrome/browser/sync/model/BUILD.gn
index 009075d..9e1e4718 100644
--- a/ios/chrome/browser/sync/model/BUILD.gn
+++ b/ios/chrome/browser/sync/model/BUILD.gn
@@ -128,7 +128,6 @@
     "//components/sync_device_info",
     "//ios/chrome/browser/push_notification/model:push_notification_service_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/common",
@@ -148,7 +147,6 @@
     "//components/sync/invalidations",
     "//ios/chrome/browser/gcm/model",
     "//ios/chrome/browser/gcm/model/instance_id",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
   ]
 }
@@ -180,7 +178,6 @@
     "//ios/chrome/browser/settings/model/sync/utils",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/ui/util",
@@ -207,7 +204,6 @@
     "//components/keyed_service/ios",
     "//components/sync",
     "//components/sync:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/common",
     "//ui/base",
diff --git a/ios/chrome/browser/sync/model/glue/BUILD.gn b/ios/chrome/browser/sync/model/glue/BUILD.gn
index 0cb55fa9..43817eef 100644
--- a/ios/chrome/browser/sync/model/glue/BUILD.gn
+++ b/ios/chrome/browser/sync/model/glue/BUILD.gn
@@ -12,7 +12,6 @@
     "//components/browser_sync",
     "//components/sync",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web/public",
   ]
diff --git a/ios/chrome/browser/synced_sessions/model/BUILD.gn b/ios/chrome/browser/synced_sessions/model/BUILD.gn
index fce5f57..1d21a9c 100644
--- a/ios/chrome/browser/synced_sessions/model/BUILD.gn
+++ b/ios/chrome/browser/synced_sessions/model/BUILD.gn
@@ -46,7 +46,6 @@
     "//base",
     "//base/test:test_support",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
diff --git a/ios/chrome/browser/tab_insertion/model/BUILD.gn b/ios/chrome/browser/tab_insertion/model/BUILD.gn
index 817c6c0..3c414e3 100644
--- a/ios/chrome/browser/tab_insertion/model/BUILD.gn
+++ b/ios/chrome/browser/tab_insertion/model/BUILD.gn
@@ -14,7 +14,6 @@
     "//ios/chrome/browser/sessions/model:session_restoration_service",
     "//ios/chrome/browser/sessions/model:session_restoration_service_factory",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/url_loading/model:new_tab_animation_tab_helper",
diff --git a/ios/chrome/browser/tabs/model/BUILD.gn b/ios/chrome/browser/tabs/model/BUILD.gn
index 7aa7b83..985824f 100644
--- a/ios/chrome/browser/tabs/model/BUILD.gn
+++ b/ios/chrome/browser/tabs/model/BUILD.gn
@@ -44,6 +44,7 @@
     "//components/autofill/ios/form_util",
     "//components/breadcrumbs/core:status",
     "//components/commerce/ios/browser",
+    "//components/data_sharing/public",
     "//components/favicon/core",
     "//components/favicon/ios",
     "//components/history/core/browser",
@@ -69,6 +70,7 @@
     "//ios/chrome/browser/complex_tasks/model",
     "//ios/chrome/browser/contextual_panel/model",
     "//ios/chrome/browser/crash_report/model/breadcrumbs",
+    "//ios/chrome/browser/data_sharing/model",
     "//ios/chrome/browser/download/model",
     "//ios/chrome/browser/download/ui_bundled:features",
     "//ios/chrome/browser/drive/model:drive_tab_helper",
@@ -110,7 +112,6 @@
     "//ios/chrome/browser/sessions/model:session_restoration_service_factory",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -175,7 +176,6 @@
     "//ios/chrome/browser/ntp/ui_bundled:feature_flags",
     "//ios/chrome/browser/sessions/model",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/tabs/model/DEPS b/ios/chrome/browser/tabs/model/DEPS
index 01205c1e0..12d5ab7 100644
--- a/ios/chrome/browser/tabs/model/DEPS
+++ b/ios/chrome/browser/tabs/model/DEPS
@@ -18,5 +18,6 @@
   # The TabHelperUtil can depend on everything.
   "^tab_helper_util.mm": [
     "+ios/chrome/browser",
+    "+components/data_sharing/public",
   ],
 }
diff --git a/ios/chrome/browser/tabs/model/tab_helper_util.mm b/ios/chrome/browser/tabs/model/tab_helper_util.mm
index 82438230..c7f1d55 100644
--- a/ios/chrome/browser/tabs/model/tab_helper_util.mm
+++ b/ios/chrome/browser/tabs/model/tab_helper_util.mm
@@ -7,6 +7,7 @@
 #import "base/feature_list.h"
 #import "components/breadcrumbs/core/breadcrumbs_status.h"
 #import "components/commerce/ios/browser/commerce_tab_helper.h"
+#import "components/data_sharing/public/features.h"
 #import "components/favicon/core/favicon_service.h"
 #import "components/favicon/ios/web_favicon_driver.h"
 #import "components/history/core/browser/top_sites.h"
@@ -33,6 +34,7 @@
 #import "ios/chrome/browser/contextual_panel/model/contextual_panel_model_service_factory.h"
 #import "ios/chrome/browser/contextual_panel/model/contextual_panel_tab_helper.h"
 #import "ios/chrome/browser/crash_report/model/breadcrumbs/breadcrumb_manager_tab_helper.h"
+#import "ios/chrome/browser/data_sharing/model/data_sharing_tab_helper.h"
 #import "ios/chrome/browser/download/model/ar_quick_look_tab_helper.h"
 #import "ios/chrome/browser/download/model/document_download_tab_helper.h"
 #import "ios/chrome/browser/download/model/download_manager_tab_helper.h"
@@ -360,4 +362,10 @@
                                                 optimization_guide_decider);
     }
   }
+
+  if (base::FeatureList::IsEnabled(
+          data_sharing::features::kDataSharingFeature) &&
+      IsSharedTabGroupsEnabled() && !is_off_the_record && !for_prerender) {
+    DataSharingTabHelper::CreateForWebState(web_state);
+  }
 }
diff --git a/ios/chrome/browser/tabs/ui_bundled/BUILD.gn b/ios/chrome/browser/tabs/ui_bundled/BUILD.gn
index 5bbba05..dfc0e35e 100644
--- a/ios/chrome/browser/tabs/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/tabs/ui_bundled/BUILD.gn
@@ -38,7 +38,6 @@
     "//ios/chrome/browser/ntp/model:util",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
@@ -84,7 +83,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/tabs/ui_bundled/requirements",
diff --git a/ios/chrome/browser/text_selection/model/BUILD.gn b/ios/chrome/browser/text_selection/model/BUILD.gn
index fae410f4..9b7a0fe 100644
--- a/ios/chrome/browser/text_selection/model/BUILD.gn
+++ b/ios/chrome/browser/text_selection/model/BUILD.gn
@@ -41,7 +41,6 @@
     "//components/keyed_service/ios",
     "//components/optimization_guide/core",
     "//ios/chrome/browser/optimization_guide/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
   ]
 }
diff --git a/ios/chrome/browser/tips_notifications/model/BUILD.gn b/ios/chrome/browser/tips_notifications/model/BUILD.gn
index 063daf0..cc912ea 100644
--- a/ios/chrome/browser/tips_notifications/model/BUILD.gn
+++ b/ios/chrome/browser/tips_notifications/model/BUILD.gn
@@ -20,7 +20,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/utils",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client.h b/ios/chrome/browser/tips_notifications/model/tips_notification_client.h
index 49c14f1..bc53f10 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.h
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.h
@@ -25,9 +25,9 @@
   ~TipsNotificationClient() override;
 
   // Override PushNotificationClient::
-  void HandleNotificationInteraction(
+  bool HandleNotificationInteraction(
       UNNotificationResponse* notification_response) override;
-  UIBackgroundFetchResult HandleNotificationReception(
+  std::optional<UIBackgroundFetchResult> HandleNotificationReception(
       NSDictionary<NSString*, id>* notification) override;
   NSArray<UNNotificationCategory*>* RegisterActionableNotifications() override;
   void OnSceneActiveForegroundBrowserReady() override;
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
index 0285c6c..93d7b39 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
@@ -127,18 +127,18 @@
 
 TipsNotificationClient::~TipsNotificationClient() = default;
 
-void TipsNotificationClient::HandleNotificationInteraction(
+bool TipsNotificationClient::HandleNotificationInteraction(
     UNNotificationResponse* response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!IsTipsNotification(response.notification.request)) {
-    return;
+    return false;
   }
 
   interacted_type_ = ParseTipsNotificationType(response.notification.request);
   if (!interacted_type_.has_value()) {
     base::UmaHistogramEnumeration("IOS.Notifications.Tips.Interaction",
                                   TipsNotificationType::kError);
-    return;
+    return false;
   }
   base::UmaHistogramEnumeration("IOS.Notifications.Tips.Interaction",
                                 interacted_type_.value());
@@ -148,6 +148,7 @@
   if (IsSceneLevelForegroundActive()) {
     CheckAndMaybeRequestNotification(base::DoNothing());
   }
+  return true;
 }
 
 void TipsNotificationClient::HandleNotificationInteraction(
@@ -162,9 +163,13 @@
                              weak_ptr_factory_.GetWeakPtr(), type))];
 }
 
-UIBackgroundFetchResult TipsNotificationClient::HandleNotificationReception(
-    NSDictionary<NSString*, id>* notification) {
+std::optional<UIBackgroundFetchResult>
+TipsNotificationClient::HandleNotificationReception(
+    NSDictionary<NSString*, id>* userInfo) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (![userInfo objectForKey:kTipsNotificationId]) {
+    return std::nullopt;
+  }
   return UIBackgroundFetchResultNoData;
 }
 
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
index 1314947..1f1a689 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
@@ -248,7 +248,10 @@
 
 // Tests that HandleNotificationReception does nothing and returns "NoData".
 TEST_F(TipsNotificationClientTest, HandleNotificationReception) {
-  EXPECT_EQ(client_->HandleNotificationReception(nil),
+  EXPECT_EQ(client_->HandleNotificationReception(nil), std::nullopt);
+  NSDictionary* user_info =
+      UserInfoForTipsNotificationType(TipsNotificationType::kWhatsNew);
+  EXPECT_EQ(client_->HandleNotificationReception(user_info),
             UIBackgroundFetchResultNoData);
 }
 
diff --git a/ios/chrome/browser/translate/model/BUILD.gn b/ios/chrome/browser/translate/model/BUILD.gn
index a10dcb83..57cb80a 100644
--- a/ios/chrome/browser/translate/model/BUILD.gn
+++ b/ios/chrome/browser/translate/model/BUILD.gn
@@ -140,7 +140,6 @@
     "//components/translate/core/browser:browser",
     "//components/translate/core/common:common",
     "//components/translate/ios/browser:browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/translate/model:model",
     "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/trusted_vault/model/BUILD.gn b/ios/chrome/browser/trusted_vault/model/BUILD.gn
index 8a6bc673..8f7dab50 100644
--- a/ios/chrome/browser/trusted_vault/model/BUILD.gn
+++ b/ios/chrome/browser/trusted_vault/model/BUILD.gn
@@ -17,7 +17,6 @@
   ]
   deps = [
     "//components/signin/public/identity_manager",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
 
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index 48068d8..39875d2 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -50,7 +50,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -201,7 +200,6 @@
     "//ios/chrome/browser/bookmarks/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/authentication/account_menu/BUILD.gn b/ios/chrome/browser/ui/authentication/account_menu/BUILD.gn
index 4531602..23704b2 100644
--- a/ios/chrome/browser/ui/authentication/account_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/account_menu/BUILD.gn
@@ -27,7 +27,7 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
+    "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/table_view:utils",
     "//ios/chrome/browser/shared/ui/util:snackbar_util",
@@ -108,7 +108,6 @@
     "//ios/chrome/browser/settings/model/sync/utils:identity_error_util",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/table_view:utils",
diff --git a/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator.mm b/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator.mm
index 6c4408f..1305034e0 100644
--- a/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator.mm
@@ -16,7 +16,7 @@
 #import "ios/chrome/browser/push_notification/model/push_notification_service.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/public/commands/application_commands.h"
 #import "ios/chrome/browser/shared/public/commands/browser_commands.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
diff --git a/ios/chrome/browser/ui/authentication/history_sync/BUILD.gn b/ios/chrome/browser/ui/authentication/history_sync/BUILD.gn
index b586ea9..38f74a9 100644
--- a/ios/chrome/browser/ui/authentication/history_sync/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/history_sync/BUILD.gn
@@ -30,7 +30,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/first_run/model",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/chrome/browser/signin/model",
@@ -40,6 +39,7 @@
     "//ios/chrome/browser/ui/authentication/history_sync/resources",
     "//ios/chrome/browser/ui/authentication/signin:constants",
     "//ios/chrome/common/ui/promo_style:promo_style",
+    "//ui/base",
   ]
   public_deps = [ "//ios/chrome/browser/first_run/ui_bundled:interruptible_chrome_coordinator" ]
 }
@@ -98,7 +98,6 @@
     "//components/signin/public/identity_manager",
     "//components/signin/public/identity_manager:test_support",
     "//components/signin/public/identity_manager/objc",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/web/public/test",
     "//testing/gtest",
diff --git a/ios/chrome/browser/ui/authentication/identity_chooser/BUILD.gn b/ios/chrome/browser/ui/authentication/identity_chooser/BUILD.gn
index a1fa8e9f..4e55df1 100644
--- a/ios/chrome/browser/ui/authentication/identity_chooser/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/identity_chooser/BUILD.gn
@@ -18,7 +18,6 @@
     "//components/prefs",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/signin/model:system_identity",
     "//ios/chrome/browser/ui/authentication/cells",
diff --git a/ios/chrome/browser/ui/authentication/signin/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/BUILD.gn
index 492a6dd..cda733d 100644
--- a/ios/chrome/browser/ui/authentication/signin/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/BUILD.gn
@@ -24,7 +24,6 @@
     "//components/signin/public/identity_manager",
     "//ios/chrome/browser/first_run/ui_bundled:interruptible_chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model:capabilities_types",
   ]
diff --git a/ios/chrome/browser/ui/authentication/signin/add_account_signin/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/add_account_signin/BUILD.gn
index 08590650..970ac65 100644
--- a/ios/chrome/browser/ui/authentication/signin/add_account_signin/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/add_account_signin/BUILD.gn
@@ -21,7 +21,6 @@
     "//ios/chrome/browser/main/model",
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/signin/model:system_identity_manager",
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/BUILD.gn
index a74e236..221b5d7bb 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/BUILD.gn
@@ -20,7 +20,6 @@
     "//ios/chrome/browser/net/model:crurl",
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_sheet/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_sheet/BUILD.gn
index b7af9344..0ea87ce 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_sheet/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_sheet/BUILD.gn
@@ -18,7 +18,6 @@
   ]
   deps = [
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/browser/shared/ui/util/image",
     "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/ui/authentication/signin/forced_signin/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/forced_signin/BUILD.gn
index c6f52c7..0dfc736 100644
--- a/ios/chrome/browser/ui/authentication/signin/forced_signin/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/forced_signin/BUILD.gn
@@ -14,7 +14,6 @@
     "//ios/chrome/browser/first_run/ui_bundled:utils",
     "//ios/chrome/browser/first_run/ui_bundled/signin",
     "//ios/chrome/browser/main/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/ui/authentication/signin:signin_headers",
     "//ios/chrome/browser/ui/authentication/signin:signin_protected",
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_history_sync/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/signin_history_sync/BUILD.gn
index 7f0fa28a..d7c4360c 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_history_sync/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/signin_history_sync/BUILD.gn
@@ -11,7 +11,6 @@
   ]
   deps = [
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/ui/authentication/history_sync",
     "//ios/chrome/browser/ui/authentication/signin:signin_headers",
diff --git a/ios/chrome/browser/ui/authentication/signout_action_sheet/BUILD.gn b/ios/chrome/browser/ui/authentication/signout_action_sheet/BUILD.gn
index 119ea425..6004fbc 100644
--- a/ios/chrome/browser/ui/authentication/signout_action_sheet/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signout_action_sheet/BUILD.gn
@@ -18,7 +18,6 @@
     "//components/sync/service",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/ui/browser_container/BUILD.gn b/ios/chrome/browser/ui/browser_container/BUILD.gn
index 83cd8e9..66856e5f 100644
--- a/ios/chrome/browser/ui/browser_container/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_container/BUILD.gn
@@ -28,7 +28,6 @@
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
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 8771caec..c8cd9eb 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -224,11 +224,11 @@
 
   ChromeBrowserState* browserState = self.browser->GetBrowserState();
 
-  _segmentationService = segmentation_platform::
-      SegmentationPlatformServiceFactory::GetForBrowserState(browserState);
-  _deviceSwitcherResultDispatcher =
-      segmentation_platform::SegmentationPlatformServiceFactory::
-          GetDispatcherForBrowserState(browserState);
+  _segmentationService =
+      segmentation_platform::SegmentationPlatformServiceFactory::GetForProfile(
+          browserState);
+  _deviceSwitcherResultDispatcher = segmentation_platform::
+      SegmentationPlatformServiceFactory::GetDispatcherForProfile(browserState);
 
   self.authService =
       AuthenticationServiceFactory::GetForBrowserState(browserState);
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model_unittest.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model_unittest.mm
index ef10397..bdd8b7b7 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model_unittest.mm
@@ -341,9 +341,9 @@
                                     GetForBrowserState(GetBrowserState())];
 
     _magicStackRankingModel = [[MagicStackRankingModel alloc]
-        initWithSegmentationService:
-            segmentation_platform::SegmentationPlatformServiceFactory::
-                GetForBrowserState(GetBrowserState())
+        initWithSegmentationService:segmentation_platform::
+                                        SegmentationPlatformServiceFactory::
+                                            GetForProfile(GetBrowserState())
                     shoppingService:commerce::ShoppingServiceFactory::
                                         GetForBrowserState(GetBrowserState())
                         prefService:GetBrowserState()->GetPrefs()
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/set_up_list/BUILD.gn
index 6cf60c5..7f70f006 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/BUILD.gn
@@ -94,7 +94,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/tab_resumption/BUILD.gn
index a913eb7b..9a4f266d 100644
--- a/ios/chrome/browser/ui/content_suggestions/tab_resumption/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/tab_resumption/BUILD.gn
@@ -35,7 +35,6 @@
     "//ios/chrome/browser/page_image/model",
     "//ios/chrome/browser/sessions/model:session_util",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/ui/fullscreen/test/BUILD.gn b/ios/chrome/browser/ui/fullscreen/test/BUILD.gn
index 6df382a..8861e44 100644
--- a/ios/chrome/browser/ui/fullscreen/test/BUILD.gn
+++ b/ios/chrome/browser/ui/fullscreen/test/BUILD.gn
@@ -36,7 +36,6 @@
   deps = [
     "//ios/chrome/browser/ntp/ui_bundled:logo",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/ui/fullscreen",
     "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/ui/lens/BUILD.gn b/ios/chrome/browser/ui/lens/BUILD.gn
index a34518c2..42b2037 100644
--- a/ios/chrome/browser/ui/lens/BUILD.gn
+++ b/ios/chrome/browser/ui/lens/BUILD.gn
@@ -25,7 +25,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index 2e881695..15e57a83 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -90,7 +90,6 @@
     "//ios/chrome/browser/sessions/model:session_util",
     "//ios/chrome/browser/settings/model/sync/utils",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/menu/BUILD.gn b/ios/chrome/browser/ui/menu/BUILD.gn
index 5e2ac98..c1a2a90 100644
--- a/ios/chrome/browser/ui/menu/BUILD.gn
+++ b/ios/chrome/browser/ui/menu/BUILD.gn
@@ -24,7 +24,6 @@
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/omnibox/BUILD.gn b/ios/chrome/browser/ui/omnibox/BUILD.gn
index 5401f26..3f7eab2e 100644
--- a/ios/chrome/browser/ui/omnibox/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/BUILD.gn
@@ -148,7 +148,6 @@
     "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/public/commands",
@@ -220,7 +219,6 @@
     "//ios/chrome/browser/history/model",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/ui/omnibox/popup:popup_ui",
     "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
index ac39fa2..156435d0 100644
--- a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
@@ -88,7 +88,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
@@ -206,7 +205,6 @@
     "//base",
     "//components/omnibox/browser",
     "//ios/chrome/browser/net/model:crurl",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/ui/omnibox:features",
     "//ios/chrome/browser/ui/omnibox:omnibox_util",
diff --git a/ios/chrome/browser/ui/page_info/BUILD.gn b/ios/chrome/browser/ui/page_info/BUILD.gn
index e0e92b1..ed46b679 100644
--- a/ios/chrome/browser/ui/page_info/BUILD.gn
+++ b/ios/chrome/browser/ui/page_info/BUILD.gn
@@ -31,7 +31,6 @@
     "//ios/chrome/browser/permissions/ui_bundled",
     "//ios/chrome/browser/permissions/ui_bundled:constants",
     "//ios/chrome/browser/permissions/ui_bundled:permission_info",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
@@ -102,7 +101,6 @@
     "//ios/chrome/browser/permissions/ui_bundled:permission_info",
     "//ios/chrome/browser/reading_list/model",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/ui/partial_translate/BUILD.gn b/ios/chrome/browser/ui/partial_translate/BUILD.gn
index 198e1fa..2f62345a 100644
--- a/ios/chrome/browser/ui/partial_translate/BUILD.gn
+++ b/ios/chrome/browser/ui/partial_translate/BUILD.gn
@@ -73,7 +73,6 @@
     "//base",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/ui/fullscreen",
diff --git a/ios/chrome/browser/ui/popup_menu/BUILD.gn b/ios/chrome/browser/ui/popup_menu/BUILD.gn
index 9c71bbf..2f5d1a02 100644
--- a/ios/chrome/browser/ui/popup_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/BUILD.gn
@@ -62,7 +62,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn b/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
index f84518e..b5af4cb2 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
@@ -64,7 +64,6 @@
     "//ios/chrome/browser/settings/model/sync/utils:identity_error_util",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_help_coordinator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_help_coordinator.mm
index aca5b58c..fe07a50 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_help_coordinator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_help_coordinator.mm
@@ -87,7 +87,7 @@
     if (!browser->GetBrowserState()->IsOffTheRecord()) {
       _deviceSwitcherResultDispatcher =
           segmentation_platform::SegmentationPlatformServiceFactory::
-              GetDispatcherForBrowserState(browser->GetBrowserState());
+              GetDispatcherForProfile(browser->GetProfile());
     }
   }
   return self;
diff --git a/ios/chrome/browser/ui/price_notifications/BUILD.gn b/ios/chrome/browser/ui/price_notifications/BUILD.gn
index 5b84b88..2e7a7ed6 100644
--- a/ios/chrome/browser/ui/price_notifications/BUILD.gn
+++ b/ios/chrome/browser/ui/price_notifications/BUILD.gn
@@ -33,7 +33,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -108,7 +107,6 @@
     "//ios/chrome/browser/push_notification/model:push_notification_service_header",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/ui/promos_manager/BUILD.gn b/ios/chrome/browser/ui/promos_manager/BUILD.gn
index 0894240..43f38dd 100644
--- a/ios/chrome/browser/ui/promos_manager/BUILD.gn
+++ b/ios/chrome/browser/ui/promos_manager/BUILD.gn
@@ -38,7 +38,6 @@
     "//ios/chrome/browser/policy/ui_bundled:user_policy_scene_agent",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
   ]
diff --git a/ios/chrome/browser/ui/push_notification/BUILD.gn b/ios/chrome/browser/ui/push_notification/BUILD.gn
index d32a8c3d..439d12a 100644
--- a/ios/chrome/browser/ui/push_notification/BUILD.gn
+++ b/ios/chrome/browser/ui/push_notification/BUILD.gn
@@ -25,7 +25,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/push_notification/metrics.h b/ios/chrome/browser/ui/push_notification/metrics.h
index cc0719fd..714ef3ac 100644
--- a/ios/chrome/browser/ui/push_notification/metrics.h
+++ b/ios/chrome/browser/ui/push_notification/metrics.h
@@ -21,6 +21,7 @@
 extern const char kNotificationsOptInPromptTipsEnabled[];
 extern const char kNotificationsOptInPromptPriceTrackingEnabled[];
 extern const char kNotificationsOptInPromptSafetyCheckEnabled[];
+extern const char kNotificationsOptInPromptSendTabEnabled[];
 extern const char kNotificationsOptInAlertPermissionDenied[];
 extern const char kNotificationsOptInAlertPermissionGranted[];
 extern const char kNotificationsOptInAlertOpenedSettings[];
diff --git a/ios/chrome/browser/ui/push_notification/metrics.mm b/ios/chrome/browser/ui/push_notification/metrics.mm
index f565696..7ef6516 100644
--- a/ios/chrome/browser/ui/push_notification/metrics.mm
+++ b/ios/chrome/browser/ui/push_notification/metrics.mm
@@ -14,6 +14,8 @@
     "IOS.Notifications.OptInPrompt.PriceTrackingEnabled";
 const char kNotificationsOptInPromptSafetyCheckEnabled[] =
     "IOS.Notifications.OptInPrompt.SafetyCheckEnabled";
+const char kNotificationsOptInPromptSendTabEnabled[] =
+    "IOS.Notifications.OptInPrompt.SendTabEnabled";
 const char kNotificationsOptInAlertPermissionDenied[] =
     "IOS.Notifications.OptInAlert.PermissionDenied";
 const char kNotificationsOptInAlertPermissionGranted[] =
diff --git a/ios/chrome/browser/ui/push_notification/notifications_opt_in_coordinator.mm b/ios/chrome/browser/ui/push_notification/notifications_opt_in_coordinator.mm
index 912da9a..9293897 100644
--- a/ios/chrome/browser/ui/push_notification/notifications_opt_in_coordinator.mm
+++ b/ios/chrome/browser/ui/push_notification/notifications_opt_in_coordinator.mm
@@ -191,6 +191,10 @@
         base::RecordAction(base::UserMetricsAction(
             kNotificationsOptInPromptSafetyCheckEnabled));
         break;
+      case PushNotificationClientId::kSendTab:
+        base::RecordAction(
+            base::UserMetricsAction(kNotificationsOptInPromptSendTabEnabled));
+        break;
     }
   }
 }
diff --git a/ios/chrome/browser/ui/reading_list/BUILD.gn b/ios/chrome/browser/ui/reading_list/BUILD.gn
index d064ecf..5b1019c 100644
--- a/ios/chrome/browser/ui/reading_list/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/BUILD.gn
@@ -48,7 +48,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
index f20262f..cb87c90 100644
--- a/ios/chrome/browser/ui/recent_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -31,7 +31,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
@@ -99,7 +98,6 @@
     "//ios/chrome/browser/sessions/model:session_util",
     "//ios/chrome/browser/settings/model/sync/utils",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
@@ -188,7 +186,6 @@
   deps = [
     "//base",
     "//base/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/ui/list_model",
     "//ios/chrome/test/app:test_support",
   ]
diff --git a/ios/chrome/browser/ui/save_to_photos/BUILD.gn b/ios/chrome/browser/ui/save_to_photos/BUILD.gn
index 40a1ed6..90150c4 100644
--- a/ios/chrome/browser/ui/save_to_photos/BUILD.gn
+++ b/ios/chrome/browser/ui/save_to_photos/BUILD.gn
@@ -26,7 +26,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/ui/search_engine_choice/BUILD.gn b/ios/chrome/browser/ui/search_engine_choice/BUILD.gn
index 7105c14..b375d0f3 100644
--- a/ios/chrome/browser/ui/search_engine_choice/BUILD.gn
+++ b/ios/chrome/browser/ui/search_engine_choice/BUILD.gn
@@ -32,7 +32,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/list_model:list_model",
diff --git a/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn b/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn
index 29004e1..5b7479d7 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn
+++ b/ios/chrome/browser/ui/send_tab_to_self/BUILD.gn
@@ -16,7 +16,6 @@
     "//ios/chrome/browser/main/model",
     "//ios/chrome/browser/send_tab_to_self/model",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index f922b5c1..6db3b4c 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -18,7 +18,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/keyboard/ui_bundled",
     "//ios/chrome/browser/net/model:crurl",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/ui/table_view",
@@ -142,6 +141,7 @@
     "//ios/chrome/browser/photos/model",
     "//ios/chrome/browser/photos/model:photos_service_factory",
     "//ios/chrome/browser/prerender/model:prerender_pref",
+    "//ios/chrome/browser/push_notification/model:push_notification_client",
     "//ios/chrome/browser/push_notification/model:push_notification_service",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/settings/model/sync/utils",
@@ -149,7 +149,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
@@ -273,7 +272,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
@@ -350,7 +348,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile/test",
@@ -414,7 +411,6 @@
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/ui/settings/address_bar_preference/BUILD.gn b/ios/chrome/browser/ui/settings/address_bar_preference/BUILD.gn
index e4ede36..34bb6a9 100644
--- a/ios/chrome/browser/ui/settings/address_bar_preference/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/address_bar_preference/BUILD.gn
@@ -15,7 +15,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/utils",
diff --git a/ios/chrome/browser/ui/settings/autofill/BUILD.gn b/ios/chrome/browser/ui/settings/autofill/BUILD.gn
index 3ceba46..d607016 100644
--- a/ios/chrome/browser/ui/settings/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/autofill/BUILD.gn
@@ -49,7 +49,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
@@ -108,7 +107,6 @@
     "//ios/chrome/browser/autofill/ui_bundled:ui_type",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/shared/ui/table_view",
     "//ios/chrome/browser/shared/ui/table_view:test_support",
diff --git a/ios/chrome/browser/ui/settings/bandwidth/BUILD.gn b/ios/chrome/browser/ui/settings/bandwidth/BUILD.gn
index 1054389..fbc8a3b 100644
--- a/ios/chrome/browser/ui/settings/bandwidth/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/bandwidth/BUILD.gn
@@ -17,7 +17,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/net/model:crurl",
     "//ios/chrome/browser/prerender/model:prerender_pref",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/BUILD.gn b/ios/chrome/browser/ui/settings/clear_browsing_data/BUILD.gn
index e35a179..b2d57c7 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/BUILD.gn
@@ -67,7 +67,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/settings/content_settings/BUILD.gn b/ios/chrome/browser/ui/settings/content_settings/BUILD.gn
index 0dae63e..f9e5c82 100644
--- a/ios/chrome/browser/ui/settings/content_settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/content_settings/BUILD.gn
@@ -34,7 +34,6 @@
     "//ios/chrome/browser/ntp/ui_bundled:feature_flags",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
@@ -49,6 +48,7 @@
     "//ios/chrome/browser/ui/settings/utils",
     "//ios/chrome/browser/web/model/annotations",
     "//ios/web/common:features",
+    "//net",
     "//ui/base",
   ]
 }
@@ -121,7 +121,6 @@
     "//components/content_settings/core/common",
     "//ios/chrome/app:app_internal",
     "//ios/chrome/browser/content_settings/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/test/app:test_support",
   ]
diff --git a/ios/chrome/browser/ui/settings/downloads/BUILD.gn b/ios/chrome/browser/ui/settings/downloads/BUILD.gn
index 569fa94a..52e9315b 100644
--- a/ios/chrome/browser/ui/settings/downloads/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/downloads/BUILD.gn
@@ -18,7 +18,6 @@
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/ui/table_view",
diff --git a/ios/chrome/browser/ui/settings/google_services/BUILD.gn b/ios/chrome/browser/ui/settings/google_services/BUILD.gn
index eab8cd7..400d6ee 100644
--- a/ios/chrome/browser/ui/settings/google_services/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/google_services/BUILD.gn
@@ -63,7 +63,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
@@ -127,7 +126,6 @@
     "//ios/chrome/browser/parcel_tracking:util",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/ui/list_model",
@@ -155,7 +153,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/ui/list_model",
     "//ios/chrome/browser/shared/ui/symbols",
diff --git a/ios/chrome/browser/ui/settings/google_services/bulk_upload/BUILD.gn b/ios/chrome/browser/ui/settings/google_services/bulk_upload/BUILD.gn
index 52caec8..1dc5ec3 100644
--- a/ios/chrome/browser/ui/settings/google_services/bulk_upload/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/google_services/bulk_upload/BUILD.gn
@@ -21,7 +21,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/util",
diff --git a/ios/chrome/browser/ui/settings/google_services/manage_accounts/BUILD.gn b/ios/chrome/browser/ui/settings/google_services/manage_accounts/BUILD.gn
index 5a56878..ba40cc3d 100644
--- a/ios/chrome/browser/ui/settings/google_services/manage_accounts/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/google_services/manage_accounts/BUILD.gn
@@ -29,7 +29,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/public/commands",
@@ -83,7 +82,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/settings/language/BUILD.gn b/ios/chrome/browser/ui/settings/language/BUILD.gn
index 445bcd3..f44428e7 100644
--- a/ios/chrome/browser/ui/settings/language/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/language/BUILD.gn
@@ -104,7 +104,6 @@
   deps = [
     "//components/language/core/browser",
     "//components/translate/core/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/translate/model",
     "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/ui/settings/multi_identity/BUILD.gn b/ios/chrome/browser/ui/settings/multi_identity/BUILD.gn
index c8becd2..e25e7d5 100644
--- a/ios/chrome/browser/ui/settings/multi_identity/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/multi_identity/BUILD.gn
@@ -16,7 +16,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
   ]
@@ -33,7 +32,6 @@
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/ui/table_view:utils",
diff --git a/ios/chrome/browser/ui/settings/notifications/BUILD.gn b/ios/chrome/browser/ui/settings/notifications/BUILD.gn
index cf9da95..1c7c62d 100644
--- a/ios/chrome/browser/ui/settings/notifications/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/notifications/BUILD.gn
@@ -15,6 +15,7 @@
     ":utils",
     "//base",
     "//components/prefs",
+    "//components/send_tab_to_self",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/content_notification/model:util",
     "//ios/chrome/browser/push_notification/model:browser_state_service",
@@ -26,7 +27,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features:features",
     "//ios/chrome/browser/shared/ui/list_model",
diff --git a/ios/chrome/browser/ui/settings/notifications/content_notifications/BUILD.gn b/ios/chrome/browser/ui/settings/notifications/content_notifications/BUILD.gn
index ef9bb823..c4d695e 100644
--- a/ios/chrome/browser/ui/settings/notifications/content_notifications/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/notifications/content_notifications/BUILD.gn
@@ -28,7 +28,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/ui/list_model",
     "//ios/chrome/browser/shared/ui/symbols:symbols",
diff --git a/ios/chrome/browser/ui/settings/notifications/content_notifications/content_notifications_mediator.mm b/ios/chrome/browser/ui/settings/notifications/content_notifications/content_notifications_mediator.mm
index c06f535..226ce8a 100644
--- a/ios/chrome/browser/ui/settings/notifications/content_notifications/content_notifications_mediator.mm
+++ b/ios/chrome/browser/ui/settings/notifications/content_notifications/content_notifications_mediator.mm
@@ -251,6 +251,7 @@
     case PushNotificationClientId::kCommerce:
     case PushNotificationClientId::kTips:
     case PushNotificationClientId::kSafetyCheck:
+    case PushNotificationClientId::kSendTab:
       // This should never be reached.
       DCHECK(FALSE);
       break;
@@ -275,6 +276,7 @@
     case PushNotificationClientId::kSports:
       return _sportsNotificationsItem;
     case PushNotificationClientId::kTips:
+    case PushNotificationClientId::kSendTab:
     case PushNotificationClientId::kSafetyCheck:
     case PushNotificationClientId::kCommerce:
       // Not a switch.
diff --git a/ios/chrome/browser/ui/settings/notifications/notifications_mediator.mm b/ios/chrome/browser/ui/settings/notifications/notifications_mediator.mm
index a50e891f..b142f30 100644
--- a/ios/chrome/browser/ui/settings/notifications/notifications_mediator.mm
+++ b/ios/chrome/browser/ui/settings/notifications/notifications_mediator.mm
@@ -374,6 +374,11 @@
       [self.consumer reconfigureCellsForItems:@[ self.tipsNotificationsItem ]];
       break;
     }
+    case PushNotificationClientId::kSendTab: {
+      // TODO(crbug.com/343492927): Create settings page entry for Send Tab
+      // Notifications.
+      break;
+    }
     case PushNotificationClientId::kSafetyCheck:
       // TODO(crbug.com/347975024): Integrate Safety Check Notifications with
       // notifications settings UI.
@@ -396,6 +401,7 @@
   switch (clientId) {
     case PushNotificationClientId::kTips:
       return _tipsNotificationsItem;
+    case PushNotificationClientId::kSendTab:
     case PushNotificationClientId::kCommerce:
     case PushNotificationClientId::kSafetyCheck:
     case PushNotificationClientId::kContent:
diff --git a/ios/chrome/browser/ui/settings/notifications/tracking_price/BUILD.gn b/ios/chrome/browser/ui/settings/notifications/tracking_price/BUILD.gn
index 1f6b5938..9436cab 100644
--- a/ios/chrome/browser/ui/settings/notifications/tracking_price/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/notifications/tracking_price/BUILD.gn
@@ -26,7 +26,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/ui/list_model",
     "//ios/chrome/browser/shared/ui/table_view:utils",
diff --git a/ios/chrome/browser/ui/settings/password/BUILD.gn b/ios/chrome/browser/ui/settings/password/BUILD.gn
index 792e1e54..d553270 100644
--- a/ios/chrome/browser/ui/settings/password/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/BUILD.gn
@@ -212,7 +212,6 @@
     "//ios/chrome/browser/passwords/model:save_passwords_consumer",
     "//ios/chrome/browser/passwords/model:store_factory",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/shared/ui/table_view:test_support",
@@ -278,7 +277,6 @@
     "//components/strings",
     "//components/webauthn/core/browser:passkey_model",
     "//ios/chrome/browser/passwords/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/sync/model",
     "//ios/chrome/browser/webauthn/model",
diff --git a/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn b/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn
index 039b3e2..cc9766a5 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn
@@ -34,7 +34,6 @@
     "//ios/chrome/browser/passwords/model/metrics",
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/ui/settings/password/password_settings/BUILD.gn b/ios/chrome/browser/ui/settings/password/password_settings/BUILD.gn
index 38de5308..3779a03 100644
--- a/ios/chrome/browser/ui/settings/password/password_settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/password_settings/BUILD.gn
@@ -29,7 +29,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/ui/settings/password/password_sharing/BUILD.gn b/ios/chrome/browser/ui/settings/password/password_sharing/BUILD.gn
index 92ce4bf9..72833cf 100644
--- a/ios/chrome/browser/ui/settings/password/password_sharing/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/password_sharing/BUILD.gn
@@ -53,7 +53,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/symbols",
diff --git a/ios/chrome/browser/ui/settings/password/password_sharing/password_sharing_coordinator.mm b/ios/chrome/browser/ui/settings/password/password_sharing/password_sharing_coordinator.mm
index 14c1fc3..d90daa459 100644
--- a/ios/chrome/browser/ui/settings/password/password_sharing/password_sharing_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/password/password_sharing/password_sharing_coordinator.mm
@@ -13,7 +13,6 @@
 #import "ios/chrome/browser/passwords/model/ios_chrome_password_sender_service_factory.h"
 #import "ios/chrome/browser/shared/coordinator/alert/alert_coordinator.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
 #import "ios/chrome/browser/signin/model/identity_manager_factory.h"
diff --git a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/BUILD.gn b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/BUILD.gn
index bca2b0d..dae9cbd 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/passwords_in_other_apps/BUILD.gn
@@ -70,7 +70,6 @@
     "//base/test:test_support",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/ui/settings/password:features",
diff --git a/ios/chrome/browser/ui/settings/password/reauthentication/BUILD.gn b/ios/chrome/browser/ui/settings/password/reauthentication/BUILD.gn
index 0433f2da..aaad5ef 100644
--- a/ios/chrome/browser/ui/settings/password/reauthentication/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/reauthentication/BUILD.gn
@@ -75,7 +75,6 @@
     "//base/test:test_support",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/ui/settings/password:features",
diff --git a/ios/chrome/browser/ui/settings/privacy/BUILD.gn b/ios/chrome/browser/ui/settings/privacy/BUILD.gn
index 3cd48d0..9db653fc 100644
--- a/ios/chrome/browser/ui/settings/privacy/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/privacy/BUILD.gn
@@ -43,7 +43,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
@@ -103,7 +102,6 @@
     "//ios/chrome/browser/content_settings/model",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/ui/settings/privacy/lockdown_mode/BUILD.gn b/ios/chrome/browser/ui/settings/privacy/lockdown_mode/BUILD.gn
index ba4605b2..12bf450f 100644
--- a/ios/chrome/browser/ui/settings/privacy/lockdown_mode/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/privacy/lockdown_mode/BUILD.gn
@@ -19,7 +19,6 @@
     "//ios/chrome/browser/net/model:crurl",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_guide/BUILD.gn b/ios/chrome/browser/ui/settings/privacy/privacy_guide/BUILD.gn
index 1a80d03..9f65563 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_guide/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_guide/BUILD.gn
@@ -26,7 +26,6 @@
     "//components/unified_consent",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
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 4f58e566..93b9508 100644
--- a/ios/chrome/browser/ui/settings/privacy/safe_browsing/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/privacy/safe_browsing/BUILD.gn
@@ -33,7 +33,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
index 26d280c9..226f4f6 100644
--- a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
@@ -46,6 +46,7 @@
     "safety_check_mediator+Testing.h",
     "safety_check_mediator.h",
     "safety_check_mediator.mm",
+    "safety_check_mediator_delegate.h",
     "safety_check_utils.h",
     "safety_check_utils.mm",
   ]
@@ -63,10 +64,12 @@
     "//ios/chrome/browser/omaha/model",
     "//ios/chrome/browser/passwords/model",
     "//ios/chrome/browser/passwords/model:password_checkup_utils",
+    "//ios/chrome/browser/push_notification/model:push_notification_client",
+    "//ios/chrome/browser/push_notification/model:push_notification_service",
+    "//ios/chrome/browser/push_notification/model:push_notification_service_header",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
@@ -79,6 +82,7 @@
     "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/sync/model",
+    "//ios/chrome/browser/ui/push_notification:opt_in_alert_coordinator",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/cells",
     "//ios/chrome/browser/ui/settings/cells:public",
@@ -130,7 +134,6 @@
     "//ios/chrome/browser/passwords/model",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/ui/settings/safety_check/DEPS b/ios/chrome/browser/ui/settings/safety_check/DEPS
new file mode 100644
index 0000000..fa9f9d77
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/safety_check/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ios/chrome/browser/ui/push_notification",
+]
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h b/ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h
index 8d49e32..704fdc9 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h
@@ -111,4 +111,8 @@
 // Accessibility identifier for the Check Now button in the Safety Check module.
 extern NSString* const kSafetyCheckCheckNowButtonAccessibilityID;
 
+// Accessibility identifier for the button on the Safety Check page that lets
+// users opt in to receive notifications.
+extern NSString* const kSafetyCheckNotificationsOptInButtonAccessibilityID;
+
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_constants.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_constants.mm
index 0530412..02227a1 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_constants.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_constants.mm
@@ -22,3 +22,6 @@
 
 NSString* const kSafetyCheckCheckNowButtonAccessibilityID =
     @"SafetyCheckCheckNowButtonAccessibilityID";
+
+NSString* const kSafetyCheckNotificationsOptInButtonAccessibilityID =
+    @"SafetyCheckNotificationsOptInButtonAccessibilityID";
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_consumer.h b/ios/chrome/browser/ui/settings/safety_check/safety_check_consumer.h
index 8548aa3..218971b4 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_consumer.h
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_consumer.h
@@ -23,6 +23,9 @@
 // Initializes the check start section with `item`.
 - (void)setCheckStartItem:(TableViewItem*)item;
 
+// Initializes the notification opt-in section with `item`.
+- (void)setNotificationsOptInItem:(TableViewItem*)item;
+
 // Initializes the footer with timestamp of last completed run.
 - (void)setTimestampFooterItem:(TableViewLinkHeaderFooterItem*)item;
 
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.h b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.h
index 55d2e0b..aa6bebb 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.h
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.h
@@ -44,6 +44,20 @@
 // Start a safety check if it is not currently running.
 - (void)startCheckIfNotRunning;
 
+// Updates the UI to display the appropriate notifications button
+// (either to "Turn on" or "Turn off" notifications) based on the provided
+// `enabled` parameter.
+//
+// If `enabled` is YES, the button will prompt users to "Turn off" notifications
+// and, when tapped, will terminate the ongoing process of sending Safety Check
+// push notifications and may present a toast confirming the change.
+//
+// If 'enabled' is NO, the button will prompt users to "Turn on" notifications
+// and, when tapped, may present an opt-in prompt if the user has not yet
+// granted notification permission. If permission is granted or already exists,
+// it will initiate the process of sending Safety Check push notifications.
+- (void)updateNotificationsButton:(BOOL)enabled;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
index c12e2f5..cd90a8e 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
@@ -16,26 +16,36 @@
 #import "ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.h"
 #import "ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.h"
 #import "ios/chrome/browser/passwords/model/password_checkup_utils.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_service.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_settings_util.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/public/commands/application_commands.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/commands/open_new_tab_command.h"
+#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
+#import "ios/chrome/browser/shared/public/commands/snackbar_commands.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
 #import "ios/chrome/browser/signin/model/authentication_service_factory.h"
 #import "ios/chrome/browser/sync/model/sync_service_factory.h"
+#import "ios/chrome/browser/ui/push_notification/notifications_opt_in_alert_coordinator.h"
 #import "ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.h"
 #import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.h"
 #import "ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_coordinator.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h"
+#import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_delegate.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_navigation_commands.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_ui_swift.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/common/ui/elements/popover_label_view_controller.h"
+#import "ios/chrome/grit/ios_strings.h"
 #import "net/base/apple/url_conversions.h"
+#import "ui/base/l10n/l10n_util.h"
+#import "ui/base/l10n/l10n_util_mac.h"
 #import "url/gurl.h"
 
 using password_manager::WarningType;
@@ -44,7 +54,9 @@
     PasswordCheckupCoordinatorDelegate,
     PopoverLabelViewControllerDelegate,
     PrivacySafeBrowsingCoordinatorDelegate,
+    NotificationsOptInAlertCoordinatorDelegate,
     SafetyCheckNavigationCommands,
+    SafetyCheckMediatorDelegate,
     SafetyCheckTableViewControllerPresentationDelegate>
 
 // Safety check mediator.
@@ -74,7 +86,10 @@
 
 @end
 
-@implementation SafetyCheckCoordinator
+@implementation SafetyCheckCoordinator {
+  // Alert Coordinator used to display the notifications system prompt.
+  NotificationsOptInAlertCoordinator* _optInAlertCoordinator;
+}
 
 @synthesize baseNavigationController = _baseNavigationController;
 
@@ -122,6 +137,7 @@
 
   self.mediator.consumer = self.viewController;
   self.mediator.handler = self;
+  self.mediator.delegate = self;
   self.viewController.serviceDelegate = self.mediator;
   self.viewController.presentationDelegate = self;
 
@@ -141,6 +157,57 @@
   [self.passwordCheckupCoordinator stop];
   self.passwordCheckupCoordinator.delegate = nil;
   self.passwordCheckupCoordinator = nil;
+
+  [_optInAlertCoordinator stop];
+  _optInAlertCoordinator = nil;
+}
+
+- (void)updateNotificationsButton:(BOOL)enabled {
+  CHECK(IsSafetyCheckNotificationsEnabled());
+
+  [self.mediator reconfigureNotificationsSection:enabled];
+}
+
+#pragma mark - SafetyCheckMediatorDelegate
+
+- (void)toggleSafetyCheckNotifications {
+  CHECK(IsSafetyCheckNotificationsEnabled());
+
+  // Safety Check notifications are controlled by app-wide notification
+  // settings, not profile-specific ones. No Gaia ID is required below in
+  // `GetMobileNotificationPermissionStatusForClient()`.
+  if (push_notification_settings::
+          GetMobileNotificationPermissionStatusForClient(
+              PushNotificationClientId::kSafetyCheck, "")) {
+    [self disableNotifications];
+
+    return;
+  }
+
+  [self enableNotifications];
+}
+
+#pragma mark - NotificationsOptInAlertCoordinatorDelegate
+
+- (void)notificationsOptInAlertCoordinator:
+            (NotificationsOptInAlertCoordinator*)alertCoordinator
+                                    result:
+                                        (NotificationsOptInAlertResult)result {
+  CHECK_EQ(_optInAlertCoordinator, alertCoordinator);
+  [_optInAlertCoordinator stop];
+  _optInAlertCoordinator = nil;
+
+  switch (result) {
+    case NotificationsOptInAlertResult::kPermissionGranted:
+      [_mediator reconfigureNotificationsSection:YES];
+      break;
+    case NotificationsOptInAlertResult::kPermissionDenied:
+    case NotificationsOptInAlertResult::kOpenedSettings:
+    case NotificationsOptInAlertResult::kCanceled:
+    case NotificationsOptInAlertResult::kError:
+      [_mediator reconfigureNotificationsSection:NO];
+      break;
+  }
 }
 
 #pragma mark - SafetyCheckTableViewControllerPresentationDelegate
@@ -291,4 +358,63 @@
   self.privacySafeBrowsingCoordinator = nil;
 }
 
+#pragma mark - Private methods
+
+// Prompts the user to opt-in to Safety Check push notifications.
+// If the user grants permission, updates the push notification service
+// preferences.
+- (void)enableNotifications {
+  CHECK(IsSafetyCheckNotificationsEnabled());
+
+  [_optInAlertCoordinator stop];
+
+  _optInAlertCoordinator = [[NotificationsOptInAlertCoordinator alloc]
+      initWithBaseViewController:self.viewController
+                         browser:self.browser];
+
+  _optInAlertCoordinator.delegate = self;
+
+  _optInAlertCoordinator.clientIds =
+      std::vector{PushNotificationClientId::kSafetyCheck};
+
+  _optInAlertCoordinator.confirmationMessage = l10n_util::GetNSStringF(
+      IDS_IOS_NOTIFICATIONS_CONFIRMATION_MESSAGE,
+      l10n_util::GetStringUTF16(IDS_IOS_SAFETY_CHECK_TITLE));
+
+  [_optInAlertCoordinator start];
+}
+
+// Opts the user out of Safety Check notifications and updates the push
+// notification service preferences. Displays a confirmation snackbar with a
+// link to notification settings.
+- (void)disableNotifications {
+  CHECK(IsSafetyCheckNotificationsEnabled());
+
+  GetApplicationContext()->GetPushNotificationService()->SetPreference(
+      nil, PushNotificationClientId::kSafetyCheck, false);
+
+  // Show confirmation snackbar.
+  NSString* buttonText =
+      l10n_util::GetNSString(IDS_IOS_NOTIFICATIONS_MANAGE_SETTINGS);
+
+  NSString* message = l10n_util::GetNSStringF(
+      IDS_IOS_NOTIFICATIONS_CONFIRMATION_MESSAGE_OFF,
+      l10n_util::GetStringUTF16(IDS_IOS_SAFETY_CHECK_TITLE));
+
+  CommandDispatcher* dispatcher = self.browser->GetCommandDispatcher();
+
+  id<SnackbarCommands> snackbarHandler =
+      HandlerForProtocol(dispatcher, SnackbarCommands);
+
+  __weak id<SettingsCommands> weakSettingsHandler =
+      HandlerForProtocol(dispatcher, SettingsCommands);
+
+  [snackbarHandler showSnackbarWithMessage:message
+                                buttonText:buttonText
+                             messageAction:^{
+                               [weakSettingsHandler showNotificationsSettings];
+                             }
+                          completionAction:nil];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator+Testing.h b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator+Testing.h
index 00d97b1..60a36f42 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator+Testing.h
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator+Testing.h
@@ -48,6 +48,10 @@
 // Row button to start the safety check.
 @property(nonatomic, strong, readonly) TableViewTextItem* checkStartItem;
 
+// Row button to opt-in to Safety Check notifications.
+@property(nonatomic, strong, readonly)
+    TableViewTextItem* notificationsOptInItem;
+
 // Current state of the start safety check row button.
 @property(nonatomic, assign) CheckStartStates checkStartState;
 
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h
index ef2d1317..a8a3f59 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h
@@ -5,12 +5,12 @@
 #ifndef IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_MEDIATOR_H_
 #define IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_MEDIATOR_H_
 
-#import "ios/chrome/browser/ui/settings/safety_check/safety_check_service_delegate.h"
-
-#include "base/memory/scoped_refptr.h"
-
 #import <UIKit/UIKit.h>
 
+#import "base/memory/scoped_refptr.h"
+#import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_delegate.h"
+#import "ios/chrome/browser/ui/settings/safety_check/safety_check_service_delegate.h"
+
 // Webpage with safe browsing toggle.
 extern const char kSafeBrowsingStringURL[];
 
@@ -55,12 +55,19 @@
 // Starts a safety check if one is not currently running.
 - (void)startCheckIfNotRunning;
 
+// Updates the display of the notifications opt-in section based on whether push
+// notifications are `enabled`.
+- (void)reconfigureNotificationsSection:(BOOL)enabled;
+
 // The consumer for the Safety Check mediator.
 @property(nonatomic, weak) id<SafetyCheckConsumer> consumer;
 
 // Handler used to navigate inside the safety check.
 @property(nonatomic, weak) id<SafetyCheckNavigationCommands> handler;
 
+// Delegate used to communicate events back to the owner of this class.
+@property(nonatomic, weak) id<SafetyCheckMediatorDelegate> delegate;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
index 4e22613..74887691 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h"
-#import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator+Testing.h"
 
 #import "base/apple/foundation_util.h"
 #import "base/metrics/histogram_functions.h"
@@ -30,6 +29,8 @@
 #import "ios/chrome/browser/passwords/model/password_check_observer_bridge.h"
 #import "ios/chrome/browser/passwords/model/password_checkup_utils.h"
 #import "ios/chrome/browser/passwords/model/password_store_observer_bridge.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_settings_util.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_backed_boolean.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
@@ -44,6 +45,7 @@
 #import "ios/chrome/browser/ui/settings/cells/settings_check_item.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_consumer.h"
+#import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator+Testing.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_navigation_commands.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_utils.h"
@@ -82,6 +84,8 @@
   // CheckStart section.
   CheckStartItemType,
   TimestampFooterItem,
+  // Notifications opt-in section.
+  NotificationsOptInItemType,
 };
 
 // The minimum time each of the three checks should show a running state. This
@@ -213,6 +217,9 @@
 // Current state of the start safety check row button.
 @property(nonatomic, assign) CheckStartStates checkStartState;
 
+// Row button to opt-in to Safety Check notifications.
+@property(nonatomic, strong) TableViewTextItem* notificationsOptInItem;
+
 // Whether or not a safety check just ran.
 @property(nonatomic, assign) BOOL checkDidRun;
 
@@ -242,6 +249,7 @@
                 syncService:(syncer::SyncService*)syncService
                    referrer:(password_manager::PasswordCheckReferrer)referrer {
   self = [super init];
+
   if (self) {
     DCHECK(userPrefService);
     DCHECK(localPrefService);
@@ -346,7 +354,28 @@
     _checkStartItem.text = GetNSString(IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON);
     _checkStartItem.textColor = [UIColor colorNamed:kBlueColor];
     _checkStartItem.accessibilityTraits |= UIAccessibilityTraitButton;
+
+    if (IsSafetyCheckNotificationsEnabled()) {
+      TableViewTextItem* notificationsOptInItem =
+          [[TableViewTextItem alloc] initWithType:NotificationsOptInItemType];
+
+      notificationsOptInItem.accessibilityIdentifier =
+          kSafetyCheckNotificationsOptInButtonAccessibilityID;
+      notificationsOptInItem.text =
+          push_notification_settings::
+                  GetMobileNotificationPermissionStatusForClient(
+                      PushNotificationClientId::kSafetyCheck, "")
+              ? GetNSString(
+                    IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_TURN_OFF_NOTIFICATIONS_ELLIPSIS)
+              : GetNSString(
+                    IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_TURN_ON_NOTIFICATIONS_ELLIPSIS);
+      notificationsOptInItem.textColor = [UIColor colorNamed:kBlueColor];
+      notificationsOptInItem.accessibilityTraits |= UIAccessibilityTraitButton;
+
+      self.notificationsOptInItem = notificationsOptInItem;
+    }
   }
+
   return self;
 }
 
@@ -360,6 +389,7 @@
   [_consumer setCheckItems:checkItems];
   [_consumer setSafetyCheckHeaderItem:self.headerItem];
   [_consumer setCheckStartItem:self.checkStartItem];
+  [_consumer setNotificationsOptInItem:self.notificationsOptInItem];
 
   // Need to reconfigure the safety check items if there are remaining issues
   // from the last check ran.
@@ -377,6 +407,21 @@
   [self startCheck];
 }
 
+- (void)reconfigureNotificationsSection:(BOOL)enabled {
+  CHECK(IsSafetyCheckNotificationsEnabled());
+
+  // If notifications are `enabled`, the button should prompt users to disable
+  // them.
+  self.notificationsOptInItem.text =
+      enabled
+          ? GetNSString(
+                IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_TURN_OFF_NOTIFICATIONS_ELLIPSIS)
+          : GetNSString(
+                IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_TURN_ON_NOTIFICATIONS_ELLIPSIS);
+
+  [self reconfigureCellForItem:self.notificationsOptInItem];
+}
+
 #pragma mark - PasswordCheckObserver
 
 - (void)passwordCheckStateDidChange:(PasswordCheckState)state {
@@ -484,6 +529,10 @@
       [self checkStartOrCancel];
       break;
     }
+    case NotificationsOptInItemType: {
+      [self.delegate toggleSafetyCheckNotifications];
+      break;
+    }
     case HeaderItem:
     case TimestampFooterItem:
       break;
@@ -506,12 +555,14 @@
     case HeaderItem:
     case TimestampFooterItem:
       return NO;
+    case NotificationsOptInItemType:
+      return YES;
   }
 }
 
 - (BOOL)isItemWithErrorInfo:(TableViewItem*)item {
   SafteyCheckItemType type = static_cast<SafteyCheckItemType>(item.type);
-  return (type != CheckStartItemType);
+  return (type != CheckStartItemType && type != NotificationsOptInItemType);
 }
 
 - (void)infoButtonWasTapped:(UIButton*)buttonView
@@ -566,6 +617,7 @@
     case HeaderItem:
     case SafeBrowsingItemType:
     case TimestampFooterItem:
+    case NotificationsOptInItemType:
       return nil;
   }
 }
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_delegate.h b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_delegate.h
new file mode 100644
index 0000000..be8a8b1
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_delegate.h
@@ -0,0 +1,21 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_MEDIATOR_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_MEDIATOR_DELEGATE_H_
+
+// Delegate protocol for handling user interactions related to Safety Check
+// push notifications. The delegate is responsible for coordinating the enabling
+// and disabling of notifications, potentially involving user interface
+// presentation.
+@protocol SafetyCheckMediatorDelegate <NSObject>
+
+// Toggles Safety Check notifications on/off. The coordinator should present
+// appropriate UI (e.g., notification opt-in/opt-out) to the user
+// based on the current notification state.
+- (void)toggleSafetyCheckNotifications;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_MEDIATOR_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm
index 7a44785..354a220 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm
@@ -39,6 +39,7 @@
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
+#import "ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_item.h"
@@ -144,7 +145,10 @@
           return std::unique_ptr<KeyedService>(
               std::make_unique<affiliations::FakeAffiliationService>());
         })));
-    browser_state_ = std::move(test_cbs_builder).Build();
+
+    browser_state_ =
+        profile_manager_.AddProfileWithBuilder(std::move(test_cbs_builder));
+
     AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
         browser_state_.get(),
         std::make_unique<FakeAuthenticationServiceDelegate>());
@@ -264,10 +268,11 @@
   base::test::ScopedFeatureList feature_list_;
   web::WebTaskEnvironment environment_;
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  raw_ptr<ChromeBrowserState> browser_state_;
   scoped_refptr<TestPasswordStore> store_;
   raw_ptr<AuthenticationService> auth_service_;
   scoped_refptr<IOSChromePasswordCheckManager> password_check_;
+  TestProfileManagerIOS profile_manager_;
   SafetyCheckMediator* mediator_;
   raw_ptr<PrefService> pref_service_;
   raw_ptr<PrefService> local_pref_service_;
@@ -822,3 +827,47 @@
   [mediator_ reconfigureCheckStartSection];
   EXPECT_TRUE([mediator_ isItemClickable:checkStartItem]);
 }
+
+// Tests that the notifications opt-in button correctly displays the "Turn Off"
+// notifications prompt when notifications are currently enabled.
+TEST_F(SafetyCheckMediatorTest, NotificationsOptInButtonPromptsTurnOff) {
+  feature_list_.InitWithFeatures({kSafetyCheckNotifications}, {});
+
+  mediator_ = [[SafetyCheckMediator alloc]
+      initWithUserPrefService:pref_service_
+             localPrefService:local_pref_service_
+         passwordCheckManager:password_check_
+                  authService:auth_service_
+                  syncService:syncService()
+                     referrer:password_manager::PasswordCheckReferrer::
+                                  kSafetyCheck];
+
+  [mediator_ reconfigureNotificationsSection:YES];
+
+  EXPECT_NSEQ(
+      mediator_.notificationsOptInItem.text,
+      GetNSString(
+          IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_TURN_OFF_NOTIFICATIONS_ELLIPSIS));
+}
+
+// Tests that the notifications opt-in button correctly displays the "Turn On"
+// notifications prompt when notifications are currently disabled.
+TEST_F(SafetyCheckMediatorTest, NotificationsOptInButtonPromptsTurnOn) {
+  feature_list_.InitWithFeatures({kSafetyCheckNotifications}, {});
+
+  mediator_ = [[SafetyCheckMediator alloc]
+      initWithUserPrefService:pref_service_
+             localPrefService:local_pref_service_
+         passwordCheckManager:password_check_
+                  authService:auth_service_
+                  syncService:syncService()
+                     referrer:password_manager::PasswordCheckReferrer::
+                                  kSafetyCheck];
+
+  [mediator_ reconfigureNotificationsSection:NO];
+
+  EXPECT_NSEQ(
+      mediator_.notificationsOptInItem.text,
+      GetNSString(
+          IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_TURN_ON_NOTIFICATIONS_ELLIPSIS));
+}
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.swift b/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.swift
index 9e5cb85..f072347 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.swift
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.swift
@@ -31,6 +31,7 @@
   @objc static let accessibilityIdentifier = "kSafetyCheckTableViewId"
 
   @objc weak var presentationDelegate: SafetyCheckTableViewControllerPresentationDelegate?
+
   // Handler for taps on items on the safety check page.
   @objc weak var serviceDelegate: SafetyCheckServiceDelegate?
 
@@ -39,6 +40,7 @@
   enum SettingsIdentifier: Int, SettingsEnum {
     case checkTypes
     case checkStart
+    case notificationsOptIn
   }
 
   // MARK: Private members
@@ -71,6 +73,13 @@
     }
   }
 
+  // Notifications opt-in button.
+  private var notificationsOptInItem: TableViewItem? {
+    didSet {
+      reloadData()
+    }
+  }
+
   override func viewDidLoad() {
     super.viewDidLoad()
     self.tableView.accessibilityIdentifier = SafetyCheckTableViewController.accessibilityIdentifier
@@ -95,6 +104,10 @@
     safetyCheckFooterItem = item
   }
 
+  func setNotificationsOptIn(_ item: TableViewItem!) {
+    notificationsOptInItem = item
+  }
+
   // MARK: LegacyChromeTableViewController
 
   override func loadModel() {
@@ -122,6 +135,12 @@
         self.tableViewModel.setFooter(footerItem, forSectionWithIdentifier: checkStart)
       }
     }
+
+    if let item = self.notificationsOptInItem {
+      let notificationsOptIn = SettingsIdentifier.notificationsOptIn.settingsRawValue
+      self.tableViewModel.addSection(withIdentifier: notificationsOptIn)
+      self.tableViewModel.add(item, toSectionWithIdentifier: notificationsOptIn)
+    }
   }
 
   // MARK: UIViewController
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 0f5f102..5d85ddb 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -55,6 +55,7 @@
 #import "ios/chrome/browser/passwords/model/password_checkup_utils.h"
 #import "ios/chrome/browser/photos/model/photos_service.h"
 #import "ios/chrome/browser/photos/model/photos_service_factory.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_settings_util.h"
 #import "ios/chrome/browser/search_engines/model/search_engine_observer_bridge.h"
 #import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
@@ -2050,6 +2051,21 @@
   }
 }
 
+// Updates the state of the Safety Check notifications button based on whether
+// the user has Safety Check notifications enabled.
+- (void)updateSafetyCheckNotificationsButtonState {
+  CHECK(IsSafetyCheckNotificationsEnabled());
+
+  // Safety Check notifications are controlled by app-wide notification
+  // settings, not profile-specific ones. No Gaia ID is required below in
+  // `GetMobileNotificationPermissionStatusForClient()`.
+  BOOL enabled = push_notification_settings::
+      GetMobileNotificationPermissionStatusForClient(
+          PushNotificationClientId::kSafetyCheck, "");
+
+  [_safetyCheckCoordinator updateNotificationsButton:enabled];
+}
+
 // Updates the string indicating the push notification state.
 - (void)updateNotificationsDetailText {
   if (!_notificationsItem) {
@@ -2649,6 +2665,11 @@
 - (void)notificationsSettingsDidChangeForClient:
     (PushNotificationClientId)clientID {
   [self updateNotificationsDetailText];
+
+  if (IsSafetyCheckNotificationsEnabled() &&
+      clientID == PushNotificationClientId::kSafetyCheck) {
+    [self updateSafetyCheckNotificationsButtonState];
+  }
 }
 
 #pragma mark - DownloadsSettingsCoordinatorDelegate
diff --git a/ios/chrome/browser/ui/settings/sync/BUILD.gn b/ios/chrome/browser/ui/settings/sync/BUILD.gn
index a7bce5b..7305668 100644
--- a/ios/chrome/browser/ui/settings/sync/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/sync/BUILD.gn
@@ -27,7 +27,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/sharing/BUILD.gn b/ios/chrome/browser/ui/sharing/BUILD.gn
index f881dda..0ac0a60 100644
--- a/ios/chrome/browser/ui/sharing/BUILD.gn
+++ b/ios/chrome/browser/ui/sharing/BUILD.gn
@@ -66,7 +66,6 @@
     "//ios/chrome/browser/bookmarks/model:test_support",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/sharing/activity_services/BUILD.gn b/ios/chrome/browser/ui/sharing/activity_services/BUILD.gn
index 36ac561..1326a86 100644
--- a/ios/chrome/browser/ui/sharing/activity_services/BUILD.gn
+++ b/ios/chrome/browser/ui/sharing/activity_services/BUILD.gn
@@ -29,7 +29,6 @@
     "//ios/chrome/browser/shared/coordinator/default_browser_promo",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
diff --git a/ios/chrome/browser/ui/sharing/activity_services/data/BUILD.gn b/ios/chrome/browser/ui/sharing/activity_services/data/BUILD.gn
index 9b06e44..ebe776c 100644
--- a/ios/chrome/browser/ui/sharing/activity_services/data/BUILD.gn
+++ b/ios/chrome/browser/ui/sharing/activity_services/data/BUILD.gn
@@ -28,7 +28,6 @@
     "//base",
     "//components/send_tab_to_self",
     "//ios/chrome/browser/find_in_page/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/ui/util:url_with_title",
diff --git a/ios/chrome/browser/ui/tab_switcher/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/BUILD.gn
index fc59804..b2c1bbb0 100644
--- a/ios/chrome/browser/ui/tab_switcher/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/BUILD.gn
@@ -52,7 +52,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/BUILD.gn
index f5dc529..0c7914a 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/BUILD.gn
@@ -53,7 +53,6 @@
     "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn
index b7557d0..9c099ee 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn
@@ -22,7 +22,6 @@
     "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
@@ -71,7 +70,6 @@
     "//ios/chrome/browser/saved_tab_groups/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/incognito/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/incognito/BUILD.gn
index 062666a..1df547c1 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/incognito/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/incognito/BUILD.gn
@@ -24,7 +24,6 @@
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/regular/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/regular/BUILD.gn
index b0d0573..9acd4763 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/regular/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/regular/BUILD.gn
@@ -17,7 +17,6 @@
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/regular/tabs_closure_animation.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/regular/tabs_closure_animation.mm
index 31e45c44..3e303db 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/regular/tabs_closure_animation.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/regular/tabs_closure_animation.mm
@@ -45,7 +45,8 @@
 CAGradientLayer* GetAnimatedDisappearingGradient(CGRect frame,
                                                  CGFloat end_radius,
                                                  NSTimeInterval duration,
-                                                 NSTimeInterval delay) {
+                                                 NSTimeInterval delay,
+                                                 NSTimeInterval media_time) {
   CAGradientLayer* gradient_layer = [CAGradientLayer layer];
   gradient_layer.type = kCAGradientLayerRadial;
 
@@ -69,7 +70,7 @@
   gradient_layer.frame =
       CGRectMake(CGPointZero.x, CGPointZero.y, frame_size, frame_size);
 
-  NSTimeInterval start_time = [gradient_layer convertTime:CACurrentMediaTime()
+  NSTimeInterval start_time = [gradient_layer convertTime:media_time
                                                 fromLayer:nil];
 
   // Circle expanding animation. We'll use the end point to expand the circle
@@ -77,8 +78,8 @@
   // the end.
   CABasicAnimation* expandAnimation =
       [CABasicAnimation animationWithKeyPath:@"endPoint"];
-  expandAnimation.duration = duration - delay;
   expandAnimation.beginTime = start_time + delay;
+  expandAnimation.duration = duration - delay;
   expandAnimation.fromValue =
       [NSValue valueWithCGPoint:gradient_layer.endPoint];
   expandAnimation.toValue = [NSValue
@@ -98,8 +99,8 @@
       [CABasicAnimation animationWithKeyPath:@"colors"];
   // The opacity animation should be shorter than the main one since the circle
   // should be fully transparent before it stops growing.
-  opacity_animation.duration = kDisappearingOpacityDuration;
   opacity_animation.beginTime = start_time + delay;
+  opacity_animation.duration = kDisappearingOpacityDuration;
   opacity_animation.fromValue = gradient_layer.colors;
   opacity_animation.toValue = @[
     (id)[UIColor colorWithWhite:1.0 alpha:0.0].CGColor,
@@ -121,7 +122,9 @@
 
 // Returns the animated gradient that creates a "wipe" effect the size of
 // `frame`. Callers are expected to add the gradient to the view hierarchy.
-CAGradientLayer* GetAnimatedWipeEffect(CGRect frame, NSTimeInterval duration) {
+CAGradientLayer* GetAnimatedWipeEffect(CGRect frame,
+                                       NSTimeInterval duration,
+                                       NSTimeInterval media_time) {
   CAGradientLayer* gradient_layer = [CAGradientLayer layer];
   gradient_layer.type = kCAGradientLayerRadial;
   gradient_layer.colors = @[
@@ -139,15 +142,15 @@
   gradient_layer.frame =
       CGRectMake(CGPointZero.x, CGPointZero.y, frame_size, frame_size);
 
-  NSTimeInterval startTime = [gradient_layer convertTime:CACurrentMediaTime()
+  NSTimeInterval startTime = [gradient_layer convertTime:media_time
                                                fromLayer:nil];
 
   // Expand circle animation.  We'll use the end point to expand the circle past
   // the size of the frame so we end up with a fully colored view at the end.
   CABasicAnimation* end_point_animation =
       [CABasicAnimation animationWithKeyPath:@"endPoint"];
-  end_point_animation.duration = duration;
   end_point_animation.beginTime = startTime;
+  end_point_animation.duration = duration;
   end_point_animation.fromValue =
       [NSValue valueWithCGPoint:gradient_layer.endPoint];
   end_point_animation.toValue = [NSValue
@@ -190,7 +193,7 @@
   // `gradient_layer`.
   CAGradientLayer* inner_gradient_layer = GetAnimatedDisappearingGradient(
       frame, kWipeDisappearingAnimationEndRadius, duration,
-      kWipeDisappearingAnimationDelay);
+      kWipeDisappearingAnimationDelay, media_time);
   gradient_layer.mask = inner_gradient_layer;
 
   return gradient_layer;
@@ -228,17 +231,20 @@
     [weakSelf onAnimationCompletedWithCompletionBlock:completion];
   }];
 
-  [self addWipeEffectAnimation];
-  [self addGridCellDisapperingAnimation];
+  CFTimeInterval mediaTime = CACurrentMediaTime();
+
+  [self addWipeEffectAnimationWithMediaTime:mediaTime];
+  [self addGridCellDisapperingAnimationWithMediaTime:mediaTime];
 
   [CATransaction commit];
 }
 
 #pragma mark - Private
 
-// Adds the "wipe" effect animation to `window`.
-- (void)addWipeEffectAnimation {
-  _gradientLayer = GetAnimatedWipeEffect(_window.frame, kAnimationDuration);
+// Adds the "wipe" effect animation to `window` with `mediaTime`.
+- (void)addWipeEffectAnimationWithMediaTime:(CFTimeInterval)mediaTime {
+  _gradientLayer =
+      GetAnimatedWipeEffect(_window.frame, kAnimationDuration, mediaTime);
   // The grid view is scrollable. The animation should happen on what is visible
   // in the window not in the middle of the grid view which might not even be
   // visible.
@@ -246,12 +252,12 @@
   [_window.layer addSublayer:_gradientLayer];
 }
 
-// Adds the disappering animation to all views in `_gridCells`.
-- (void)addGridCellDisapperingAnimation {
+// Adds the disappering animation to all views in `_gridCells` with `mediaTime`.
+- (void)addGridCellDisapperingAnimationWithMediaTime:(CFTimeInterval)mediaTime {
   for (UIView* cell : _gridCells) {
     CAGradientLayer* gridCellGradientLayer = GetAnimatedDisappearingGradient(
         _window.frame, kGridCellDisappearingAnimationEndRadius,
-        kAnimationDuration, kGridCellDisappearingAnimationDelay);
+        kAnimationDuration, kGridCellDisappearingAnimationDelay, mediaTime);
 
     // Get position of the cell on the tab grid's coordinate system. The
     // `gridCellGradientLayer` position coordinates are in the cell's coordinate
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/BUILD.gn
index c066a27..12806d2 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/BUILD.gn
@@ -16,7 +16,6 @@
     "//ios/chrome/browser/default_browser/model:utils",
     "//ios/chrome/browser/drag_and_drop/model",
     "//ios/chrome/browser/main/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/BUILD.gn
index 876cd062..fef0f72 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/BUILD.gn
@@ -19,7 +19,6 @@
     "//ios/chrome/browser/bookmarks/ui_bundled:utils",
     "//ios/chrome/browser/ntp/model:util",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/model/web_state_list:utils",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_groups/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_groups/BUILD.gn
index 703d62e..7777d3f 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_groups/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_groups/BUILD.gn
@@ -33,7 +33,6 @@
     "//ios/chrome/browser/saved_tab_groups/model",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -81,7 +80,6 @@
     "//components/tab_groups",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/model/web_state_list:utils",
@@ -318,7 +316,6 @@
     "//components/saved_tab_groups:fake_tab_group_sync_service",
     "//ios/chrome/browser/saved_tab_groups/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
   ]
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_groups/tab_group_sync_earl_grey_app_interface.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_groups/tab_group_sync_earl_grey_app_interface.mm
index bf075e7..116bf50 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_groups/tab_group_sync_earl_grey_app_interface.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_groups/tab_group_sync_earl_grey_app_interface.mm
@@ -7,7 +7,7 @@
 #import "components/saved_tab_groups/fake_tab_group_sync_service.h"
 #import "ios/chrome/browser/saved_tab_groups/model/tab_group_sync_service_factory.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/model/profile/profile_manager_ios.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/toolbars/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/toolbars/BUILD.gn
index 0dd254f..e38819b 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/toolbars/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/toolbars/BUILD.gn
@@ -20,7 +20,6 @@
     "//ios/chrome/browser/feature_engagement/model",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/BUILD.gn
index d5ba3014..014ab6d8 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/BUILD.gn
@@ -26,7 +26,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/ui/toolbar/BUILD.gn b/ios/chrome/browser/ui/toolbar/BUILD.gn
index 0809ff9..73358ba 100644
--- a/ios/chrome/browser/ui/toolbar/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/BUILD.gn
@@ -64,7 +64,6 @@
     "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/ui/toolbar/buttons/BUILD.gn b/ios/chrome/browser/ui/toolbar/buttons/BUILD.gn
index c8cc6b15..85b498c 100644
--- a/ios/chrome/browser/ui/toolbar/buttons/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/buttons/BUILD.gn
@@ -27,7 +27,6 @@
     "//ios/chrome/app/theme",
     "//ios/chrome/browser/intents:intents_donation_helper",
     "//ios/chrome/browser/iph_for_new_chrome_user/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
index 0dd7b49..27be17f 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
@@ -127,7 +127,7 @@
   if (!browser->GetBrowserState()->IsOffTheRecord()) {
     deviceSwitcherResult =
         segmentation_platform::SegmentationPlatformServiceFactory::
-            GetDispatcherForBrowserState(browser->GetBrowserState());
+            GetDispatcherForProfile(browser->GetProfile());
   }
   self.toolbarMediator = [[ToolbarMediator alloc]
       initWithWebStateList:browser->GetWebStateList()
diff --git a/ios/chrome/browser/ui/voice/BUILD.gn b/ios/chrome/browser/ui/voice/BUILD.gn
index fe1ca56..139033b 100644
--- a/ios/chrome/browser/ui/voice/BUILD.gn
+++ b/ios/chrome/browser/ui/voice/BUILD.gn
@@ -13,7 +13,6 @@
     "//base",
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/web/public",
   ]
diff --git a/ios/chrome/browser/unified_consent/model/BUILD.gn b/ios/chrome/browser/unified_consent/model/BUILD.gn
index 4a332c8..c71fee9d 100644
--- a/ios/chrome/browser/unified_consent/model/BUILD.gn
+++ b/ios/chrome/browser/unified_consent/model/BUILD.gn
@@ -14,7 +14,6 @@
     "//components/sync",
     "//components/sync_preferences",
     "//components/unified_consent",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/unit_conversion/BUILD.gn b/ios/chrome/browser/unit_conversion/BUILD.gn
index 385e472..2cd434c 100644
--- a/ios/chrome/browser/unit_conversion/BUILD.gn
+++ b/ios/chrome/browser/unit_conversion/BUILD.gn
@@ -11,7 +11,6 @@
     "//base",
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/public/provider/chrome/browser/unit_conversion:unit_conversion_api",
   ]
 }
diff --git a/ios/chrome/browser/unit_conversion/ui_bundled/BUILD.gn b/ios/chrome/browser/unit_conversion/ui_bundled/BUILD.gn
index 90ea6457..6f84358d 100644
--- a/ios/chrome/browser/unit_conversion/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/unit_conversion/ui_bundled/BUILD.gn
@@ -16,7 +16,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/unit_conversion",
@@ -105,7 +104,6 @@
   deps = [
     "//base",
     "//base/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/test/app:test_support",
   ]
diff --git a/ios/chrome/browser/url_loading/model/BUILD.gn b/ios/chrome/browser/url_loading/model/BUILD.gn
index bb4ef36b..d1ace3e2 100644
--- a/ios/chrome/browser/url_loading/model/BUILD.gn
+++ b/ios/chrome/browser/url_loading/model/BUILD.gn
@@ -37,7 +37,6 @@
     "//ios/chrome/browser/sessions/model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -100,7 +99,6 @@
     "//ios/chrome/browser/ntp/model",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/tab_insertion/model",
diff --git a/ios/chrome/browser/visited_url_ranking/model/visited_url_ranking_service_factory.mm b/ios/chrome/browser/visited_url_ranking/model/visited_url_ranking_service_factory.mm
index b4e904b..b366aab 100644
--- a/ios/chrome/browser/visited_url_ranking/model/visited_url_ranking_service_factory.mm
+++ b/ios/chrome/browser/visited_url_ranking/model/visited_url_ranking_service_factory.mm
@@ -87,8 +87,9 @@
            std::unique_ptr<visited_url_ranking::URLVisitAggregatesTransformer>>
       transformers = {};
 
-  auto* segmentation_platform_service = segmentation_platform::
-      SegmentationPlatformServiceFactory::GetForBrowserState(browser_state);
+  auto* segmentation_platform_service =
+      segmentation_platform::SegmentationPlatformServiceFactory::GetForProfile(
+          browser_state);
   transformers.emplace(
       visited_url_ranking::URLVisitAggregatesTransformType::
           kSegmentationMetricsData,
diff --git a/ios/chrome/browser/voice/model/BUILD.gn b/ios/chrome/browser/voice/model/BUILD.gn
index aa375e8..d3d1351 100644
--- a/ios/chrome/browser/voice/model/BUILD.gn
+++ b/ios/chrome/browser/voice/model/BUILD.gn
@@ -62,7 +62,6 @@
     "//base",
     "//components/google/core/common",
     "//components/prefs",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web/public",
     "//ios/web/public/js_messaging:js_messaging",
diff --git a/ios/chrome/browser/web/model/BUILD.gn b/ios/chrome/browser/web/model/BUILD.gn
index b36ee8b16..5c6fe9d1 100644
--- a/ios/chrome/browser/web/model/BUILD.gn
+++ b/ios/chrome/browser/web/model/BUILD.gn
@@ -54,7 +54,6 @@
     "//ios/chrome/browser/ntp/model",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/utils",
@@ -102,7 +101,6 @@
     "//ios/chrome/browser/overlays/model/public/web_content_area",
     "//ios/chrome/browser/permissions/model:tab_helper",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/snapshots/model",
@@ -188,7 +186,6 @@
     "//ios/chrome/browser/overlays/model",
     "//ios/chrome/browser/overlays/model/public/web_content_area",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
@@ -304,7 +301,6 @@
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/segmentation_platform/model:ukm_client",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
@@ -452,7 +448,6 @@
     "//components/content_settings/core/browser",
     "//components/content_settings/core/browser:cookie_settings",
     "//components/lookalikes/core",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/test/app:test_support",
     "//ios/components/security_interstitials/lookalikes",
     "//ios/testing/earl_grey:eg_app_support+eg2",
diff --git a/ios/chrome/browser/web/model/annotations/BUILD.gn b/ios/chrome/browser/web/model/annotations/BUILD.gn
index 571a953e..7dd3e5c4 100644
--- a/ios/chrome/browser/web/model/annotations/BUILD.gn
+++ b/ios/chrome/browser/web/model/annotations/BUILD.gn
@@ -17,7 +17,6 @@
     "//ios/chrome/browser/parcel_tracking:features",
     "//ios/chrome/browser/parcel_tracking:prefs",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/web/model/font_size/BUILD.gn b/ios/chrome/browser/web/model/font_size/BUILD.gn
index 6bf0afe..ea0f4b1 100644
--- a/ios/chrome/browser/web/model/font_size/BUILD.gn
+++ b/ios/chrome/browser/web/model/font_size/BUILD.gn
@@ -19,7 +19,6 @@
     "//components/prefs",
     "//components/ukm/ios:ukm_url_recorder",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/web/model:feature_flags",
diff --git a/ios/chrome/browser/web/model/print/BUILD.gn b/ios/chrome/browser/web/model/print/BUILD.gn
index fa434835..4ac8dfb 100644
--- a/ios/chrome/browser/web/model/print/BUILD.gn
+++ b/ios/chrome/browser/web/model/print/BUILD.gn
@@ -16,7 +16,6 @@
     ":print_js",
     "//base",
     "//components/prefs",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web/public",
diff --git a/ios/chrome/browser/web_state_list/model/BUILD.gn b/ios/chrome/browser/web_state_list/model/BUILD.gn
index d833c54..a94ba86 100644
--- a/ios/chrome/browser/web_state_list/model/BUILD.gn
+++ b/ios/chrome/browser/web_state_list/model/BUILD.gn
@@ -9,7 +9,6 @@
   ]
   deps = [
     "//base",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
   ]
 }
diff --git a/ios/chrome/browser/web_state_list/model/web_usage_enabler/BUILD.gn b/ios/chrome/browser/web_state_list/model/web_usage_enabler/BUILD.gn
index a264f4af..a550f46 100644
--- a/ios/chrome/browser/web_state_list/model/web_usage_enabler/BUILD.gn
+++ b/ios/chrome/browser/web_state_list/model/web_usage_enabler/BUILD.gn
@@ -16,7 +16,6 @@
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/web_state_list",
   ]
 }
diff --git a/ios/chrome/browser/webui/ui_bundled/BUILD.gn b/ios/chrome/browser/webui/ui_bundled/BUILD.gn
index 280a613..5fcb521 100644
--- a/ios/chrome/browser/webui/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/webui/ui_bundled/BUILD.gn
@@ -92,7 +92,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
@@ -136,7 +135,6 @@
     "//ios/chrome/browser/commerce/model:shopping_service",
     "//ios/chrome/browser/omaha/model",
     "//ios/chrome/browser/optimization_guide/resources:on_device_llm_buildflags",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/public/features:system_flags",
diff --git a/ios/chrome/browser/webui/ui_bundled/gcm/BUILD.gn b/ios/chrome/browser/webui/ui_bundled/gcm/BUILD.gn
index 986e67b8..b84befbb 100644
--- a/ios/chrome/browser/webui/ui_bundled/gcm/BUILD.gn
+++ b/ios/chrome/browser/webui/ui_bundled/gcm/BUILD.gn
@@ -12,7 +12,6 @@
     "//components/gcm_driver",
     "//components/resources",
     "//ios/chrome/browser/gcm/model",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/web/public/webui",
diff --git a/ios/chrome/browser/webui/ui_bundled/interstitials/BUILD.gn b/ios/chrome/browser/webui/ui_bundled/interstitials/BUILD.gn
index 8b52a81..1f1f309 100644
--- a/ios/chrome/browser/webui/ui_bundled/interstitials/BUILD.gn
+++ b/ios/chrome/browser/webui/ui_bundled/interstitials/BUILD.gn
@@ -20,7 +20,6 @@
     "//components/security_interstitials/core:unsafe_resource",
     "//ios/chrome/browser/safe_browsing/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/ssl/model",
diff --git a/ios/chrome/browser/webui/ui_bundled/local_state/BUILD.gn b/ios/chrome/browser/webui/ui_bundled/local_state/BUILD.gn
index 2fb21eb..b5b7b702 100644
--- a/ios/chrome/browser/webui/ui_bundled/local_state/BUILD.gn
+++ b/ios/chrome/browser/webui/ui_bundled/local_state/BUILD.gn
@@ -13,7 +13,6 @@
     "//components/prefs",
     "//components/resources",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/web/public/webui",
diff --git a/ios/chrome/browser/webui/ui_bundled/net_export/BUILD.gn b/ios/chrome/browser/webui/ui_bundled/net_export/BUILD.gn
index 2e2fe302..be172da 100644
--- a/ios/chrome/browser/webui/ui_bundled/net_export/BUILD.gn
+++ b/ios/chrome/browser/webui/ui_bundled/net_export/BUILD.gn
@@ -14,7 +14,6 @@
     "//components/resources",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/webui/model",
diff --git a/ios/chrome/browser/webui/ui_bundled/optimization_guide_internals/BUILD.gn b/ios/chrome/browser/webui/ui_bundled/optimization_guide_internals/BUILD.gn
index 76b9db2..ed1e7cbe 100644
--- a/ios/chrome/browser/webui/ui_bundled/optimization_guide_internals/BUILD.gn
+++ b/ios/chrome/browser/webui/ui_bundled/optimization_guide_internals/BUILD.gn
@@ -15,7 +15,6 @@
     "//components/optimization_guide/optimization_guide_internals/webui:url_constants",
     "//ios/chrome/browser/optimization_guide/model",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/web/public",
     "//ios/web/public/webui",
diff --git a/ios/chrome/browser/webui/ui_bundled/policy/BUILD.gn b/ios/chrome/browser/webui/ui_bundled/policy/BUILD.gn
index 5cdc6db..d362dd22 100644
--- a/ios/chrome/browser/webui/ui_bundled/policy/BUILD.gn
+++ b/ios/chrome/browser/webui/ui_bundled/policy/BUILD.gn
@@ -25,7 +25,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/policy/model",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/ui/util",
diff --git a/ios/chrome/browser/webui/ui_bundled/translate_internals/BUILD.gn b/ios/chrome/browser/webui/ui_bundled/translate_internals/BUILD.gn
index 286d735c..98adc43 100644
--- a/ios/chrome/browser/webui/ui_bundled/translate_internals/BUILD.gn
+++ b/ios/chrome/browser/webui/ui_bundled/translate_internals/BUILD.gn
@@ -10,13 +10,13 @@
     "translate_internals_ui.mm",
   ]
   deps = [
+    "//base",
     "//components/language/ios/browser",
     "//components/translate/core/common",
     "//components/translate/translate_internals",
     "//ios/chrome/app/resources:ios_resources",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index cad589aa..d6bc3c4 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -79,7 +79,6 @@
     "//ios/chrome/browser/promos_manager/model:features",
     "//ios/chrome/browser/promos_manager/model:test_support",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
     "//ios/chrome/browser/shared/ui/util",
     "//ios/components/security_interstitials/safe_browsing:test_support",
@@ -361,7 +360,6 @@
     "//ios/chrome/browser/shared/coordinator/layout_guide:unit_tests",
     "//ios/chrome/browser/shared/coordinator/scene:unit_tests",
     "//ios/chrome/browser/shared/model/browser:unit_tests",
-    "//ios/chrome/browser/shared/model/browser_state:unit_tests",
     "//ios/chrome/browser/shared/model/prefs:unit_tests",
     "//ios/chrome/browser/shared/model/profile:unit_tests",
     "//ios/chrome/browser/shared/model/profile/test:unit_tests",
diff --git a/ios/chrome/test/app/BUILD.gn b/ios/chrome/test/app/BUILD.gn
index cf767ea1..9e75fe9 100644
--- a/ios/chrome/test/app/BUILD.gn
+++ b/ios/chrome/test/app/BUILD.gn
@@ -75,7 +75,6 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/coordinator/scene:scene_testing",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 9ed059f..8ec15e3 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -43,6 +43,7 @@
     "//ios/chrome/browser/saved_tab_groups/model",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/browser_state",
+    "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/chrome/browser/signin/model",
@@ -142,7 +143,6 @@
     "//ios/chrome/browser/sessions/model:session_restoration_service_factory",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
-    "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/test/earl_grey/eg_tests_hook.mm b/ios/chrome/test/earl_grey/eg_tests_hook.mm
index af8eed1..8ca18d2 100644
--- a/ios/chrome/test/earl_grey/eg_tests_hook.mm
+++ b/ios/chrome/test/earl_grey/eg_tests_hook.mm
@@ -24,7 +24,7 @@
 #import "ios/chrome/browser/saved_tab_groups/model/tab_group_local_update_observer.h"
 #import "ios/chrome/browser/shared/model/browser/browser_list.h"
 #import "ios/chrome/browser/shared/model/browser/browser_list_factory.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/signin/model/fake_system_identity.h"
 #import "ios/chrome/browser/signin/model/fake_system_identity_manager.h"
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm
index 66ed1c9..c4c22b30 100644
--- a/ios/testing/earl_grey/base_earl_grey_test_case.mm
+++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -38,6 +38,7 @@
 + (void)setUp {
   NSArray<NSString*>* blockedURLs = @[
     @".*app-measurement\\.com.*",
+    @".*google\\.com.*",
     @".*app-analytics-services\\.com.*",
   ];
   [[GREYConfiguration sharedConfiguration]
diff --git a/ios/web/init/web_main_loop.mm b/ios/web/init/web_main_loop.mm
index 9e088bc6..60cc7f8 100644
--- a/ios/web/init/web_main_loop.mm
+++ b/ios/web/init/web_main_loop.mm
@@ -115,7 +115,7 @@
   // of it?
   // TODO(crbug.com/40240952): Remove this once we have confidence PowerMonitor
   // is not needed for iOS
-  base::PowerMonitor::Initialize(
+  base::PowerMonitor::GetInstance()->Initialize(
       std::make_unique<base::PowerMonitorDeviceSource>());
 
   return result_code_;
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
index 6e92b44..2fbaf44 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
@@ -87,7 +87,7 @@
   void ShowDeleteAddressProfileDialog(
       const AutofillProfile& profile,
       AddressProfileDeleteDialogCallback delete_dialog_callback) override;
-  void ShowAutofillSuggestions(
+  SuggestionUiSessionId ShowAutofillSuggestions(
       const AutofillClient::PopupOpenArgs& open_args,
       base::WeakPtr<AutofillSuggestionDelegate> delegate) override;
   void UpdateAutofillDataListValues(
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
index 0f11cad..5bac607 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
@@ -221,10 +221,12 @@
   NOTREACHED_IN_MIGRATION();
 }
 
-void WebViewAutofillClientIOS::ShowAutofillSuggestions(
+AutofillClient::SuggestionUiSessionId
+WebViewAutofillClientIOS::ShowAutofillSuggestions(
     const AutofillClient::PopupOpenArgs& open_args,
     base::WeakPtr<AutofillSuggestionDelegate> delegate) {
   [bridge_ showAutofillPopup:open_args.suggestions suggestionDelegate:delegate];
+  return SuggestionUiSessionId();
 }
 
 void WebViewAutofillClientIOS::UpdateAutofillDataListValues(
diff --git a/media/audio/mac/audio_manager_mac.cc b/media/audio/mac/audio_manager_mac.cc
index b967c23..9b8c5d0 100644
--- a/media/audio/mac/audio_manager_mac.cc
+++ b/media/audio/mac/audio_manager_mac.cc
@@ -462,7 +462,7 @@
  public:
   AudioPowerObserver()
       : is_suspending_(false),
-        is_monitoring_(base::PowerMonitor::IsInitialized()),
+        is_monitoring_(base::PowerMonitor::GetInstance()->IsInitialized()),
         num_resume_notifications_(0) {
     // The PowerMonitor requires significant setup (a CFRunLoop and preallocated
     // IO ports) so it's not available under unit tests.  See the OSX impl of
@@ -470,7 +470,7 @@
     if (!is_monitoring_) {
       return;
     }
-    base::PowerMonitor::AddPowerSuspendObserver(this);
+    base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
   }
 
   AudioPowerObserver(const AudioPowerObserver&) = delete;
@@ -481,7 +481,7 @@
     if (!is_monitoring_) {
       return;
     }
-    base::PowerMonitor::RemovePowerSuspendObserver(this);
+    base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
   }
 
   bool IsSuspending() const {
@@ -500,7 +500,7 @@
 
   bool IsOnBatteryPower() const {
     DCHECK(thread_checker_.CalledOnValidThread());
-    return base::PowerMonitor::IsOnBatteryPower();
+    return base::PowerMonitor::GetInstance()->IsOnBatteryPower();
   }
 
  private:
diff --git a/media/audio/power_observer_helper.cc b/media/audio/power_observer_helper.cc
index 2293be9..353e998 100644
--- a/media/audio/power_observer_helper.cc
+++ b/media/audio/power_observer_helper.cc
@@ -29,12 +29,12 @@
   // TODO(grunell): We could be suspending when adding this as observer, and
   // we won't be notified about that. See if we can add
   // PowerMonitorSource::IsSuspending() so that this can be checked here.
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 }
 
 PowerObserverHelper::~PowerObserverHelper() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 }
 
 bool PowerObserverHelper::IsSuspending() const {
diff --git a/media/base/media_track.cc b/media/base/media_track.cc
index 2c1f161..47e1b1c 100644
--- a/media/base/media_track.cc
+++ b/media/base/media_track.cc
@@ -6,19 +6,6 @@
 
 namespace media {
 
-MediaTrack::MediaTrack(Type type,
-                       bool enabled,
-                       StreamParser::TrackId bytestream_track_id,
-                       const Kind& kind,
-                       const Label& label,
-                       const Language& lang)
-    : type_(type),
-      enabled_(enabled),
-      bytestream_track_id_(bytestream_track_id),
-      kind_(kind),
-      label_(label),
-      language_(lang) {}
-
 MediaTrack::~MediaTrack() = default;
 
 const char* TrackTypeToStr(MediaTrack::Type type) {
@@ -31,4 +18,98 @@
   NOTREACHED();
 }
 
+// static
+MediaTrack::Kind MediaTrack::VideoKindToString(MediaTrack::VideoKind kind) {
+  switch (kind) {
+    case MediaTrack::VideoKind::kAlternative:
+      return MediaTrack::Kind{"alternative"};
+    case MediaTrack::VideoKind::kCaptions:
+      return MediaTrack::Kind{"captions"};
+    case MediaTrack::VideoKind::kMain:
+      return MediaTrack::Kind{"main"};
+    case MediaTrack::VideoKind::kSign:
+      return MediaTrack::Kind{"sign"};
+    case MediaTrack::VideoKind::kSubtitles:
+      return MediaTrack::Kind{"subtitles"};
+    case MediaTrack::VideoKind::kCommentary:
+      return MediaTrack::Kind{"commentary"};
+    case MediaTrack::VideoKind::kNone:
+      return MediaTrack::Kind{""};
+  }
+}
+
+// static
+MediaTrack::Kind MediaTrack::AudioKindToString(MediaTrack::AudioKind kind) {
+  switch (kind) {
+    case MediaTrack::AudioKind::kAlternative:
+      return MediaTrack::Kind{"alternative"};
+    case MediaTrack::AudioKind::kDescriptions:
+      return MediaTrack::Kind{"descriptions"};
+    case MediaTrack::AudioKind::kMain:
+      return MediaTrack::Kind{"main"};
+    case MediaTrack::AudioKind::kMainDescriptions:
+      return MediaTrack::Kind{"main-desc"};
+    case MediaTrack::AudioKind::kTranslation:
+      return MediaTrack::Kind{"translation"};
+    case MediaTrack::AudioKind::kCommentary:
+      return MediaTrack::Kind{"commentary"};
+    case MediaTrack::AudioKind::kNone:
+      return MediaTrack::Kind{""};
+  }
+}
+
+// static
+MediaTrack MediaTrack::CreateVideoTrack(const std::string& id,
+                                        VideoKind kind,
+                                        const std::string& label,
+                                        const std::string& language,
+                                        bool enabled) {
+  return MediaTrack{MediaTrack::Type::kVideo,
+                    0,
+                    MediaTrack::Id{id},
+                    VideoKindToString(kind),
+                    Label{label},
+                    Language{language},
+                    enabled};
+}
+
+// static
+MediaTrack MediaTrack::CreateAudioTrack(const std::string& id,
+                                        AudioKind kind,
+                                        const std::string& label,
+                                        const std::string& language,
+                                        bool enabled) {
+  return MediaTrack{MediaTrack::Type::kAudio,
+                    0,
+                    MediaTrack::Id{id},
+                    AudioKindToString(kind),
+                    Label{label},
+                    Language{language},
+                    enabled};
+}
+
+MediaTrack::MediaTrack(MediaTrack::Type type,
+                       StreamParser::TrackId stream_id,
+                       const MediaTrack::Id& track_id,
+                       const MediaTrack::Kind& kind,
+                       const MediaTrack::Label& label,
+                       const MediaTrack::Language& language,
+                       bool enabled)
+    : type_(type),
+      enabled_(enabled),
+      stream_id_(stream_id),
+      track_id_(track_id),
+      kind_(kind),
+      label_(label),
+      language_(language) {}
+
+MediaTrack::MediaTrack(const MediaTrack& track)
+    : type_(track.type()),
+      enabled_(track.enabled()),
+      stream_id_(track.bytestream_track_id()),
+      track_id_(track.id()),
+      kind_(track.kind()),
+      label_(track.label()),
+      language_(track.language()) {}
+
 }  // namespace media
diff --git a/media/base/media_track.h b/media/base/media_track.h
index 9b313b33..6a6c6fa 100644
--- a/media/base/media_track.h
+++ b/media/base/media_track.h
@@ -16,49 +16,88 @@
 class MEDIA_EXPORT MediaTrack {
  public:
   enum class Type { kAudio, kVideo };
+
+  enum class VideoKind {
+    kNone,
+    kAlternative,
+    kCaptions,
+    kMain,
+    kSign,
+    kSubtitles,
+    kCommentary
+  };
+
+  enum class AudioKind {
+    kNone,
+    kAlternative,
+    kDescriptions,
+    kMain,
+    kMainDescriptions,
+    kTranslation,
+    kCommentary
+  };
+
   using Id = base::StrongAlias<class IdTag, std::string>;
   using Kind = base::StrongAlias<class KindTag, std::string>;
   using Label = base::StrongAlias<class LabelTag, std::string>;
   using Language = base::StrongAlias<class LanguageTag, std::string>;
-  MediaTrack(Type type,
-             bool enabled,
-             StreamParser::TrackId bytestream_track_id,
-             const Kind& kind,
-             const Label& label,
-             const Language& lang);
+
+  static MediaTrack CreateVideoTrack(const std::string& id,
+                                     VideoKind kind,
+                                     const std::string& label,
+                                     const std::string& language,
+                                     bool enabled);
+
+  static MediaTrack CreateAudioTrack(const std::string& id,
+                                     AudioKind kind,
+                                     const std::string& label,
+                                     const std::string& language,
+                                     bool enabled);
+
+  static MediaTrack::Kind VideoKindToString(MediaTrack::VideoKind kind);
+  static MediaTrack::Kind AudioKindToString(MediaTrack::AudioKind kind);
+
   ~MediaTrack();
+  MediaTrack(const MediaTrack&);
+  MediaTrack& operator=(const MediaTrack&) = default;
 
   Type type() const { return type_; }
-  bool enabled() const { return enabled_; }
-
-  StreamParser::TrackId bytestream_track_id() const {
-    return bytestream_track_id_;
-  }
+  const Id& id() const { return track_id_; }
   const Kind& kind() const { return kind_; }
   const Label& label() const { return label_; }
   const Language& language() const { return language_; }
+  StreamParser::TrackId bytestream_track_id() const { return stream_id_; }
+  bool enabled() const { return enabled_; }
 
-  Id id() const { return id_; }
   void set_id(Id id) {
-    DCHECK(id_.value().empty());
-    DCHECK(!id.value().empty());
-    id_ = id;
+    DCHECK(track_id_.value().empty() && !id.value().empty());
+    track_id_ = id;
   }
 
  private:
+  friend class MediaTracks;
+
+  MediaTrack(Type type,
+             StreamParser::TrackId stream_id,
+             const Id& track_id,
+             const Kind& kind,
+             const Label& label,
+             const Language& lang,
+             bool enabled);
+
   Type type_;
   bool enabled_;
 
-  // |bytestream_track_id_| is read from the bytestream and is guaranteed to be
+  // |stream_id_| is read from the bytestream and is guaranteed to be
   // unique only within the scope of single bytestream's initialization segment.
   // But we might have multiple bytestreams (MediaSource might have multiple
   // SourceBuffers attached to it, which translates into ChunkDemuxer having
   // multiple SourceBufferStates and multiple bytestreams) or subsequent init
   // segments may redefine the bytestream ids. Thus bytestream track ids are not
   // guaranteed to be unique at the Demuxer and HTMLMediaElement level. So we
-  // generate truly unique media track |id_| on the Demuxer level.
-  StreamParser::TrackId bytestream_track_id_;
-  Id id_;
+  // generate truly unique media track |track_id_| on the Demuxer level.
+  StreamParser::TrackId stream_id_;
+  Id track_id_;
 
   // These properties are read from input streams by stream parsers as specified
   // in https://dev.w3.org/html5/html-sourcing-inband-tracks/.
diff --git a/media/base/media_tracks.cc b/media/base/media_tracks.cc
index e6264b0..fbb50bb 100644
--- a/media/base/media_tracks.cc
+++ b/media/base/media_tracks.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/functional/bind.h"
+#include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
 #include "media/base/audio_decoder_config.h"
 #include "media/base/video_decoder_config.h"
@@ -26,9 +27,9 @@
     const MediaTrack::Language& language) {
   DCHECK(config.IsValidConfig());
   CHECK(audio_configs_.find(bytestream_track_id) == audio_configs_.end());
-  std::unique_ptr<MediaTrack> track =
-      std::make_unique<MediaTrack>(MediaTrack::Type::kAudio, enabled,
-                                   bytestream_track_id, kind, label, language);
+  auto track = base::WrapUnique(
+      new MediaTrack(MediaTrack::Type::kAudio, bytestream_track_id,
+                     MediaTrack::Id{""}, kind, label, language, enabled));
   MediaTrack* track_ptr = track.get();
   tracks_.push_back(std::move(track));
   audio_configs_[bytestream_track_id] = config;
@@ -44,9 +45,9 @@
     const MediaTrack::Language& language) {
   DCHECK(config.IsValidConfig());
   CHECK(video_configs_.find(bytestream_track_id) == video_configs_.end());
-  std::unique_ptr<MediaTrack> track =
-      std::make_unique<MediaTrack>(MediaTrack::Type::kVideo, enabled,
-                                   bytestream_track_id, kind, label, language);
+  auto track = base::WrapUnique(
+      new MediaTrack(MediaTrack::Type::kVideo, bytestream_track_id,
+                     MediaTrack::Id{""}, kind, label, language, enabled));
   MediaTrack* track_ptr = track.get();
   tracks_.push_back(std::move(track));
   video_configs_[bytestream_track_id] = config;
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 7338c95..9801b390 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -352,6 +352,7 @@
       "//components/chromeos_camera:mojo_mjpeg_decode_accelerator",
       "//components/chromeos_camera/common",
       "//components/device_event_log",
+      "//gpu/ipc/client:ipc_client_sources",
       "//gpu/ipc/common:common",
       "//media/capture/video/chromeos/mojom:cros_camera",
       "//media/capture/video/chromeos/mojom:document_scanner",
diff --git a/media/capture/video/chromeos/camera_buffer_factory.cc b/media/capture/video/chromeos/camera_buffer_factory.cc
index 5e6b239..bb163a38 100644
--- a/media/capture/video/chromeos/camera_buffer_factory.cc
+++ b/media/capture/video/chromeos/camera_buffer_factory.cc
@@ -5,7 +5,9 @@
 #include "media/capture/video/chromeos/camera_buffer_factory.h"
 
 #include "base/containers/contains.h"
+#include "base/functional/callback_helpers.h"
 #include "components/viz/common/resources/shared_image_format_utils.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
 #include "media/capture/video/chromeos/video_capture_device_factory_chromeos.h"
 
 namespace media {
@@ -22,14 +24,32 @@
 CameraBufferFactory::CreateGpuMemoryBuffer(const gfx::Size& size,
                                            gfx::BufferFormat format,
                                            gfx::BufferUsage usage) {
+  // TODO(b/363936240): Check if we can set and use GpuChannelHost in the
+  // browser process to create buffers.
+  // GpuChannelHost is able to be reset with new channel host to establish the
+  // new connection to GPU process via viz::Gpu APIs when GPU process crashes.
+  // Therefore, we use GpuChannelHost to create buffers in the utility process.
+  // |gpu_channel_host| is a nullptr in the browser process.
+  scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
+      VideoCaptureDeviceFactoryChromeOS::GetGpuChannelHost();
+  if (gpu_channel_host) {
+    gfx::GpuMemoryBufferHandle gmb_handle;
+    gpu_channel_host->CreateGpuMemoryBuffer(
+        size, viz::GetSharedImageFormat(format), usage, &gmb_handle);
+    return gpu_memory_buffer_support_.CreateGpuMemoryBufferImplFromHandle(
+        std::move(gmb_handle), size, format, usage, base::NullCallback());
+  }
+  // GpuMemoryBufferManagerSingleton is only available in the browser process.
+  // |buf_manager| is a nullptr in the utility process.
   gpu::GpuMemoryBufferManager* buf_manager =
       VideoCaptureDeviceFactoryChromeOS::GetBufferManager();
-  if (!buf_manager) {
-    LOG(ERROR) << "GpuMemoryBufferManager not set";
-    return nullptr;
+  if (buf_manager) {
+    return buf_manager->CreateGpuMemoryBuffer(size, format, usage,
+                                              gpu::kNullSurfaceHandle, nullptr);
   }
-  return buf_manager->CreateGpuMemoryBuffer(size, format, usage,
-                                            gpu::kNullSurfaceHandle, nullptr);
+  LOG(ERROR)
+      << "Both of GpuChannelHost and GpuMemoryBufferManager are not set.";
+  return nullptr;
 }
 
 scoped_refptr<gpu::ClientSharedImage> CameraBufferFactory::CreateSharedImage(
diff --git a/media/capture/video/chromeos/camera_buffer_factory.h b/media/capture/video/chromeos/camera_buffer_factory.h
index 1fb3afc..cafa735 100644
--- a/media/capture/video/chromeos/camera_buffer_factory.h
+++ b/media/capture/video/chromeos/camera_buffer_factory.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory>
 
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/capture/video/chromeos/mojom/camera3.mojom.h"
 #include "media/capture/video/chromeos/pixel_format_utils.h"
 #include "media/capture/video_capture_types.h"
@@ -53,6 +54,8 @@
   std::map<std::pair<cros::mojom::HalPixelFormat, gfx::BufferUsage>,
            ChromiumPixelFormat>
       resolved_format_usages_;
+
+  gpu::GpuMemoryBufferSupport gpu_memory_buffer_support_;
 };
 
 }  // namespace media
diff --git a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
index 0a7b8cb..b6b71aa 100644
--- a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
+++ b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/task/single_thread_task_runner.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
 #include "media/capture/video/chromeos/camera_app_device_bridge_impl.h"
 #include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
 
@@ -18,6 +19,7 @@
 
 gpu::GpuMemoryBufferManager* g_gpu_buffer_manager = nullptr;
 scoped_refptr<gpu::SharedImageInterface> g_shared_image_interface = nullptr;
+scoped_refptr<gpu::GpuChannelHost> gpu_channel_host_ = nullptr;
 
 }  // namespace
 
@@ -84,6 +86,18 @@
 }
 
 // static
+void VideoCaptureDeviceFactoryChromeOS::SetGpuChannelHost(
+    scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
+  gpu_channel_host_ = std::move(gpu_channel_host);
+}
+
+// static
+scoped_refptr<gpu::GpuChannelHost>
+VideoCaptureDeviceFactoryChromeOS::GetGpuChannelHost() {
+  return gpu_channel_host_;
+}
+
+// static
 gpu::SharedImageInterface*
 VideoCaptureDeviceFactoryChromeOS::GetSharedImageInterface() {
   return g_shared_image_interface.get();
diff --git a/media/capture/video/chromeos/video_capture_device_factory_chromeos.h b/media/capture/video/chromeos/video_capture_device_factory_chromeos.h
index 4e2c9d89..836ef2d4 100644
--- a/media/capture/video/chromeos/video_capture_device_factory_chromeos.h
+++ b/media/capture/video/chromeos/video_capture_device_factory_chromeos.h
@@ -15,6 +15,7 @@
 
 namespace gpu {
 class SharedImageInterface;
+class GpuChannelHost;
 }
 
 namespace media {
@@ -43,6 +44,10 @@
   static gpu::GpuMemoryBufferManager* GetBufferManager();
   static void SetGpuBufferManager(gpu::GpuMemoryBufferManager* buffer_manager);
 
+  static void SetGpuChannelHost(
+      scoped_refptr<gpu::GpuChannelHost> gpu_channel_host);
+  static scoped_refptr<gpu::GpuChannelHost> GetGpuChannelHost();
+
   static gpu::SharedImageInterface* GetSharedImageInterface();
   static void SetSharedImageInterface(
       scoped_refptr<gpu::SharedImageInterface> shared_image_interface);
diff --git a/media/gpu/chromeos/mailbox_video_frame_converter.h b/media/gpu/chromeos/mailbox_video_frame_converter.h
index 189d9fd..91d7ebd 100644
--- a/media/gpu/chromeos/mailbox_video_frame_converter.h
+++ b/media/gpu/chromeos/mailbox_video_frame_converter.h
@@ -21,7 +21,7 @@
 #include "media/gpu/chromeos/frame_resource_converter.h"
 #include "media/gpu/media_gpu_export.h"
 #include "third_party/skia/include/core/SkAlphaType.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/buffer_types.h"
 
 namespace base {
diff --git a/media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.cc b/media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.cc
index 0e27720..29aaaf09 100644
--- a/media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.cc
+++ b/media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.cc
@@ -112,15 +112,27 @@
                            /*read_write=*/true);
   permissions.push_back(BrokerFilePermission::ReadOnly("/dev/dri"));
 
+  permissions.push_back(
+      BrokerFilePermission::ReadOnly("/usr/share/vulkan/icd.d"));
+  permissions.push_back(BrokerFilePermission::ReadOnly(
+      "/usr/share/vulkan/icd.d/radeon_icd.x86_64.json"));
+
+  constexpr int kDlopenFlags = RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE;
   const char* radeonsi_lib = "/usr/lib64/dri/radeonsi_dri.so";
 #if defined(DRI_DRIVER_DIR)
   radeonsi_lib = DRI_DRIVER_DIR "/radeonsi_dri.so";
 #endif
-  if (nullptr == dlopen(radeonsi_lib, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE)) {
+  if (nullptr == dlopen(radeonsi_lib, kDlopenFlags)) {
     LOG(ERROR) << "dlopen(radeonsi_dri.so) failed with error: " << dlerror();
     return false;
   }
 
+  // minigbm may use the DRI driver (requires Mesa 24.0 or older) or the
+  // Vulkan driver (requires VK_EXT_image_drm_format_modifier).  Preload the
+  // Vulkan driver as well but ignore failures.
+  dlopen("libvulkan.so.1", kDlopenFlags);
+  dlopen("libvulkan_radeon.so", kDlopenFlags);
+
 #if BUILDFLAG(USE_VAAPI)
   VaapiWrapper::PreSandboxInitialization(/*allow_disabling_global_lock=*/true);
   return true;
diff --git a/media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.cc b/media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.cc
index e1dbbae..2154e19 100644
--- a/media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.cc
+++ b/media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.cc
@@ -68,6 +68,11 @@
       permissions.push_back(
           BrokerFilePermission::ReadOnlyRecursive(path + "/"));
     }
+
+    permissions.push_back(
+        BrokerFilePermission::ReadOnly("/usr/share/vulkan/icd.d"));
+    permissions.push_back(BrokerFilePermission::ReadOnly(
+        "/usr/share/vulkan/icd.d/radeon_icd.x86_64.json"));
   }
 #endif
 
@@ -110,15 +115,21 @@
   VaapiWrapper::PreSandboxInitialization(/*allow_disabling_global_lock=*/true);
 
   if (options.use_amd_specific_policies) {
+    constexpr int kDlopenFlags = RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE;
     const char* radeonsi_lib = "/usr/lib64/dri/radeonsi_dri.so";
 #if defined(DRI_DRIVER_DIR)
     radeonsi_lib = DRI_DRIVER_DIR "/radeonsi_dri.so";
 #endif
-    if (nullptr ==
-        dlopen(radeonsi_lib, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE)) {
+    if (nullptr == dlopen(radeonsi_lib, kDlopenFlags)) {
       LOG(ERROR) << "dlopen(radeonsi_dri.so) failed with error: " << dlerror();
       return false;
     }
+
+    // minigbm may use the DRI driver (requires Mesa 24.0 or older) or the
+    // Vulkan driver (requires VK_EXT_image_drm_format_modifier).  Preload the
+    // Vulkan driver as well but ignore failures.
+    dlopen("libvulkan.so.1", kDlopenFlags);
+    dlopen("libvulkan_radeon.so", kDlopenFlags);
   }
 #endif
   return true;
diff --git a/media/gpu/vaapi/vaapi_unittest.cc b/media/gpu/vaapi/vaapi_unittest.cc
index 785aa37e..b4da432 100644
--- a/media/gpu/vaapi/vaapi_unittest.cc
+++ b/media/gpu/vaapi/vaapi_unittest.cc
@@ -224,6 +224,10 @@
 #if VA_MAJOR_VERSION >= 2 || VA_MINOR_VERSION >= 18
     TOSTR(VAProfileH264High10);
 #endif
+#if VA_MAJOR_VERSION >= 2 || VA_MINOR_VERSION >= 22
+    TOSTR(VAProfileVVCMain10);
+    TOSTR(VAProfileVVCMultilayerMain10);
+#endif
   }
   // clang-format on
   return "<unknown profile>";
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
index b0ac2e57..189195e4 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -1031,6 +1031,13 @@
   encoder_info_.supports_frame_size_change =
       !workarounds_.disable_media_foundation_frame_size_change;
 
+  if (rate_ctrl_) {
+    // Software rate control should always have a trusted QP. We are safe to
+    // report encoder info right away.
+    client_->NotifyEncoderInfoChange(encoder_info_);
+    encoder_info_sent_ = true;
+  }
+
   return true;
 }
 
diff --git a/media/mojo/clients/mojo_stable_video_decoder.cc b/media/mojo/clients/mojo_stable_video_decoder.cc
index 921cef4..49711a9 100644
--- a/media/mojo/clients/mojo_stable_video_decoder.cc
+++ b/media/mojo/clients/mojo_stable_video_decoder.cc
@@ -25,7 +25,7 @@
 #include "media/gpu/macros.h"
 #include "media/video/gpu_video_accelerator_factories.h"
 #include "third_party/skia/include/core/SkAlphaType.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gpu_memory_buffer.h"
diff --git a/media/mojo/services/gpu_mojo_media_client_linux.cc b/media/mojo/services/gpu_mojo_media_client_linux.cc
index 6c8dcffc..c592c2e 100644
--- a/media/mojo/services/gpu_mojo_media_client_linux.cc
+++ b/media/mojo/services/gpu_mojo_media_client_linux.cc
@@ -45,20 +45,21 @@
 std::vector<Fourcc> GetPreferredRenderableFourccs(
     const gpu::GpuPreferences& gpu_preferences) {
   std::vector<Fourcc> renderable_fourccs;
-  // TODO(crbug.com/349428388): For HEVC Main 10 and VP9 Profile2 10-bit video,
-  // the current implementation requires additional VPP to convert the P010
-  // format to a renderable format. This VPP happens on the Vulkan path
-  // (P010 -> NV12) and OpenGL path (P010 -> AR24). While this VPP introduces a
-  // loss of color depth, it should be optimized for zero-copy path in the
-  // future.
 #if BUILDFLAG(ENABLE_VULKAN)
-  // Support for zero-copy NV12 textures preferentially.
+  // Support for zero-copy NV12/P010 textures preferentially.
   if (gpu_preferences.gr_context_type == gpu::GrContextType::kVulkan) {
     renderable_fourccs.emplace_back(Fourcc::NV12);
+    renderable_fourccs.emplace_back(Fourcc::P010);
   }
 #endif  // BUILDFLAG(ENABLE_VULKAN)
 
   // Support 1-copy argb textures.
+  //
+  // TODO(crbug.com/349428388): For VP9 Profile2 and HEVC Main 10 10-bit video,
+  // the current implementation requires additional VPP to convert the NV12/P010
+  // format to a renderable format AR24. While this VPP introduces a loss of
+  // color depth (P010 -> AR24), it should be optimized for zero-copy path in
+  // the future.
   renderable_fourccs.emplace_back(Fourcc::AR24);
 
   return renderable_fourccs;
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc
index e1f0e5ff..f989ff3 100644
--- a/media/renderers/audio_renderer_impl.cc
+++ b/media/renderers/audio_renderer_impl.cc
@@ -83,14 +83,16 @@
   // won't remove the observer until we're destructed on |task_runner_| so we
   // must post it here if we're on the wrong thread.
   if (task_runner_->RunsTasksInCurrentSequence()) {
-    base::PowerMonitor::AddPowerSuspendObserver(this);
+    base::PowerMonitor::GetInstance()->GetInstance()->AddPowerSuspendObserver(
+        this);
   } else {
     // Safe to post this without a WeakPtr because this class must be destructed
     // on the same thread and construction has not completed yet.
     task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(
-            IgnoreResult(&base::PowerMonitor::AddPowerSuspendObserver), this));
+            base::IgnoreResult(&base::PowerMonitor::AddPowerSuspendObserver),
+            base::Unretained(base::PowerMonitor::GetInstance()), this));
   }
 
   // Do not add anything below this line since the above actions are only safe
@@ -100,7 +102,8 @@
 AudioRendererImpl::~AudioRendererImpl() {
   DVLOG(1) << __func__;
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->GetInstance()->RemovePowerSuspendObserver(
+      this);
 
   // If Render() is in progress, this call will wait for Render() to finish.
   // After this call, the |sink_| will not call back into |this| anymore.
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index a9dec77..ffb7096 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -53,11 +53,11 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkImageGenerator.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 
diff --git a/media/renderers/paint_canvas_video_renderer_unittest.cc b/media/renderers/paint_canvas_video_renderer_unittest.cc
index 2e06ffb..ab54d45 100644
--- a/media/renderers/paint_canvas_video_renderer_unittest.cc
+++ b/media/renderers/paint_canvas_video_renderer_unittest.cc
@@ -7,6 +7,8 @@
 #pragma allow_unsafe_buffers
 #endif
 
+#include "media/renderers/paint_canvas_video_renderer.h"
+
 #include <GLES3/gl3.h>
 #include <stdint.h>
 
@@ -31,7 +33,6 @@
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
-#include "media/renderers/paint_canvas_video_renderer.h"
 #include "media/renderers/shared_image_video_frame_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libyuv/include/libyuv/convert.h"
@@ -40,7 +41,7 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gl/gl_implementation.h"
diff --git a/media/renderers/video_frame_rgba_to_yuva_converter.h b/media/renderers/video_frame_rgba_to_yuva_converter.h
index 2840648..932ad41 100644
--- a/media/renderers/video_frame_rgba_to_yuva_converter.h
+++ b/media/renderers/video_frame_rgba_to_yuva_converter.h
@@ -6,7 +6,7 @@
 #define MEDIA_RENDERERS_VIDEO_FRAME_RGBA_TO_YUVA_CONVERTER_H_
 
 #include "media/base/media_export.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 
 namespace gfx {
 class ColorSpace;
diff --git a/media/video/renderable_gpu_memory_buffer_video_frame_pool.h b/media/video/renderable_gpu_memory_buffer_video_frame_pool.h
index 2ff5165..c3d623d0 100644
--- a/media/video/renderable_gpu_memory_buffer_video_frame_pool.h
+++ b/media/video/renderable_gpu_memory_buffer_video_frame_pool.h
@@ -12,7 +12,7 @@
 #include "media/base/media_export.h"
 #include "media/base/video_types.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 #include "ui/gfx/buffer_types.h"
 
 namespace gfx {
diff --git a/mojo/public/tools/fuzzers/mojolpm.h b/mojo/public/tools/fuzzers/mojolpm.h
index be3de4cd..6170359 100644
--- a/mojo/public/tools/fuzzers/mojolpm.h
+++ b/mojo/public/tools/fuzzers/mojolpm.h
@@ -685,6 +685,7 @@
       }
     } else {
       next_sequence_idx_++;
+      next_action_idx_ = 0;
       fuzzer_task_runner->PostTask(FROM_HERE, std::move(run_closure));
     }
   }
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index b22abdd..064f4c5 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -91,10 +91,8 @@
 }
 
 bool IsOnBatteryPower() {
-  if (base::PowerMonitor::IsInitialized()) {
-    return base::PowerMonitor::IsOnBatteryPower();
-  }
-  return false;
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  return power_monitor->IsInitialized() && power_monitor->IsOnBatteryPower();
 }
 
 enum ExternallyConditionalizedType {
diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc
index d1cc859..2f49257 100644
--- a/net/http/http_network_layer.cc
+++ b/net/http/http_network_layer.cc
@@ -26,14 +26,14 @@
     : session_(session) {
   DCHECK(session_);
 #if BUILDFLAG(IS_WIN)
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 #endif
 }
 
 HttpNetworkLayer::~HttpNetworkLayer() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 #if BUILDFLAG(IS_WIN)
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 #endif
 }
 
diff --git a/net/socket/tcp_client_socket.cc b/net/socket/tcp_client_socket.cc
index 2ff5172..9244212 100644
--- a/net/socket/tcp_client_socket.cc
+++ b/net/socket/tcp_client_socket.cc
@@ -73,7 +73,7 @@
 TCPClientSocket::~TCPClientSocket() {
   Disconnect();
 #if defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
 #endif  // defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
 }
 
@@ -173,7 +173,7 @@
   if (socket_->IsValid())
     socket_->SetDefaultOptionsForClient();
 #if defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
-  base::PowerMonitor::AddPowerSuspendObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
 #endif  // defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
 }
 
diff --git a/net/socket/tcp_client_socket_unittest.cc b/net/socket/tcp_client_socket_unittest.cc
index 38dbde6..f0fef88 100644
--- a/net/socket/tcp_client_socket_unittest.cc
+++ b/net/socket/tcp_client_socket_unittest.cc
@@ -60,7 +60,9 @@
   TCPClientSocketTest()
       : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {}
 
-  ~TCPClientSocketTest() override { base::PowerMonitor::ShutdownForTesting(); }
+  ~TCPClientSocketTest() override {
+    base::PowerMonitor::GetInstance()->ShutdownForTesting();
+  }
 
   void Suspend() { power_monitor_source_.Suspend(); }
   void Resume() { power_monitor_source_.Resume(); }
diff --git a/net/third_party/quiche/src b/net/third_party/quiche/src
index b686c15..ae346f5 160000
--- a/net/third_party/quiche/src
+++ b/net/third_party/quiche/src
@@ -1 +1 @@
-Subproject commit b686c15a23031e9e18f911fc2c483f84a491789e
+Subproject commit ae346f567c2b7a60ccf20b4c05bc16dd94a80a0e
diff --git a/remoting/host/setup/host_starter_base.cc b/remoting/host/setup/host_starter_base.cc
index e3eaf5f..648f9d9 100644
--- a/remoting/host/setup/host_starter_base.cc
+++ b/remoting/host/setup/host_starter_base.cc
@@ -15,6 +15,7 @@
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -97,7 +98,8 @@
 
 void HostStarterBase::OnUserTokensRetrieved(const std::string& user_email,
                                             const std::string& access_token,
-                                            const std::string& refresh_token) {
+                                            const std::string& refresh_token,
+                                            const std::string& scope_str) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // If an owner email was not provided, then use the account which created the
@@ -106,6 +108,14 @@
     start_host_params_.owner_email = base::ToLowerASCII(user_email);
   }
 
+  // TODO(garykac): Setup host based on the scope.
+  std::vector<std::string> scopes = SplitString(
+      scope_str, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  LOG(INFO) << "User scopes:";
+  for (auto s : scopes) {
+    LOG(INFO) << "   " << s;
+  }
+
   // We don't need a `refresh_token` for the user so ignore it even if the
   // authorization_code was created with the offline param.
   RegisterNewHost(key_pair_->GetPublicKey(), access_token);
@@ -171,7 +181,8 @@
 void HostStarterBase::OnServiceAccountTokensRetrieved(
     const std::string& service_account_email,
     const std::string& access_token,
-    const std::string& refresh_token) {
+    const std::string& refresh_token,
+    const std::string& scopes) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (service_account_email_.empty()) {
diff --git a/remoting/host/setup/host_starter_base.h b/remoting/host/setup/host_starter_base.h
index a1ace9bf..bf3026c 100644
--- a/remoting/host/setup/host_starter_base.h
+++ b/remoting/host/setup/host_starter_base.h
@@ -65,7 +65,8 @@
   void OnExistingConfigLoaded(std::optional<base::Value::Dict> config);
   void OnUserTokensRetrieved(const std::string& user_email,
                              const std::string& access_token,
-                             const std::string& refresh_token);
+                             const std::string& refresh_token,
+                             const std::string& scopes);
   virtual void RegisterNewHost(const std::string& public_key,
                                std::optional<std::string> access_token) = 0;
   void OnNewHostRegistered(const std::string& directory_id,
@@ -74,7 +75,8 @@
                            const std::string& authorization_code);
   void OnServiceAccountTokensRetrieved(const std::string& service_account_email,
                                        const std::string& access_token,
-                                       const std::string& refresh_token);
+                                       const std::string& refresh_token,
+                                       const std::string& scopes);
   virtual void RemoveOldHostFromDirectory(
       base::OnceClosure on_host_removed) = 0;
   void StopOldHost();
diff --git a/remoting/host/setup/host_starter_base_unittest.cc b/remoting/host/setup/host_starter_base_unittest.cc
index e8c2354..a726da2 100644
--- a/remoting/host/setup/host_starter_base_unittest.cc
+++ b/remoting/host/setup/host_starter_base_unittest.cc
@@ -30,13 +30,23 @@
             "expires_in": 3600,
             "token_type": "Bearer"
          })";
-constexpr char kGetUserEmailResponseForUser[] = R"({"email": "user@test.com"})";
-constexpr char kGetUserEmailResponseForServiceAccount[] =
-    R"({"email": "robot@chromoting.com"})";
+// Note: We include "email" in the Token Info response because we require that
+// the scopes contain "userinfo.email", which causes "email" to be included.
+constexpr char kGetTokenInfoResponseForUser[] = R"({
+            "email": "user@test.com",
+            "expires_in": 3600,
+            "scope": "scope1 scope2"
+         })";
+constexpr char kGetTokenInfoResponseForServiceAccount[] = R"({
+            "email": "robot@chromoting.com",
+            "expires_in": 3600,
+            "scope": "scope1 scope2"
+         })";
 
 // Known values for testing config file generation.
 constexpr char kTestUserEmail[] = "user@test.com";
 constexpr char kTestRobotEmail[] = "robot@chromoting.com";
+constexpr char kTestScopes[] = "scope1 scope2";
 constexpr char kTestRobotAuthCode[] = "robot_auth_code";
 constexpr char kTestDirectoryId[] = "test_directory_id";
 constexpr char kTestMachineName[] = "test_machine_name";
@@ -156,6 +166,8 @@
 
   void clear_service_account_email() { service_account_email_.clear(); }
 
+  void clear_scopes() { scopes_.clear(); }
+
   void clear_robot_authorization_code() { robot_authorization_code_.clear(); }
 
  private:
@@ -165,6 +177,7 @@
   std::string directory_id_{kTestDirectoryId};
   std::string owner_account_email_{kTestUserEmail};
   std::string service_account_email_{kTestRobotEmail};
+  std::string scopes_{kTestScopes};
   std::string robot_authorization_code_{kTestRobotAuthCode};
 
   base::OnceClosure configure_gaia_for_service_account_;
@@ -293,16 +306,16 @@
   test_url_loader_factory_.AddResponse(
       GaiaUrls::GetInstance()->oauth2_token_url().spec(), kGetTokensResponse);
   test_url_loader_factory_.AddResponse(
-      GaiaUrls::GetInstance()->oauth_user_info_url().spec(),
-      kGetUserEmailResponseForUser);
+      GaiaUrls::GetInstance()->oauth2_token_info_url().spec(),
+      kGetTokenInfoResponseForUser);
 }
 
 void HostStarterBaseTest::ConfigureGaiaResponseForServiceAccount() {
   test_url_loader_factory_.AddResponse(
       GaiaUrls::GetInstance()->oauth2_token_url().spec(), kGetTokensResponse);
   test_url_loader_factory_.AddResponse(
-      GaiaUrls::GetInstance()->oauth_user_info_url().spec(),
-      kGetUserEmailResponseForServiceAccount);
+      GaiaUrls::GetInstance()->oauth2_token_info_url().spec(),
+      kGetTokenInfoResponseForServiceAccount);
 }
 
 TEST_F(HostStarterBaseTest, StartHostUsingOAuth) {
diff --git a/remoting/host/setup/host_starter_oauth_helper.cc b/remoting/host/setup/host_starter_oauth_helper.cc
index 20c9dcc..f8f4002 100644
--- a/remoting/host/setup/host_starter_oauth_helper.cc
+++ b/remoting/host/setup/host_starter_oauth_helper.cc
@@ -64,34 +64,51 @@
   access_token_ = access_token;
   refresh_token_ = refresh_token;
 
-  // Get the email corresponding to the access token.
-  oauth_client_->GetUserEmail(access_token, 1, this);
+  // Get information (email, scopes) associated with the access token.
+  oauth_client_->GetTokenInfo(access_token, 1, this);
 }
 
-void HostStarterOAuthHelper::OnRefreshTokenResponse(
-    const std::string& access_token,
-    int expires_in_seconds) {
-  // We never request a new access token using a refresh token.
-  NOTREACHED_IN_MIGRATION();
-}
-
-void HostStarterOAuthHelper::OnGetUserEmailResponse(
-    const std::string& user_email) {
+void HostStarterOAuthHelper::OnGetTokenInfoResponse(
+    const base::Value::Dict& token_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (token_info.FindString("error")) {
+    std::move(error_callback_)
+        .Run(*token_info.FindString("error"), Result::OAUTH_ERROR);
+    return;
+  }
+
+  const std::string* email = token_info.FindString("email");
+  if (!email) {
+    std::move(error_callback_)
+        .Run(base::StringPrintf(
+                 "missing email for authorization_code; expected: `%s`",
+                 expected_user_email_.c_str()),
+             Result::OAUTH_ERROR);
+    return;
+  }
   if (!expected_user_email_.empty() &&
-      !base::EqualsCaseInsensitiveASCII(expected_user_email_, user_email)) {
+      !base::EqualsCaseInsensitiveASCII(expected_user_email_, *email)) {
     // Verify that the token retrieved matches the expected user email.
     std::move(error_callback_)
         .Run(base::StringPrintf(
                  "authorization_code was created for `%s` which does not "
                  "match the expected account: `%s`",
-                 user_email.c_str(), expected_user_email_.c_str()),
+                 email->c_str(), expected_user_email_.c_str()),
              Result::PERMISSION_DENIED);
     return;
   }
 
-  std::move(done_callback_).Run(user_email, access_token_, refresh_token_);
+  const std::string* scopes = token_info.FindString("scope");
+  if (!scopes) {
+    std::move(error_callback_)
+        .Run(base::StringPrintf("missing scopes for user: `%s`",
+                                expected_user_email_.c_str()),
+             Result::OAUTH_ERROR);
+    return;
+  }
+
+  std::move(done_callback_).Run(*email, access_token_, refresh_token_, *scopes);
 }
 
 void HostStarterOAuthHelper::OnOAuthError() {
diff --git a/remoting/host/setup/host_starter_oauth_helper.h b/remoting/host/setup/host_starter_oauth_helper.h
index 0351b02..8b82b11f 100644
--- a/remoting/host/setup/host_starter_oauth_helper.h
+++ b/remoting/host/setup/host_starter_oauth_helper.h
@@ -25,7 +25,8 @@
   using OnDoneCallback =
       base::OnceCallback<void(const std::string& user_email,
                               const std::string& access_token,
-                              const std::string& refresh_token)>;
+                              const std::string& refresh_token,
+                              const std::string& scopes)>;
   using OnErrorCallback =
       base::OnceCallback<void(const std::string& error_message,
                               HostStarter::Result result)>;
@@ -49,9 +50,7 @@
   void OnGetTokensResponse(const std::string& refresh_token,
                            const std::string& access_token,
                            int expires_in_seconds) override;
-  void OnRefreshTokenResponse(const std::string& access_token,
-                              int expires_in_seconds) override;
-  void OnGetUserEmailResponse(const std::string& user_email) override;
+  void OnGetTokenInfoResponse(const base::Value::Dict& token_info) override;
   void OnOAuthError() override;
   void OnNetworkError(int response_code) override;
 
diff --git a/remoting/host/setup/host_starter_oauth_helper_unittest.cc b/remoting/host/setup/host_starter_oauth_helper_unittest.cc
index 7610760..4db525c 100644
--- a/remoting/host/setup/host_starter_oauth_helper_unittest.cc
+++ b/remoting/host/setup/host_starter_oauth_helper_unittest.cc
@@ -26,9 +26,16 @@
             "expires_in": 3600,
             "token_type": "Bearer"
          })";
+// Note: We include "email" in the Token Info response because we require that
+// the scopes contain "userinfo.email", which causes "email" to be included.
+constexpr char kGetTokenInfoResponse[] = R"({
+            "email": "user@test.com",
+            "expires_in": 3600,
+            "scope": "scope1 scope2"
+         })";
 constexpr char kTestEmail[] = "user@test.com";
 constexpr char kDifferentTestEmail[] = "different_user@test.com";
-constexpr char kGetUserEmailResponse[] = R"({"email": "user@test.com"})";
+constexpr char kTestScopes[] = "scope1 scope2";
 }  // namespace
 
 class HostStarterOAuthHelperTest : public testing::Test {
@@ -40,16 +47,17 @@
 
   void OnTokensRetrieved(const std::string& user_email,
                          const std::string& access_token,
-                         const std::string& refresh_token);
+                         const std::string& refresh_token,
+                         const std::string& scopes);
   void HandleOAuthError(const std::string& error_message,
                         HostStarter::Result error_result);
 
  protected:
   void RunUntilQuit();
   void AddGetTokenResponse(const std::string& response);
-  void AddGetUserEmailResponse(const std::string& response);
+  void AddGetTokenInfoResponse(const std::string& response);
   void AddGetTokenErrorResponse(net::HttpStatusCode status);
-  void AddGetUserEmailErrorResponse(net::HttpStatusCode status);
+  void AddGetTokenInfoErrorResponse(net::HttpStatusCode status);
 
   HostStarterOAuthHelper& host_starter_oauth_helper() {
     return *host_starter_oauth_helper_;
@@ -57,13 +65,15 @@
   std::string& access_token() { return access_token_; }
   std::string& refresh_token() { return refresh_token_; }
   std::string& user_email() { return user_email_; }
+  std::string& scopes() { return scopes_; }
   std::string& error_message() { return error_message_; }
   std::optional<HostStarter::Result>& error_result() { return error_result_; }
 
  private:
-  std::string user_email_;
   std::string access_token_;
   std::string refresh_token_;
+  std::string user_email_;
+  std::string scopes_;
   std::string error_message_;
   std::optional<HostStarter::Result> error_result_;
 
@@ -86,6 +96,7 @@
   access_token_.clear();
   refresh_token_.clear();
   user_email_.clear();
+  scopes_.clear();
   error_message_.clear();
   error_result_.reset();
 
@@ -98,10 +109,12 @@
 void HostStarterOAuthHelperTest::OnTokensRetrieved(
     const std::string& user_email,
     const std::string& access_token,
-    const std::string& refresh_token) {
-  user_email_ = user_email;
+    const std::string& refresh_token,
+    const std::string& scopes) {
   access_token_ = access_token;
   refresh_token_ = refresh_token;
+  user_email_ = user_email;
+  scopes_ = scopes;
   quit_closure_.Run();
 }
 
@@ -119,10 +132,10 @@
       GaiaUrls::GetInstance()->oauth2_token_url().spec(), response);
 }
 
-void HostStarterOAuthHelperTest::AddGetUserEmailResponse(
+void HostStarterOAuthHelperTest::AddGetTokenInfoResponse(
     const std::string& response) {
   test_url_loader_factory_.AddResponse(
-      GaiaUrls::GetInstance()->oauth_user_info_url().spec(), response);
+      GaiaUrls::GetInstance()->oauth2_token_info_url().spec(), response);
 }
 
 void HostStarterOAuthHelperTest::AddGetTokenErrorResponse(
@@ -132,10 +145,10 @@
       /*content=*/std::string(), status);
 }
 
-void HostStarterOAuthHelperTest::AddGetUserEmailErrorResponse(
+void HostStarterOAuthHelperTest::AddGetTokenInfoErrorResponse(
     net::HttpStatusCode status) {
   test_url_loader_factory_.AddResponse(
-      GaiaUrls::GetInstance()->oauth_user_info_url().spec(),
+      GaiaUrls::GetInstance()->oauth2_token_info_url().spec(),
       /*content=*/std::string(), status);
 }
 
@@ -145,7 +158,7 @@
 
 TEST_F(HostStarterOAuthHelperTest, NoUserEmail_TokensRetrievedSuccessfully) {
   AddGetTokenResponse(kGetTokensResponse);
-  AddGetUserEmailResponse(kGetUserEmailResponse);
+  AddGetTokenInfoResponse(kGetTokenInfoResponse);
 
   host_starter_oauth_helper().FetchTokens(
       std::string(), kAuthorizationCodeValue, gaia::OAuthClientInfo(),
@@ -159,12 +172,13 @@
   ASSERT_EQ(access_token(), kAccessTokenValue);
   ASSERT_EQ(refresh_token(), kRefreshTokenValue);
   ASSERT_EQ(user_email(), kTestEmail);
+  ASSERT_EQ(scopes(), kTestScopes);
   ASSERT_FALSE(error_result().has_value());
 }
 
 TEST_F(HostStarterOAuthHelperTest, UserEmail_TokensRetrievedSuccessfully) {
   AddGetTokenResponse(kGetTokensResponse);
-  AddGetUserEmailResponse(kGetUserEmailResponse);
+  AddGetTokenInfoResponse(kGetTokenInfoResponse);
 
   host_starter_oauth_helper().FetchTokens(
       kTestEmail, kAuthorizationCodeValue, gaia::OAuthClientInfo(),
@@ -178,12 +192,13 @@
   ASSERT_EQ(access_token(), kAccessTokenValue);
   ASSERT_EQ(refresh_token(), kRefreshTokenValue);
   ASSERT_EQ(user_email(), kTestEmail);
+  ASSERT_EQ(scopes(), kTestScopes);
   ASSERT_FALSE(error_result().has_value());
 }
 
 TEST_F(HostStarterOAuthHelperTest, DifferentUserEmail_RunsErrorCallback) {
   AddGetTokenResponse(kGetTokensResponse);
-  AddGetUserEmailResponse(kGetUserEmailResponse);
+  AddGetTokenInfoResponse(kGetTokenInfoResponse);
 
   host_starter_oauth_helper().FetchTokens(
       kDifferentTestEmail, kAuthorizationCodeValue, gaia::OAuthClientInfo(),
@@ -197,6 +212,7 @@
   ASSERT_TRUE(access_token().empty());
   ASSERT_TRUE(refresh_token().empty());
   ASSERT_TRUE(user_email().empty());
+  ASSERT_TRUE(scopes().empty());
   ASSERT_FALSE(error_message().empty());
   ASSERT_TRUE(error_result().has_value());
   ASSERT_EQ(*error_result(), HostStarter::Result::PERMISSION_DENIED);
@@ -217,6 +233,7 @@
   ASSERT_TRUE(access_token().empty());
   ASSERT_TRUE(refresh_token().empty());
   ASSERT_TRUE(user_email().empty());
+  ASSERT_TRUE(scopes().empty());
   ASSERT_FALSE(error_message().empty());
   ASSERT_TRUE(error_result().has_value());
   ASSERT_EQ(*error_result(), HostStarter::Result::NETWORK_ERROR);
@@ -239,6 +256,7 @@
   ASSERT_TRUE(access_token().empty());
   ASSERT_TRUE(refresh_token().empty());
   ASSERT_TRUE(user_email().empty());
+  ASSERT_TRUE(scopes().empty());
   ASSERT_FALSE(error_message().empty());
   ASSERT_TRUE(error_result().has_value());
   ASSERT_EQ(*error_result(), HostStarter::Result::OAUTH_ERROR);
@@ -246,7 +264,7 @@
 
 TEST_F(HostStarterOAuthHelperTest, GetUserEmailNetworkError_RunsErrorCallback) {
   AddGetTokenResponse(kGetTokensResponse);
-  AddGetUserEmailErrorResponse(net::HttpStatusCode::HTTP_SERVICE_UNAVAILABLE);
+  AddGetTokenInfoErrorResponse(net::HttpStatusCode::HTTP_SERVICE_UNAVAILABLE);
 
   host_starter_oauth_helper().FetchTokens(
       kTestEmail, kAuthorizationCodeValue, gaia::OAuthClientInfo(),
@@ -260,6 +278,7 @@
   ASSERT_TRUE(access_token().empty());
   ASSERT_TRUE(refresh_token().empty());
   ASSERT_TRUE(user_email().empty());
+  ASSERT_TRUE(scopes().empty());
   ASSERT_FALSE(error_message().empty());
   ASSERT_EQ(error_result(), HostStarter::Result::NETWORK_ERROR);
 }
diff --git a/remoting/internal b/remoting/internal
index 9d8de68..1cf2ed8 160000
--- a/remoting/internal
+++ b/remoting/internal
@@ -1 +1 @@
-Subproject commit 9d8de68788f20252dc2ece395077ac3b7ffdcfbe
+Subproject commit 1cf2ed8f6e38a924f1b2754a880d039516611493
diff --git a/remoting/protocol/chromium_socket_factory.cc b/remoting/protocol/chromium_socket_factory.cc
index 1a5a21b..b9ba1d7 100644
--- a/remoting/protocol/chromium_socket_factory.cc
+++ b/remoting/protocol/chromium_socket_factory.cc
@@ -12,7 +12,9 @@
 
 #include "base/functional/bind.h"
 #include "base/logging.h"
+#include "base/memory/weak_ptr.h"
 #include "base/rand_util.h"
+#include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "components/webrtc/net_address_utils.h"
 #include "net/base/io_buffer.h"
@@ -112,7 +114,8 @@
   void OnReadCompleted(int result);
   void HandleReadResult(int result);
 
-  std::unique_ptr<net::UDPServerSocket> socket_;
+  std::unique_ptr<net::UDPServerSocket> socket_
+      GUARDED_BY_CONTEXT(thread_checker_);
 
   State state_ = STATE_CLOSED;
   int error_ = 0;
@@ -123,9 +126,16 @@
   scoped_refptr<net::IOBuffer> receive_buffer_;
   net::IPEndPoint receive_address_;
 
-  bool send_pending_ = false;
-  std::list<PendingPacket> send_queue_;
-  int send_queue_size_ = 0;
+  bool send_pending_ GUARDED_BY_CONTEXT(thread_checker_) = false;
+  std::list<PendingPacket> send_queue_ GUARDED_BY_CONTEXT(thread_checker_);
+  int send_queue_size_ GUARDED_BY_CONTEXT(thread_checker_) = 0;
+
+  THREAD_CHECKER(thread_checker_);
+
+  // Cache a WeakPtr instance to prevent calling memory barrier functions for
+  // each send callback.
+  base::WeakPtr<UdpPacketSocket> weak_ptr_;
+  base::WeakPtrFactory<UdpPacketSocket> weak_factory_{this};
 };
 
 UdpPacketSocket::PendingPacket::PendingPacket(const void* buffer,
@@ -138,7 +148,9 @@
   memcpy(data->data(), buffer, buffer_size);
 }
 
-UdpPacketSocket::UdpPacketSocket() = default;
+UdpPacketSocket::UdpPacketSocket() {
+  weak_ptr_ = weak_factory_.GetWeakPtr();
+}
 
 UdpPacketSocket::~UdpPacketSocket() {
   Close();
@@ -147,6 +159,7 @@
 bool UdpPacketSocket::Init(const rtc::SocketAddress& local_address,
                            uint16_t min_port,
                            uint16_t max_port) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK_LE(min_port, max_port);
   net::IPEndPoint local_endpoint;
   if (!webrtc::SocketAddressToIPEndPoint(local_address, &local_endpoint)) {
@@ -213,6 +226,8 @@
                             size_t data_size,
                             const rtc::SocketAddress& address,
                             const rtc::PacketOptions& options) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   if (state_ != STATE_BOUND) {
     NOTREACHED_IN_MIGRATION();
     return EINVAL;
@@ -231,8 +246,7 @@
     return EWOULDBLOCK;
   }
 
-  PendingPacket packet(data, data_size, endpoint, options);
-  send_queue_.push_back(packet);
+  send_queue_.emplace_back(data, data_size, endpoint, options);
   send_queue_size_ += data_size;
 
   DoSend();
@@ -240,8 +254,11 @@
 }
 
 int UdpPacketSocket::Close() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   state_ = STATE_CLOSED;
   socket_.reset();
+  weak_ptr_.reset();
   return 0;
 }
 
@@ -256,6 +273,8 @@
 }
 
 int UdpPacketSocket::SetOption(rtc::Socket::Option option, int value) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   if (state_ != STATE_BOUND) {
     NOTREACHED_IN_MIGRATION();
     return EINVAL;
@@ -311,6 +330,8 @@
 }
 
 void UdpPacketSocket::DoSend() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   // SendTo() usually completes synchronously however if the socket is not able
   // to send, it will return ERR_IO_PENDING. In that case, we break out of the
   // send loop to allow it time to finish sending packets. Once the socket is
@@ -322,10 +343,9 @@
         packet.data->bytes(), packet.data->size(),
         packet.options.packet_time_params,
         (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds());
-    int result =
-        socket_->SendTo(packet.data.get(), packet.data->size(), packet.address,
-                        base::BindOnce(&UdpPacketSocket::OnSendCompleted,
-                                       base::Unretained(this)));
+    int result = socket_->SendTo(
+        packet.data.get(), packet.data->size(), packet.address,
+        base::BindOnce(&UdpPacketSocket::OnSendCompleted, weak_ptr_));
     if (result != net::ERR_IO_PENDING) {
       OnSendCompleted(result);
     } else {
@@ -335,6 +355,8 @@
 }
 
 void UdpPacketSocket::OnSendCompleted(int result) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   // If |send_pending_| is true, that means OnSendCompleted was run via the
   // callback we provide to the socket because it is able to process send
   // packets again. In that case, we want to call DoSend() so that any packets
@@ -377,19 +399,22 @@
 }
 
 void UdpPacketSocket::DoRead() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   int result = 0;
   while (result >= 0) {
     receive_buffer_ =
         base::MakeRefCounted<net::IOBufferWithSize>(kReceiveBufferSize);
-    result = socket_->RecvFrom(receive_buffer_.get(), kReceiveBufferSize,
-                               &receive_address_,
-                               base::BindOnce(&UdpPacketSocket::OnReadCompleted,
-                                              base::Unretained(this)));
+    result = socket_->RecvFrom(
+        receive_buffer_.get(), kReceiveBufferSize, &receive_address_,
+        base::BindOnce(&UdpPacketSocket::OnReadCompleted, weak_ptr_));
     HandleReadResult(result);
   }
 }
 
 void UdpPacketSocket::OnReadCompleted(int result) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   HandleReadResult(result);
   if (result >= 0) {
     DoRead();
diff --git a/services/device/power_monitor/power_monitor_message_broadcaster.cc b/services/device/power_monitor/power_monitor_message_broadcaster.cc
index 36e983c9..75cd4c7f 100644
--- a/services/device/power_monitor/power_monitor_message_broadcaster.cc
+++ b/services/device/power_monitor/power_monitor_message_broadcaster.cc
@@ -10,13 +10,15 @@
 namespace device {
 
 PowerMonitorMessageBroadcaster::PowerMonitorMessageBroadcaster() {
-  base::PowerMonitor::AddPowerSuspendObserver(this);
-  base::PowerMonitor::AddPowerStateObserver(this);
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  power_monitor->AddPowerSuspendObserver(this);
+  power_monitor->AddPowerStateObserver(this);
 }
 
 PowerMonitorMessageBroadcaster::~PowerMonitorMessageBroadcaster() {
-  base::PowerMonitor::RemovePowerSuspendObserver(this);
-  base::PowerMonitor::RemovePowerStateObserver(this);
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  power_monitor->RemovePowerSuspendObserver(this);
+  power_monitor->RemovePowerStateObserver(this);
 }
 
 // static
@@ -30,11 +32,13 @@
         power_monitor_client) {
   mojo::RemoteSetElementId element_id =
       clients_.Add(std::move(power_monitor_client));
+  auto* power_monitor = base::PowerMonitor::GetInstance();
 
-  if (!base::PowerMonitor::IsInitialized())
+  if (!power_monitor->IsInitialized()) {
     return;
+  }
 
-  bool on_battery_power = base::PowerMonitor::IsOnBatteryPower();
+  bool on_battery_power = power_monitor->IsOnBatteryPower();
 
   // If the state has changed since we last checked, update all clients.
   if (on_battery_power != on_battery_power_) {
diff --git a/services/device/power_monitor/power_monitor_message_broadcaster_unittest.cc b/services/device/power_monitor/power_monitor_message_broadcaster_unittest.cc
index 6968429..6f784ae 100644
--- a/services/device/power_monitor/power_monitor_message_broadcaster_unittest.cc
+++ b/services/device/power_monitor/power_monitor_message_broadcaster_unittest.cc
@@ -62,7 +62,9 @@
     power_monitor_source_.SetOnBatteryPower(on_battery_power);
   }
 
-  bool IsOnBatteryPower() { return power_monitor_source_.IsOnBatteryPower(); }
+  bool IsOnBatteryPower() const {
+    return power_monitor_source_.IsOnBatteryPower();
+  }
 
   void TearDown() override {
     DestroyDeviceService();
diff --git a/services/device/public/cpp/power_monitor/power_monitor_broadcast_source.cc b/services/device/public/cpp/power_monitor/power_monitor_broadcast_source.cc
index 98a7874..281389b 100644
--- a/services/device/public/cpp/power_monitor/power_monitor_broadcast_source.cc
+++ b/services/device/public/cpp/power_monitor/power_monitor_broadcast_source.cc
@@ -35,7 +35,7 @@
   }
 }
 
-bool PowerMonitorBroadcastSource::IsOnBatteryPower() {
+bool PowerMonitorBroadcastSource::IsOnBatteryPower() const {
   return client_->last_reported_on_battery_power_state();
 }
 
diff --git a/services/device/public/cpp/power_monitor/power_monitor_broadcast_source.h b/services/device/public/cpp/power_monitor/power_monitor_broadcast_source.h
index 3cb3a4e..f2400ef 100644
--- a/services/device/public/cpp/power_monitor/power_monitor_broadcast_source.h
+++ b/services/device/public/cpp/power_monitor/power_monitor_broadcast_source.h
@@ -85,7 +85,7 @@
 
   Client* client_for_testing() const { return client_.get(); }
 
-  bool IsOnBatteryPower() override;
+  bool IsOnBatteryPower() const override;
 
   std::unique_ptr<Client> client_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/services/device/public/cpp/power_monitor/power_monitor_broadcast_source_unittest.cc b/services/device/public/cpp/power_monitor/power_monitor_broadcast_source_unittest.cc
index 5a61c3e8..e6b4a3d6 100644
--- a/services/device/public/cpp/power_monitor/power_monitor_broadcast_source_unittest.cc
+++ b/services/device/public/cpp/power_monitor/power_monitor_broadcast_source_unittest.cc
@@ -29,12 +29,13 @@
     auto power_monitor_source = std::make_unique<PowerMonitorBroadcastSource>(
         base::SequencedTaskRunner::GetCurrentDefault());
     power_monitor_source_ptr_ = power_monitor_source.get();
-    base::PowerMonitor::Initialize(std::move(power_monitor_source));
+    base::PowerMonitor::GetInstance()->Initialize(
+        std::move(power_monitor_source));
     power_monitor_source_ptr_->Init(mojo::NullRemote());
   }
 
   void TearDown() override {
-    base::PowerMonitor::ShutdownForTesting();
+    base::PowerMonitor::GetInstance()->ShutdownForTesting();
     base::RunLoop().RunUntilIdle();
   }
 
@@ -51,8 +52,9 @@
 
 TEST_F(PowerMonitorBroadcastSourceTest, PowerMessageReceiveBroadcast) {
   base::test::PowerMonitorTestObserver observer;
-  base::PowerMonitor::AddPowerSuspendObserver(&observer);
-  base::PowerMonitor::AddPowerStateObserver(&observer);
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  power_monitor->AddPowerSuspendObserver(&observer);
+  power_monitor->AddPowerStateObserver(&observer);
 
   // Sending resume when not suspended should have no effect.
   client()->Resume();
@@ -103,8 +105,8 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(observer.power_state_changes(), 2);
 
-  base::PowerMonitor::RemovePowerSuspendObserver(&observer);
-  base::PowerMonitor::RemovePowerStateObserver(&observer);
+  power_monitor->RemovePowerSuspendObserver(&observer);
+  power_monitor->RemovePowerStateObserver(&observer);
 }
 
 }  // namespace device
diff --git a/services/media_session/audio_focus_manager.cc b/services/media_session/audio_focus_manager.cc
index 1da0aba..c277cfd 100644
--- a/services/media_session/audio_focus_manager.cc
+++ b/services/media_session/audio_focus_manager.cc
@@ -39,14 +39,14 @@
  public:
   explicit MediaPowerDelegate(base::WeakPtr<AudioFocusManager> owner)
       : owner_(owner) {
-    base::PowerMonitor::AddPowerSuspendObserver(this);
+    base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
   }
 
   MediaPowerDelegate(const MediaPowerDelegate&) = delete;
   MediaPowerDelegate& operator=(const MediaPowerDelegate&) = delete;
 
   ~MediaPowerDelegate() override {
-    base::PowerMonitor::RemovePowerSuspendObserver(this);
+    base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
   }
 
   // base::PowerSuspendObserver:
diff --git a/services/video_capture/video_capture_service_impl.cc b/services/video_capture/video_capture_service_impl.cc
index 0e9a60d..dadfad66 100644
--- a/services/video_capture/video_capture_service_impl.cc
+++ b/services/video_capture/video_capture_service_impl.cc
@@ -133,9 +133,8 @@
       media::VideoCaptureGpuChannelHost::GetInstance().SetSharedImageInterface(
           viz_gpu_->GetGpuChannel()->CreateClientSharedImageInterface());
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-      media::VideoCaptureDeviceFactoryChromeOS::SetGpuBufferManager(
-          media::VideoCaptureGpuChannelHost::GetInstance()
-              .GetGpuMemoryBufferManager());
+      media::VideoCaptureDeviceFactoryChromeOS::SetGpuChannelHost(
+          viz_gpu_->GetGpuChannel());
       media::VideoCaptureDeviceFactoryChromeOS::SetSharedImageInterface(
           viz_gpu_->GetGpuChannel()->CreateClientSharedImageInterface());
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -151,7 +150,7 @@
       media::VideoCaptureGpuChannelHost::GetInstance().SetSharedImageInterface(
           nullptr);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-      media::VideoCaptureDeviceFactoryChromeOS::SetGpuBufferManager(nullptr);
+      media::VideoCaptureDeviceFactoryChromeOS::SetGpuChannelHost(nullptr);
       media::VideoCaptureDeviceFactoryChromeOS::SetSharedImageInterface(
           nullptr);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -172,9 +171,12 @@
       media::VideoCaptureGpuChannelHost::GetInstance().SetSharedImageInterface(
           nullptr);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-      media::VideoCaptureDeviceFactoryChromeOS::SetGpuBufferManager(nullptr);
+      media::VideoCaptureDeviceFactoryChromeOS::SetGpuChannelHost(nullptr);
       media::VideoCaptureDeviceFactoryChromeOS::SetSharedImageInterface(
           nullptr);
+    } else {
+      media::VideoCaptureDeviceFactoryChromeOS::SetGpuChannelHost(
+          viz_gpu_->GetGpuChannel());
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
     }
 
diff --git a/services/viz/public/cpp/gpu/context_provider_command_buffer.cc b/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
index 215d964..0efca99 100644
--- a/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
+++ b/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
@@ -49,7 +49,7 @@
 #include "services/viz/public/cpp/gpu/command_buffer_metrics.h"
 #include "skia/buildflags.h"
 #include "third_party/skia/include/core/SkTraceMemoryDump.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "ui/gl/trace_util.h"
 
 class SkDiscardableMemory;
diff --git a/services/webnn/coreml/graph_builder_coreml.cc b/services/webnn/coreml/graph_builder_coreml.cc
index 264a3e7..a9ba71e 100644
--- a/services/webnn/coreml/graph_builder_coreml.cc
+++ b/services/webnn/coreml/graph_builder_coreml.cc
@@ -635,6 +635,7 @@
        /*arg_min_max_input=*/kFloatsAndInt32,
        /*arg_min_max_output=*/
        kArgMinMaxOutputSupportedDataTypes,
+       /*batch_normalization_input=*/DataTypeConstraint::kFloat16To32,
        // Note that BOOL, INT16, and UINT16 is also supported by CoreML, but
        // WebNN does not have corresponding types.
        // https://apple.github.io/coremltools/source/coremltools.converters.mil.mil.ops.defs.html#coremltools.converters.mil.mil.ops.defs.iOS17.elementwise_unary.cast
@@ -643,6 +644,8 @@
        // https://apple.github.io/coremltools/source/coremltools.converters.mil.mil.ops.defs.html#coremltools.converters.mil.mil.ops.defs.iOS15.elementwise_unary.clip
        /*clamp_input=*/DataTypeConstraint::kFloat16To32,
        /*concat_inputs=*/kFloatsAndInt32,
+       /*conv2d_input=*/DataTypeConstraint::kFloat16To32,
+       /*conv_transpose2d_input=*/DataTypeConstraint::kFloat16To32,
        /*add_input=*/kFloatsAndInt32,
        /*sub_input=*/kFloatsAndInt32,
        /*mul_input=*/kFloatsAndInt32,
@@ -685,12 +688,22 @@
        /*gather_elements_indices=*/{},
        /*gelu_input=*/DataTypeConstraint::kFloat16To32,
        /*gemm_input=*/DataTypeConstraint::kFloat16To32,
+       // Gru is not implemented.
+       /*gru_input=*/{},
+       // GruCell is not implemented.
+       /*gru_cell_input=*/{},
        /*hard_sigmoid_input=*/DataTypeConstraint::kFloat16To32,
        /*hard_swish_input=*/DataTypeConstraint::kFloat16To32,
+       /*instance_normalization_input=*/DataTypeConstraint::kFloat16To32,
+       /*layer_normalization_input=*/DataTypeConstraint::kFloat16To32,
        /*leaky_relu_input=*/DataTypeConstraint::kFloat16To32,
        // TODO: crbug.com/338667172 - Consider enhancing the data type support
        // to include int32.
        /*linear_input=*/DataTypeConstraint::kFloat16To32,
+       // Lstm is not implemented.
+       /*lstm_input=*/{},
+       // LstmCell is not implemented.
+       /*lstm_cell_input=*/{},
        /*matmul_input=*/kFloatsAndInt32,
        /*pad_input=*/DataTypeConstraint::kFloat16To32,
        /*average_pool2d_input=*/DataTypeConstraint::kFloat16To32,
@@ -1354,7 +1367,8 @@
     CoreML::Specification::MILSpec::Block& block) {
   const OperandInfo& input_operand_info =
       GetOperandInfo(operation.input_operand_id);
-  CHECK(kFloatDataTypes.contains(input_operand_info.mil_data_type));
+  CHECK(context_properties_.data_type_limits.batch_normalization_input.Has(
+      MILDataTypeToOperandType(input_operand_info.mil_data_type)));
 
   // TODO(crbug.com/338529225): Support ND inputs.
   if (input_operand_info.dimensions.size() < 3 ||
@@ -1503,8 +1517,6 @@
     CoreML::Specification::MILSpec::Block& block) {
   const OperandInfo& input_operand = GetOperandInfo(operation.input_operand_id);
 
-  CHECK(kFloatDataTypes.contains(input_operand.mil_data_type));
-
   static constexpr char kParamWeight[] = "weight";
   static constexpr char kParamStrides[] = "strides";
   static constexpr char kParamPadType[] = "pad_type";
@@ -1517,9 +1529,13 @@
   CoreML::Specification::MILSpec::Operation* op = block.add_operations();
   switch (operation.kind) {
     case mojom::Conv2d::Kind::kDirect:
+      CHECK(context_properties_.data_type_limits.conv2d_input.Has(
+          MILDataTypeToOperandType(input_operand.mil_data_type)));
       op->set_type(kOpConv2dTypeName);
       break;
     case mojom::Conv2d::Kind::kTransposed:
+      CHECK(context_properties_.data_type_limits.conv_transpose2d_input.Has(
+          MILDataTypeToOperandType(input_operand.mil_data_type)));
       op->set_type(kOpConvTranspose2dTypeName);
       break;
   }
@@ -2111,7 +2127,8 @@
     CoreML::Specification::MILSpec::Block& block) {
   const OperandInfo& input_operand_info =
       GetOperandInfo(operation.input_operand_id);
-  CHECK(kFloatDataTypes.contains(input_operand_info.mil_data_type));
+  CHECK(context_properties_.data_type_limits.instance_normalization_input.Has(
+      MILDataTypeToOperandType(input_operand_info.mil_data_type)));
 
   if (operation.layout != mojom::InputOperandLayout::kChannelsFirst) {
     // TODO(crbug.com/338398666) Support channels-last by adding transposes.
@@ -2148,7 +2165,8 @@
     CoreML::Specification::MILSpec::Block& block) {
   const OperandInfo& input_operand_info =
       GetOperandInfo(operation.input_operand_id);
-  CHECK(kFloatDataTypes.contains(input_operand_info.mil_data_type));
+  CHECK(context_properties_.data_type_limits.layer_normalization_input.Has(
+      MILDataTypeToOperandType(input_operand_info.mil_data_type)));
 
   // TODO: crbug.com/356905058: Figure out if unordered axes should be allowed.
   if (!base::ranges::is_sorted(operation.axes)) {
diff --git a/services/webnn/dml/context_impl_dml.cc b/services/webnn/dml/context_impl_dml.cc
index 45a9309..2fa258fa 100644
--- a/services/webnn/dml/context_impl_dml.cc
+++ b/services/webnn/dml/context_impl_dml.cc
@@ -88,6 +88,9 @@
        /*arg_min_max_input=*/SupportedDataTypes::All(),
        /*arg_min_max_output=*/DataTypeConstraint::kInt32To64,
 
+       // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_batch_normalization_operator_desc#tensor-support
+       /*batch_normalization_input=*/DataTypeConstraint::kFloat16To32,
+
        // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_cast_operator_desc#tensor-support
        /*cast_input=*/SupportedDataTypes::All(),
 
@@ -97,6 +100,10 @@
        // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_join_operator_desc#tensor-support
        /*concat_inputs=*/kFloat16To32Ints8To32,
 
+       // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_convolution_operator_desc#tensor-support
+       /*conv2d_input=*/DataTypeConstraint::kFloat16To32,
+       /*conv_transpose2d_input=*/DataTypeConstraint::kFloat16To32,
+
        // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_element_wise_add_operator_desc#tensor-support
        /*add_input=*/kFloat16To32Ints32,
 
@@ -205,6 +212,10 @@
        // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_gemm_operator_desc#tensor-support
        /*gemm_input=*/DataTypeConstraint::kFloat16To32,
 
+       // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_gru_operator_desc#tensor-support
+       /*gru_input=*/DataTypeConstraint::kFloat16To32,
+       /*gru_cell_input=*/DataTypeConstraint::kFloat16To32,
+
        // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_activation_hard_sigmoid_operator_desc
        /*hard_sigmoid_input=*/DataTypeConstraint::kFloat16To32,
 
@@ -212,12 +223,20 @@
        // https://learn.microsoft.com/en-us/windows/ai/directml/api/ns-directml-dml_activation_hard_swish_operator_desc
        /*hard_swish_input=*/DataTypeConstraint::kFloat16To32,
 
+       // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_mean_variance_normalization1_operator_desc#tensor-support
+       /*instance_normalization_input=*/DataTypeConstraint::kFloat16To32,
+       /*layer_normalization_input=*/DataTypeConstraint::kFloat16To32,
+
        // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_activation_leaky_relu_operator_desc
        /*leaky_relu_input=*/DataTypeConstraint::kFloat16To32,
 
        // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_activation_linear_operator_desc#tensor-support
        /*linear_input=*/DataTypeConstraint::kFloat16To32,
 
+       // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_lstm_operator_desc#tensor-support
+       /*lstm_input=*/DataTypeConstraint::kFloat16To32,
+       /*lstm_cell_input=*/DataTypeConstraint::kFloat16To32,
+
        // Matmul is emulated by gemm.
        /*matmul_input=*/DataTypeConstraint::kFloat16To32,
 
diff --git a/services/webnn/dml/graph_impl_dml.cc b/services/webnn/dml/graph_impl_dml.cc
index 297f028e..dd06234 100644
--- a/services/webnn/dml/graph_impl_dml.cc
+++ b/services/webnn/dml/graph_impl_dml.cc
@@ -1334,6 +1334,7 @@
 }
 
 void CreateOperatorNodeForBatchNormalization(
+    const ContextProperties& context_properties,
     const Operation* operation,
     const std::map<const Operation*, const Operation*>&
         operation_to_fusible_standalone_activation_map,
@@ -1352,6 +1353,9 @@
   uint64_t output_id = batch_normalization->output_operand_id;
   const OperandPtr& output_operand = id_to_operand_map.at(output_id);
   OperandDataType data_type = output_operand->descriptor.data_type();
+  CHECK(context_properties.data_type_limits.batch_normalization_input.Has(
+      data_type));
+
   const TensorDesc output_tensor_desc(GetTensorDataType(data_type),
                                       output_operand->descriptor.shape());
 
@@ -1566,6 +1570,7 @@
 }
 
 void CreateOperatorNodeForConv2d(
+    const ContextProperties& context_properties,
     const IdToOperandMap& id_to_operand_map,
     const Operation* operation,
     const std::map<const Operation*, const Operation*>&
@@ -1578,7 +1583,26 @@
   // The input tensor description may be transposed.
   auto input_tensor_desc = input->GetTensorDesc();
   CHECK_EQ(input_tensor_desc.GetDimensions().size(), 4u);
-  CHECK(kDmlFloatDataTypes.contains(input_tensor_desc.GetDataType()));
+
+  const OperandPtr& input_operand =
+      id_to_operand_map.at(conv2d->input_operand_id);
+  OperandDataType data_type = input_operand->descriptor.data_type();
+  DML_CONVOLUTION_DIRECTION conv2d_direction;
+  switch (conv2d->kind) {
+    case mojom::Conv2d::Kind::kDirect: {
+      CHECK(context_properties.data_type_limits.conv2d_input.Has(data_type));
+      conv2d_direction =
+          DML_CONVOLUTION_DIRECTION::DML_CONVOLUTION_DIRECTION_FORWARD;
+      break;
+    }
+    case mojom::Conv2d::Kind::kTransposed: {
+      CHECK(context_properties.data_type_limits.conv_transpose2d_input.Has(
+          data_type));
+      conv2d_direction =
+          DML_CONVOLUTION_DIRECTION::DML_CONVOLUTION_DIRECTION_BACKWARD;
+      break;
+    }
+  }
 
   const NodeOutput* filter =
       GetNodeOutputForOperand(id_to_node_output_map, conv2d->filter_operand_id);
@@ -1650,18 +1674,6 @@
     activation_dml_desc = activation_operator_desc->GetActivationDmlDesc();
   }
 
-  DML_CONVOLUTION_DIRECTION conv2d_direction;
-  switch (conv2d->kind) {
-    case mojom::Conv2d::Kind::kDirect:
-      conv2d_direction =
-          DML_CONVOLUTION_DIRECTION::DML_CONVOLUTION_DIRECTION_FORWARD;
-      break;
-    case mojom::Conv2d::Kind::kTransposed:
-      conv2d_direction =
-          DML_CONVOLUTION_DIRECTION::DML_CONVOLUTION_DIRECTION_BACKWARD;
-      break;
-  }
-
   DML_CONVOLUTION_OPERATOR_DESC conv2d_operator_desc{
       .InputTensor = &input_tensor_desc.GetDMLTensorDesc(),
       .FilterTensor = &filter_tensor_desc.GetDMLTensorDesc(),
@@ -3064,7 +3076,10 @@
 
 // `GruType` must be `mojom::GruPtr` or `mojom::GruCellPtr`.
 template <typename GruType>
+  requires(std::is_same_v<GruType, mojom::GruPtr> ||
+           std::is_same_v<GruType, mojom::GruCellPtr>)
 base::expected<void, mojom::ErrorPtr> CreateOperatorNodeForGru(
+    const ContextProperties& context_properties,
     const IdToOperandMap& id_to_operand_map,
     const GruType& gru,
     mojom::GraphInfoPtr& graph_info,
@@ -3072,25 +3087,6 @@
     IdToNodeOutputMap& id_to_node_output_map,
     std::unordered_map<uint64_t, uint32_t>& constant_id_to_input_index_map,
     uint64_t& next_operand_id) {
-  static_assert(std::is_same<GruType, mojom::GruPtr>::value ||
-                std::is_same<GruType, mojom::GruCellPtr>::value);
-
-  mojom::Operation::Tag op_tag;
-  std::optional<uint64_t> initial_hidden_state_operand_id;
-  bool return_sequence;
-  mojom::RecurrentNetworkDirection direction;
-  if constexpr (std::is_same<GruType, mojom::GruPtr>::value) {
-    op_tag = mojom::Operation::Tag::kGru;
-    initial_hidden_state_operand_id = gru->initial_hidden_state_operand_id;
-    return_sequence = gru->return_sequence;
-    direction = gru->direction;
-  } else /* GruType is mojom::GruCell */ {
-    op_tag = mojom::Operation::Tag::kGruCell;
-    initial_hidden_state_operand_id = gru->hidden_state_operand_id;
-    return_sequence = false;
-    direction = mojom::RecurrentNetworkDirection::kForward;
-  }
-
   const NodeOutput* input =
       GetNodeOutputForOperand(id_to_node_output_map, gru->input_operand_id);
   // Since the InputTensor doesn't support the DML_TENSOR_FLAG_OWNED_BY_DML
@@ -3098,6 +3094,28 @@
   // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_gru_operator_desc
   input = AppendIdentityToConstantOperand(graph_builder, input);
   TensorDesc input_tensor_desc = input->GetTensorDesc();
+  const OperandDataType input_data_type =
+      DmlDataTypeToOperand(input_tensor_desc.GetDataType());
+
+  mojom::Operation::Tag op_tag;
+  std::optional<uint64_t> initial_hidden_state_operand_id;
+  bool return_sequence;
+  mojom::RecurrentNetworkDirection direction;
+  if constexpr (std::is_same_v<GruType, mojom::GruPtr>) {
+    CHECK(context_properties.data_type_limits.gru_input.Has(input_data_type));
+    op_tag = mojom::Operation::Tag::kGru;
+    initial_hidden_state_operand_id = gru->initial_hidden_state_operand_id;
+    return_sequence = gru->return_sequence;
+    direction = gru->direction;
+  } else /* GruType is mojom::GruCellPtr */ {
+    CHECK(context_properties.data_type_limits.gru_cell_input.Has(
+        input_data_type));
+    op_tag = mojom::Operation::Tag::kGruCell;
+    initial_hidden_state_operand_id = gru->hidden_state_operand_id;
+    return_sequence = false;
+    direction = mojom::RecurrentNetworkDirection::kForward;
+  }
+
   // The input tensor is 4-D for gru and 3-D for gruCell, while DirectML expects
   // a 4-D tensor.
   input_tensor_desc.EnsureMinimumRank(/*rank=*/4,
@@ -3442,8 +3460,11 @@
 }
 
 template <typename NormalizationPtr>
+  requires(std::is_same_v<NormalizationPtr, mojom::InstanceNormalizationPtr> ||
+           std::is_same_v<NormalizationPtr, mojom::LayerNormalizationPtr>)
 base::expected<void, mojom::ErrorPtr>
 CreateOperatorNodeForMeanVarianceNormalization(
+    const ContextProperties& context_properties,
     const NormalizationPtr& normalization,
     const Operation* operation,
     const std::map<const Operation*, const Operation*>&
@@ -3465,6 +3486,16 @@
   uint64_t output_id = normalization->output_operand_id;
   const OperandPtr& output_operand = id_to_operand_map.at(output_id);
   OperandDataType data_type = output_operand->descriptor.data_type();
+
+  if constexpr (std::is_same_v<NormalizationPtr,
+                               mojom::InstanceNormalizationPtr>) {
+    CHECK(context_properties.data_type_limits.instance_normalization_input.Has(
+        data_type));
+  } else /* `NormalizationPtr` is `mojom::LayerNormalizationPtr` */ {
+    CHECK(context_properties.data_type_limits.layer_normalization_input.Has(
+        data_type));
+  }
+
   const TensorDesc output_tensor_desc(GetTensorDataType(data_type),
                                       output_operand->descriptor.shape());
 
@@ -3650,16 +3681,16 @@
 
 // `LstmType` must be `mojom::Lstm` or `mojom::LstmCell`.
 template <typename LstmType>
+  requires(std::is_same_v<LstmType, mojom::Lstm> ||
+           std::is_same_v<LstmType, mojom::LstmCell>)
 base::expected<void, mojom::ErrorPtr> CreateOperatorNodeForLstm(
+    const ContextProperties& context_properties,
     const LstmType& lstm,
     mojom::GraphInfoPtr& graph_info,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map,
     std::unordered_map<uint64_t, uint32_t>& constant_id_to_input_index_map,
     uint64_t& next_operand_id) {
-  static_assert(std::is_same<LstmType, mojom::Lstm>::value ||
-                std::is_same<LstmType, mojom::LstmCell>::value);
-
   const std::string& label = lstm.label;
   // TODO(crbug.com/329702350): Support the ifgo layout.
   if (lstm.layout == mojom::LstmWeightLayout::kIfgo) {
@@ -3668,25 +3699,6 @@
         "The lstm weight layout (ifgo) is not supported.", label);
   }
 
-  mojom::Operation::Tag op_tag;
-  std::optional<uint64_t> initial_hidden_state_operand_id;
-  std::optional<uint64_t> initial_cell_state_operand_id;
-  bool return_sequence;
-  mojom::RecurrentNetworkDirection direction;
-  if constexpr (std::is_same<LstmType, mojom::Lstm>::value) {
-    op_tag = mojom::Operation::Tag::kLstm;
-    initial_hidden_state_operand_id = lstm.initial_hidden_state_operand_id;
-    initial_cell_state_operand_id = lstm.initial_cell_state_operand_id;
-    return_sequence = lstm.return_sequence;
-    direction = lstm.direction;
-  } else /* `LstmType` is `mojom::LstmCell` */ {
-    op_tag = mojom::Operation::Tag::kLstmCell;
-    initial_hidden_state_operand_id = lstm.hidden_state_operand_id;
-    initial_cell_state_operand_id = lstm.cell_state_operand_id;
-    return_sequence = false;
-    direction = mojom::RecurrentNetworkDirection::kForward;
-  }
-
   const NodeOutput* input =
       GetNodeOutputForOperand(id_to_node_output_map, lstm.input_operand_id);
   // Append an identity node if the input is a constant operand since
@@ -3694,6 +3706,31 @@
   // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_lstm_operator_desc
   input = AppendIdentityToConstantOperand(graph_builder, input);
   TensorDesc input_tensor_desc = input->GetTensorDesc();
+  const OperandDataType input_data_type =
+      DmlDataTypeToOperand(input_tensor_desc.GetDataType());
+
+  mojom::Operation::Tag op_tag;
+  std::optional<uint64_t> initial_hidden_state_operand_id;
+  std::optional<uint64_t> initial_cell_state_operand_id;
+  bool return_sequence;
+  mojom::RecurrentNetworkDirection direction;
+  if constexpr (std::is_same_v<LstmType, mojom::Lstm>) {
+    CHECK(context_properties.data_type_limits.lstm_input.Has(input_data_type));
+    op_tag = mojom::Operation::Tag::kLstm;
+    initial_hidden_state_operand_id = lstm.initial_hidden_state_operand_id;
+    initial_cell_state_operand_id = lstm.initial_cell_state_operand_id;
+    return_sequence = lstm.return_sequence;
+    direction = lstm.direction;
+  } else /* `LstmType` is `mojom::LstmCell` */ {
+    CHECK(context_properties.data_type_limits.lstm_cell_input.Has(
+        input_data_type));
+    op_tag = mojom::Operation::Tag::kLstmCell;
+    initial_hidden_state_operand_id = lstm.hidden_state_operand_id;
+    initial_cell_state_operand_id = lstm.cell_state_operand_id;
+    return_sequence = false;
+    direction = mojom::RecurrentNetworkDirection::kForward;
+  }
+
   // The input tensor is 2-D for lstmCell and 3-D for lstm, while DirectML
   // expects a 4-D tensor.
   input_tensor_desc.EnsureMinimumRank(/*rank=*/4,
@@ -5626,7 +5663,7 @@
       }
       case mojom::Operation::Tag::kBatchNormalization: {
         CreateOperatorNodeForBatchNormalization(
-            operation.get(),
+            context_properties, operation.get(),
             graph_fusion_info.operation_to_fusible_standalone_activation_map,
             graph_info, graph_builder, id_to_node_output_map,
             constant_id_to_input_index_map, next_operand_id);
@@ -5645,7 +5682,7 @@
       }
       case Operation::Tag::kConv2d: {
         CreateOperatorNodeForConv2d(
-            id_to_operand_map, operation.get(),
+            context_properties, id_to_operand_map, operation.get(),
             graph_fusion_info.operation_to_fusible_standalone_activation_map,
             graph_builder, id_to_node_output_map);
         break;
@@ -5704,15 +5741,15 @@
       }
       case mojom::Operation::Tag::kGru: {
         create_operator_result = CreateOperatorNodeForGru<mojom::GruPtr>(
-            id_to_operand_map, operation->get_gru(), graph_info, graph_builder,
-            id_to_node_output_map, constant_id_to_input_index_map,
-            next_operand_id);
+            context_properties, id_to_operand_map, operation->get_gru(),
+            graph_info, graph_builder, id_to_node_output_map,
+            constant_id_to_input_index_map, next_operand_id);
         break;
       }
       case mojom::Operation::Tag::kGruCell: {
         create_operator_result = CreateOperatorNodeForGru<mojom::GruCellPtr>(
-            id_to_operand_map, operation->get_gru_cell(), graph_info,
-            graph_builder, id_to_node_output_map,
+            context_properties, id_to_operand_map, operation->get_gru_cell(),
+            graph_info, graph_builder, id_to_node_output_map,
             constant_id_to_input_index_map, next_operand_id);
         break;
       }
@@ -5746,7 +5783,7 @@
             break;
         }
         create_operator_result = CreateOperatorNodeForMeanVarianceNormalization(
-            instance_normalization, operation.get(),
+            context_properties, instance_normalization, operation.get(),
             graph_fusion_info.operation_to_fusible_standalone_activation_map,
             graph_info, graph_builder, id_to_node_output_map,
             constant_id_to_input_index_map, next_operand_id, mean_variance_axes,
@@ -5757,7 +5794,7 @@
         const auto& layer_normalization = operation->get_layer_normalization();
         const auto axes = layer_normalization->axes;
         create_operator_result = CreateOperatorNodeForMeanVarianceNormalization(
-            layer_normalization, operation.get(),
+            context_properties, layer_normalization, operation.get(),
             graph_fusion_info.operation_to_fusible_standalone_activation_map,
             graph_info, graph_builder, id_to_node_output_map,
             constant_id_to_input_index_map, next_operand_id, axes, axes,
@@ -5778,16 +5815,16 @@
       }
       case Operation::Tag::kLstm: {
         create_operator_result = CreateOperatorNodeForLstm<mojom::Lstm>(
-            *operation->get_lstm(), graph_info, graph_builder,
-            id_to_node_output_map, constant_id_to_input_index_map,
-            next_operand_id);
+            context_properties, *operation->get_lstm(), graph_info,
+            graph_builder, id_to_node_output_map,
+            constant_id_to_input_index_map, next_operand_id);
         break;
       }
       case Operation::Tag::kLstmCell: {
         create_operator_result = CreateOperatorNodeForLstm<mojom::LstmCell>(
-            *operation->get_lstm_cell(), graph_info, graph_builder,
-            id_to_node_output_map, constant_id_to_input_index_map,
-            next_operand_id);
+            context_properties, *operation->get_lstm_cell(), graph_info,
+            graph_builder, id_to_node_output_map,
+            constant_id_to_input_index_map, next_operand_id);
         break;
       }
       case mojom::Operation::Tag::kMatmul: {
diff --git a/services/webnn/public/cpp/data_type_limits.cc b/services/webnn/public/cpp/data_type_limits.cc
index c2688f4..49effe4a 100644
--- a/services/webnn/public/cpp/data_type_limits.cc
+++ b/services/webnn/public/cpp/data_type_limits.cc
@@ -12,9 +12,12 @@
                                SupportedDataTypes constant,
                                SupportedDataTypes arg_min_max_input,
                                SupportedDataTypes arg_min_max_output,
+                               SupportedDataTypes batch_normalization_input,
                                SupportedDataTypes cast_input,
                                SupportedDataTypes clamp_input,
                                SupportedDataTypes concat_inputs,
+                               SupportedDataTypes conv2d_input,
+                               SupportedDataTypes conv_transpose2d_input,
                                SupportedDataTypes add_input,
                                SupportedDataTypes sub_input,
                                SupportedDataTypes mul_input,
@@ -51,10 +54,16 @@
                                SupportedDataTypes gather_elements_indices,
                                SupportedDataTypes gelu_input,
                                SupportedDataTypes gemm_input,
+                               SupportedDataTypes gru_input,
+                               SupportedDataTypes gru_cell_input,
                                SupportedDataTypes hard_sigmoid_input,
                                SupportedDataTypes hard_swish_input,
+                               SupportedDataTypes instance_normalization_input,
+                               SupportedDataTypes layer_normalization_input,
                                SupportedDataTypes leaky_relu_input,
                                SupportedDataTypes linear_input,
+                               SupportedDataTypes lstm_input,
+                               SupportedDataTypes lstm_cell_input,
                                SupportedDataTypes matmul_input,
                                SupportedDataTypes pad_input,
                                SupportedDataTypes average_pool2d_input,
@@ -89,9 +98,12 @@
       constant(constant),
       arg_min_max_input(arg_min_max_input),
       arg_min_max_output(arg_min_max_output),
+      batch_normalization_input(batch_normalization_input),
       cast_input(cast_input),
       clamp_input(clamp_input),
       concat_inputs(concat_inputs),
+      conv2d_input(conv2d_input),
+      conv_transpose2d_input(conv_transpose2d_input),
       add_input(add_input),
       sub_input(sub_input),
       mul_input(mul_input),
@@ -128,10 +140,16 @@
       gather_elements_indices(gather_elements_indices),
       gelu_input(gelu_input),
       gemm_input(gemm_input),
+      gru_input(gru_input),
+      gru_cell_input(gru_cell_input),
       hard_sigmoid_input(hard_sigmoid_input),
       hard_swish_input(hard_swish_input),
+      instance_normalization_input(instance_normalization_input),
+      layer_normalization_input(layer_normalization_input),
       leaky_relu_input(leaky_relu_input),
       linear_input(linear_input),
+      lstm_input(lstm_input),
+      lstm_cell_input(lstm_cell_input),
       matmul_input(matmul_input),
       pad_input(pad_input),
       average_pool2d_input(average_pool2d_input),
diff --git a/services/webnn/public/cpp/data_type_limits.h b/services/webnn/public/cpp/data_type_limits.h
index 4908086..6d86a752 100644
--- a/services/webnn/public/cpp/data_type_limits.h
+++ b/services/webnn/public/cpp/data_type_limits.h
@@ -17,9 +17,12 @@
                  SupportedDataTypes constant,
                  SupportedDataTypes arg_min_max_input,
                  SupportedDataTypes arg_min_max_output,
+                 SupportedDataTypes batch_normalization_input,
                  SupportedDataTypes cast_input,
                  SupportedDataTypes clamp_input,
                  SupportedDataTypes concat_inputs,
+                 SupportedDataTypes conv2d_input,
+                 SupportedDataTypes conv_transpose2d_input,
                  SupportedDataTypes add_input,
                  SupportedDataTypes sub_input,
                  SupportedDataTypes mul_input,
@@ -56,10 +59,16 @@
                  SupportedDataTypes gather_elements_indices,
                  SupportedDataTypes gelu_input,
                  SupportedDataTypes gemm_input,
+                 SupportedDataTypes gru_input,
+                 SupportedDataTypes gru_cell_input,
                  SupportedDataTypes hard_sigmoid_input,
                  SupportedDataTypes hard_swish_input,
+                 SupportedDataTypes instance_normalization_input,
+                 SupportedDataTypes layer_normalization_input,
                  SupportedDataTypes leaky_relu_input,
                  SupportedDataTypes linear_input,
+                 SupportedDataTypes lstm_input,
+                 SupportedDataTypes lstm_cell_input,
                  SupportedDataTypes matmul_input,
                  SupportedDataTypes pad_input,
                  SupportedDataTypes average_pool2d_input,
@@ -105,9 +114,12 @@
   SupportedDataTypes constant;
   SupportedDataTypes arg_min_max_input;
   SupportedDataTypes arg_min_max_output;
+  SupportedDataTypes batch_normalization_input;
   SupportedDataTypes cast_input;
   SupportedDataTypes clamp_input;
   SupportedDataTypes concat_inputs;
+  SupportedDataTypes conv2d_input;
+  SupportedDataTypes conv_transpose2d_input;
   SupportedDataTypes add_input;
   SupportedDataTypes sub_input;
   SupportedDataTypes mul_input;
@@ -144,10 +156,16 @@
   SupportedDataTypes gather_elements_indices;
   SupportedDataTypes gelu_input;
   SupportedDataTypes gemm_input;
+  SupportedDataTypes gru_input;
+  SupportedDataTypes gru_cell_input;
   SupportedDataTypes hard_sigmoid_input;
   SupportedDataTypes hard_swish_input;
+  SupportedDataTypes instance_normalization_input;
+  SupportedDataTypes layer_normalization_input;
   SupportedDataTypes leaky_relu_input;
   SupportedDataTypes linear_input;
+  SupportedDataTypes lstm_input;
+  SupportedDataTypes lstm_cell_input;
   SupportedDataTypes matmul_input;
   SupportedDataTypes pad_input;
   SupportedDataTypes average_pool2d_input;
@@ -186,9 +204,12 @@
          lhs.constant == rhs.constant &&
          lhs.arg_min_max_input == rhs.arg_min_max_input &&
          lhs.arg_min_max_output == rhs.arg_min_max_output &&
+         lhs.batch_normalization_input == rhs.batch_normalization_input &&
          lhs.cast_input == rhs.cast_input &&
          lhs.clamp_input == rhs.clamp_input &&
          lhs.concat_inputs == rhs.concat_inputs &&
+         lhs.conv2d_input == rhs.conv2d_input &&
+         lhs.conv_transpose2d_input == rhs.conv_transpose2d_input &&
          lhs.add_input == rhs.add_input &&
          lhs.sub_input == rhs.sub_input &&
          lhs.mul_input == rhs.mul_input &&
@@ -225,10 +246,16 @@
          lhs.gather_elements_indices == rhs.gather_elements_indices &&
          lhs.gelu_input == rhs.gelu_input &&
          lhs.gemm_input == rhs.gemm_input &&
+         lhs.gru_input == rhs.gru_input &&
+         lhs.gru_cell_input == rhs.gru_cell_input &&
          lhs.hard_sigmoid_input == rhs.hard_sigmoid_input &&
          lhs.hard_swish_input == rhs.hard_swish_input &&
+         lhs.instance_normalization_input == rhs.instance_normalization_input &&
+         lhs.layer_normalization_input == rhs.layer_normalization_input &&
          lhs.leaky_relu_input == rhs.leaky_relu_input &&
          lhs.linear_input == rhs.linear_input &&
+         lhs.lstm_input == rhs.lstm_input &&
+         lhs.lstm_cell_input == rhs.lstm_cell_input &&
          lhs.matmul_input == rhs.matmul_input &&
          lhs.pad_input == rhs.pad_input &&
          lhs.average_pool2d_input == rhs.average_pool2d_input &&
diff --git a/services/webnn/public/cpp/graph_validation_utils.cc b/services/webnn/public/cpp/graph_validation_utils.cc
index 5caf7a79..9263ac4 100644
--- a/services/webnn/public/cpp/graph_validation_utils.cc
+++ b/services/webnn/public/cpp/graph_validation_utils.cc
@@ -184,15 +184,9 @@
 // Validate and get the input info of 2-D direct and transposed convolution
 // operation given input operand and attributes.
 base::expected<Conv2dInputOutputInfo, std::string>
-ValidateAndGetConv2dInputInfo(const OperandDescriptor& input,
+ValidateAndGetConv2dInputInfo(const std::string& label,
+                              const OperandDescriptor& input,
                               const Conv2dAttributesBase& attributes) {
-  // Validate input operand.
-  const std::string& label = attributes.label;
-  if (!IsFloatingPointType(input.data_type())) {
-    return base::unexpected(ErrorWithLabel(
-        label, "The input data type must be a floating point type."));
-  }
-
   if (input.Rank() != 4) {
     return base::unexpected(
         ErrorWithLabel(label, "The input should be a 4-D tensor."));
@@ -533,16 +527,22 @@
 
 base::expected<OperandDescriptor, std::string>
 ValidateBatchNormalizationAndInferOutput(
+    const ContextProperties& context_properties,
     const OperandDescriptor& input,
     const OperandDescriptor& mean,
     const OperandDescriptor& variance,
     const BatchNormalizationAttributes& attributes) {
   // Validate input type.
   const std::string& label = attributes.label;
-  if (!IsFloatingPointType(input.data_type())) {
+  if (!context_properties.data_type_limits.batch_normalization_input.Has(
+          input.data_type())) {
     return base::unexpected(ErrorWithLabel(
-        label, "The input type must be one of the floating point types."));
+        label,
+        NotSupportedInputArgumentTypeError(
+            input.data_type(),
+            context_properties.data_type_limits.batch_normalization_input)));
   }
+
   if (attributes.axis >= input.Rank()) {
     return base::unexpected(ErrorWithLabel(
         label,
@@ -614,13 +614,22 @@
     default;
 
 base::expected<OperandDescriptor, std::string> ValidateConv2dAndInferOutput(
+    const ContextProperties& context_properties,
     const OperandDescriptor& input,
     const OperandDescriptor& filter,
     const Conv2dAttributes& attributes) {
-  // Validate input operand.
-  ASSIGN_OR_RETURN(Conv2dInputOutputInfo input_info,
-                   ValidateAndGetConv2dInputInfo(input, attributes));
   const std::string& label = attributes.label;
+  // Validate input operand.
+  if (!context_properties.data_type_limits.conv2d_input.Has(
+          input.data_type())) {
+    return base::unexpected(ErrorWithLabel(
+        label, NotSupportedInputArgumentTypeError(
+                   input.data_type(),
+                   context_properties.data_type_limits.conv2d_input)));
+  }
+  ASSIGN_OR_RETURN(Conv2dInputOutputInfo input_info,
+                   ValidateAndGetConv2dInputInfo(label, input, attributes));
+
   // Validate filter operand.
   if (filter.data_type() != input.data_type()) {
     return base::unexpected(ErrorWithLabel(
@@ -707,15 +716,26 @@
 
 base::expected<OperandDescriptor, std::string>
 ValidateConvTranspose2dAndInferOutput(
+    const ContextProperties& context_properties,
     const OperandDescriptor& input,
     const OperandDescriptor& filter,
     const ConvTranspose2dAttributes& attributes) {
   // Validate input operand.
   const std::string& label = attributes.label;
-  const auto input_info = ValidateAndGetConv2dInputInfo(input, attributes);
+  if (!context_properties.data_type_limits.conv_transpose2d_input.Has(
+          input.data_type())) {
+    return base::unexpected(ErrorWithLabel(
+        label,
+        NotSupportedInputArgumentTypeError(
+            input.data_type(),
+            context_properties.data_type_limits.conv_transpose2d_input)));
+  }
+  const auto input_info =
+      ValidateAndGetConv2dInputInfo(label, input, attributes);
   if (!input_info.has_value()) {
     return base::unexpected(ErrorWithLabel(label, input_info.error()));
   }
+
   // Validate filter operand.
   if (filter.data_type() != input.data_type()) {
     return base::unexpected(ErrorWithLabel(
@@ -1414,7 +1434,8 @@
 GruAttributes& GruAttributes::operator=(GruAttributes&& other) = default;
 
 base::expected<std::vector<OperandDescriptor>, std::string>
-ValidateGruAndInferOutput(const OperandDescriptor& input,
+ValidateGruAndInferOutput(const ContextProperties& context_properties,
+                          const OperandDescriptor& input,
                           const OperandDescriptor& weight,
                           const OperandDescriptor& recurrent_weight,
                           uint32_t steps,
@@ -1431,18 +1452,16 @@
   }
 
   // Validate the weight operand.
-  // The current spec doesn't specify the operand data type constraints of
-  // gru. An issue has been filed to track it:
-  // https://github.com/webmachinelearning/webnn/issues/283.
-  if (!IsFloatingPointType(input.data_type())) {
-    return base::unexpected(ErrorWithLabel(
-        label,
-        "The data type of input must be one of the floating point types."));
-  }
   if (input.Rank() != 3) {
     return base::unexpected(
         ErrorWithLabel(label, "The input must be a 3-D tensor."));
   }
+  if (!context_properties.data_type_limits.gru_input.Has(input.data_type())) {
+    return base::unexpected(ErrorWithLabel(
+        label,
+        NotSupportedInputArgumentTypeError(
+            input.data_type(), context_properties.data_type_limits.gru_input)));
+  }
 
   const std::vector<uint32_t>& input_dimensions = input.shape();
   if (input_dimensions[0] != steps) {
@@ -1532,6 +1551,7 @@
     default;
 
 base::expected<OperandDescriptor, std::string> ValidateGruCellAndInferOutput(
+    const ContextProperties& context_properties,
     const OperandDescriptor& input,
     const OperandDescriptor& weight,
     const OperandDescriptor& recurrent_weight,
@@ -1545,17 +1565,17 @@
   }
 
   // Validate the weight operand.
-  // TODO(crbug.com/331055053): Specify the operand data type constraints of
-  // operation.
-  if (!IsFloatingPointType(input.data_type())) {
-    return base::unexpected(ErrorWithLabel(
-        label,
-        "The data type of input must be one of the floating point types."));
-  }
   if (input.Rank() != 2) {
     return base::unexpected(
         ErrorWithLabel(label, "The input must be a 2-D tensor."));
   }
+  if (!context_properties.data_type_limits.gru_cell_input.Has(
+          input.data_type())) {
+    return base::unexpected(ErrorWithLabel(
+        label, NotSupportedInputArgumentTypeError(
+                   input.data_type(),
+                   context_properties.data_type_limits.gru_cell_input)));
+  }
 
   const uint32_t batch_size = input.shape()[0];
   const uint32_t input_size = input.shape()[1];
@@ -1623,18 +1643,23 @@
 
 base::expected<OperandDescriptor, std::string>
 ValidateInstanceNormalizationAndInferOutput(
+    const ContextProperties& context_properties,
     const OperandDescriptor& input,
     const InstanceNormalizationAttributes& attributes) {
   const std::string& label = attributes.label;
   // Validate the input operand.
-  if (!IsFloatingPointType(input.data_type())) {
-    return base::unexpected(ErrorWithLabel(
-        label, "The input type must be one of the floating point types."));
-  }
   if (input.Rank() != 4) {
     return base::unexpected(
         ErrorWithLabel(label, "The input should be a 4-D tensor."));
   }
+  if (!context_properties.data_type_limits.instance_normalization_input.Has(
+          input.data_type())) {
+    return base::unexpected(ErrorWithLabel(
+        label,
+        NotSupportedInputArgumentTypeError(
+            input.data_type(),
+            context_properties.data_type_limits.instance_normalization_input)));
+  }
 
   uint32_t axis;
   switch (attributes.layout) {
@@ -1683,14 +1708,19 @@
 
 base::expected<OperandDescriptor, std::string>
 ValidateLayerNormalizationAndInferOutput(
+    const ContextProperties& context_properties,
     const OperandDescriptor& input,
     base::span<const uint32_t> axes,
     const LayerNormalizationAttributes& attributes) {
   const std::string& label = attributes.label;
   // Validate the input operand.
-  if (!IsFloatingPointType(input.data_type())) {
+  if (!context_properties.data_type_limits.layer_normalization_input.Has(
+          input.data_type())) {
     return base::unexpected(ErrorWithLabel(
-        label, "The input type must be one of the floating point types."));
+        label,
+        NotSupportedInputArgumentTypeError(
+            input.data_type(),
+            context_properties.data_type_limits.layer_normalization_input)));
   }
 
   // Ensure that the axes are all less than the input rank and have no
@@ -1748,7 +1778,8 @@
 LstmAttributes& LstmAttributes::operator=(LstmAttributes&& other) = default;
 
 base::expected<std::vector<OperandDescriptor>, std::string>
-ValidateLstmAndInferOutput(const OperandDescriptor& input,
+ValidateLstmAndInferOutput(const ContextProperties& context_properties,
+                           const OperandDescriptor& input,
                            const OperandDescriptor& weight,
                            const OperandDescriptor& recurrent_weight,
                            const uint32_t steps,
@@ -1775,20 +1806,18 @@
     return base::unexpected(
         ErrorWithLabel(label, "The input should be a 3-D tensor."));
   }
+  if (!context_properties.data_type_limits.lstm_input.Has(input.data_type())) {
+    return base::unexpected(ErrorWithLabel(
+        label, NotSupportedInputArgumentTypeError(
+                   input.data_type(),
+                   context_properties.data_type_limits.lstm_input)));
+  }
 
   const auto& input_dimensions = input.shape();
   if (input_dimensions[0] != steps) {
     return base::unexpected(ErrorWithLabel(
         label, "The input dimensions[0] must be equal to the steps."));
   }
-  // The current spec doesn't specify the operand data type constraints of
-  // lstm. An issue has been filed to track it:
-  // https://github.com/webmachinelearning/webnn/issues/283.
-  if (!IsFloatingPointType(input.data_type())) {
-    return base::unexpected(ErrorWithLabel(
-        label,
-        "The data type of input must be one of the floating point types."));
-  }
 
   const uint32_t batch_size = input_dimensions[1];
   const uint32_t input_size = input_dimensions[2];
@@ -1886,7 +1915,8 @@
     default;
 
 base::expected<std::vector<OperandDescriptor>, std::string>
-ValidateLstmCellAndInferOutput(const OperandDescriptor& input,
+ValidateLstmCellAndInferOutput(const ContextProperties& context_properties,
+                               const OperandDescriptor& input,
                                const OperandDescriptor& weight,
                                const OperandDescriptor& recurrent_weight,
                                const OperandDescriptor& hidden_state,
@@ -1910,13 +1940,12 @@
     return base::unexpected(
         ErrorWithLabel(label, "The input should be a 2-D tensor."));
   }
-
-  // TODO(crbug.com/331055053): The current spec doesn't specify the operand
-  // data type constraints of lstm.
-  if (!IsFloatingPointType(input.data_type())) {
+  if (!context_properties.data_type_limits.lstm_cell_input.Has(
+          input.data_type())) {
     return base::unexpected(ErrorWithLabel(
-        label,
-        "The data type of input must be one of the floating point types."));
+        label, NotSupportedInputArgumentTypeError(
+                   input.data_type(),
+                   context_properties.data_type_limits.lstm_cell_input)));
   }
 
   const uint32_t batch_size = input.shape()[0];
diff --git a/services/webnn/public/cpp/graph_validation_utils.h b/services/webnn/public/cpp/graph_validation_utils.h
index f6c6897..bd758bd 100644
--- a/services/webnn/public/cpp/graph_validation_utils.h
+++ b/services/webnn/public/cpp/graph_validation_utils.h
@@ -428,6 +428,7 @@
 base::expected<OperandDescriptor, std::string> COMPONENT_EXPORT(
     WEBNN_PUBLIC_CPP)
     ValidateBatchNormalizationAndInferOutput(
+        const ContextProperties& context_properties,
         const OperandDescriptor& input,
         const OperandDescriptor& mean,
         const OperandDescriptor& variance,
@@ -437,7 +438,8 @@
 // WebIDL here https://www.w3.org/TR/webnn/#api-mlgraphbuilder-conv2d
 base::expected<OperandDescriptor, std::string> COMPONENT_EXPORT(
     WEBNN_PUBLIC_CPP)
-    ValidateConv2dAndInferOutput(const OperandDescriptor& input,
+    ValidateConv2dAndInferOutput(const ContextProperties& context_properties,
+                                 const OperandDescriptor& input,
                                  const OperandDescriptor& filter,
                                  const Conv2dAttributes& attributes);
 
@@ -447,6 +449,7 @@
 base::expected<OperandDescriptor, std::string> COMPONENT_EXPORT(
     WEBNN_PUBLIC_CPP)
     ValidateConvTranspose2dAndInferOutput(
+        const ContextProperties& context_properties,
         const OperandDescriptor& input,
         const OperandDescriptor& filter,
         const ConvTranspose2dAttributes& attributes);
@@ -525,7 +528,8 @@
 // https://www.w3.org/TR/webnn/#api-mlgraphbuilder-gru.
 base::expected<std::vector<OperandDescriptor>, std::string> COMPONENT_EXPORT(
     WEBNN_PUBLIC_CPP)
-    ValidateGruAndInferOutput(const OperandDescriptor& input,
+    ValidateGruAndInferOutput(const ContextProperties& context_properties,
+                              const OperandDescriptor& input,
                               const OperandDescriptor& weight,
                               const OperandDescriptor& recurrent_weight,
                               uint32_t steps,
@@ -536,7 +540,8 @@
 // here https://www.w3.org/TR/webnn/#api-mlgraphbuilder-grucell.
 base::expected<OperandDescriptor, std::string> COMPONENT_EXPORT(
     WEBNN_PUBLIC_CPP)
-    ValidateGruCellAndInferOutput(const OperandDescriptor& input,
+    ValidateGruCellAndInferOutput(const ContextProperties& context_properties,
+                                  const OperandDescriptor& input,
                                   const OperandDescriptor& weight,
                                   const OperandDescriptor& recurrent_weight,
                                   const OperandDescriptor& hidden_state,
@@ -549,6 +554,7 @@
 base::expected<OperandDescriptor, std::string> COMPONENT_EXPORT(
     WEBNN_PUBLIC_CPP)
     ValidateInstanceNormalizationAndInferOutput(
+        const ContextProperties& context_properties,
         const OperandDescriptor& input,
         const InstanceNormalizationAttributes& attributes);
 
@@ -557,6 +563,7 @@
 base::expected<OperandDescriptor, std::string> COMPONENT_EXPORT(
     WEBNN_PUBLIC_CPP)
     ValidateLayerNormalizationAndInferOutput(
+        const ContextProperties& context_properties,
         const OperandDescriptor& input,
         base::span<const uint32_t> axes,
         const LayerNormalizationAttributes& attributes);
@@ -565,7 +572,8 @@
 // in WebIDL here https://www.w3.org/TR/webnn/#api-mlgraphbuilder-lstm.
 base::expected<std::vector<OperandDescriptor>, std::string> COMPONENT_EXPORT(
     WEBNN_PUBLIC_CPP)
-    ValidateLstmAndInferOutput(const OperandDescriptor& input,
+    ValidateLstmAndInferOutput(const ContextProperties& context_properties,
+                               const OperandDescriptor& input,
                                const OperandDescriptor& weight,
                                const OperandDescriptor& recurrent_weight,
                                const uint32_t steps,
@@ -576,7 +584,8 @@
 // in WebIDL here https://www.w3.org/TR/webnn/#api-mlgraphbuilder-lstmcell.
 base::expected<std::vector<OperandDescriptor>, std::string> COMPONENT_EXPORT(
     WEBNN_PUBLIC_CPP)
-    ValidateLstmCellAndInferOutput(const OperandDescriptor& input,
+    ValidateLstmCellAndInferOutput(const ContextProperties& context_properties,
+                                   const OperandDescriptor& input,
                                    const OperandDescriptor& weight,
                                    const OperandDescriptor& recurrent_weight,
                                    const OperandDescriptor& hidden_state,
diff --git a/services/webnn/public/mojom/context_properties_mojom_traits_unittest.cc b/services/webnn/public/mojom/context_properties_mojom_traits_unittest.cc
index 7dcef5ca..371e33b2 100644
--- a/services/webnn/public/mojom/context_properties_mojom_traits_unittest.cc
+++ b/services/webnn/public/mojom/context_properties_mojom_traits_unittest.cc
@@ -25,6 +25,8 @@
        {webnn::OperandDataType::kFloat16, webnn::OperandDataType::kFloat32},
        {webnn::OperandDataType::kFloat16, webnn::OperandDataType::kFloat32},
        webnn::SupportedDataTypes::All(),
+       webnn::SupportedDataTypes::All(),
+       webnn::SupportedDataTypes::All(),
        {webnn::OperandDataType::kFloat16, webnn::OperandDataType::kFloat32},
        {webnn::OperandDataType::kInt32, webnn::OperandDataType::kInt64},
        {webnn::OperandDataType::kFloat16, webnn::OperandDataType::kInt8},
@@ -72,6 +74,13 @@
        webnn::SupportedDataTypes::All(),
        webnn::SupportedDataTypes::All(),
        {webnn::OperandDataType::kFloat32},
+       {webnn::OperandDataType::kFloat16, webnn::OperandDataType::kInt8},
+       {webnn::OperandDataType::kFloat16, webnn::OperandDataType::kUint8},
+       {webnn::OperandDataType::kUint8},
+       webnn::SupportedDataTypes::All(),
+       webnn::SupportedDataTypes::All(),
+       webnn::SupportedDataTypes::All(),
+       {webnn::OperandDataType::kFloat16, webnn::OperandDataType::kFloat32},
        {webnn::OperandDataType::kFloat32},
        webnn::SupportedDataTypes::All(),
        webnn::SupportedDataTypes::All(),
@@ -93,11 +102,11 @@
 
   webnn::ContextProperties output(
       webnn::InputOperandLayout::kNhwc, webnn::Resample2DAxes::kChannelsFirst,
-      {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
-       {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
-       {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
-       {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
-       {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}});
+      {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+       {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+       {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+       {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+       {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}});
 
   EXPECT_TRUE(
       mojo::test::SerializeAndDeserialize<webnn::mojom::ContextProperties>(
diff --git a/services/webnn/public/mojom/data_type_limits_mojom_traits.h b/services/webnn/public/mojom/data_type_limits_mojom_traits.h
index 7e6c463..2ed616a 100644
--- a/services/webnn/public/mojom/data_type_limits_mojom_traits.h
+++ b/services/webnn/public/mojom/data_type_limits_mojom_traits.h
@@ -30,6 +30,10 @@
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.arg_min_max_output;
   }
+  static webnn::SupportedDataTypes batch_normalization_input(
+      const webnn::DataTypeLimits& data_type_limits) {
+    return data_type_limits.batch_normalization_input;
+  }
   static webnn::SupportedDataTypes cast_input(
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.cast_input;
@@ -42,6 +46,14 @@
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.concat_inputs;
   }
+  static webnn::SupportedDataTypes conv2d_input(
+      const webnn::DataTypeLimits& data_type_limits) {
+    return data_type_limits.conv2d_input;
+  }
+  static webnn::SupportedDataTypes conv_transpose2d_input(
+      const webnn::DataTypeLimits& data_type_limits) {
+    return data_type_limits.conv_transpose2d_input;
+  }
   static webnn::SupportedDataTypes add_input(
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.add_input;
@@ -186,6 +198,14 @@
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.gemm_input;
   }
+  static webnn::SupportedDataTypes gru_input(
+      const webnn::DataTypeLimits& data_type_limits) {
+    return data_type_limits.gru_input;
+  }
+  static webnn::SupportedDataTypes gru_cell_input(
+      const webnn::DataTypeLimits& data_type_limits) {
+    return data_type_limits.gru_cell_input;
+  }
   static webnn::SupportedDataTypes hard_sigmoid_input(
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.hard_sigmoid_input;
@@ -194,6 +214,14 @@
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.hard_swish_input;
   }
+  static webnn::SupportedDataTypes instance_normalization_input(
+      const webnn::DataTypeLimits& data_type_limits) {
+    return data_type_limits.instance_normalization_input;
+  }
+  static webnn::SupportedDataTypes layer_normalization_input(
+      const webnn::DataTypeLimits& data_type_limits) {
+    return data_type_limits.layer_normalization_input;
+  }
   static webnn::SupportedDataTypes leaky_relu_input(
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.leaky_relu_input;
@@ -202,6 +230,14 @@
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.linear_input;
   }
+  static webnn::SupportedDataTypes lstm_input(
+      const webnn::DataTypeLimits& data_type_limits) {
+    return data_type_limits.lstm_input;
+  }
+  static webnn::SupportedDataTypes lstm_cell_input(
+      const webnn::DataTypeLimits& data_type_limits) {
+    return data_type_limits.lstm_cell_input;
+  }
   static webnn::SupportedDataTypes matmul_input(
       const webnn::DataTypeLimits& data_type_limits) {
     return data_type_limits.matmul_input;
@@ -328,9 +364,12 @@
     return data.ReadInput(&out->input) && data.ReadConstant(&out->constant) &&
            data.ReadArgMinMaxInput(&out->arg_min_max_input) &&
            data.ReadArgMinMaxOutput(&out->arg_min_max_output) &&
+           data.ReadBatchNormalizationInput(&out->batch_normalization_input) &&
            data.ReadCastInput(&out->cast_input) &&
            data.ReadClampInput(&out->clamp_input) &&
            data.ReadConcatInputs(&out->concat_inputs) &&
+           data.ReadConv2dInput(&out->conv2d_input) &&
+           data.ReadConvTranspose2dInput(&out->conv_transpose2d_input) &&
            data.ReadAddInput(&out->add_input) &&
            data.ReadSubInput(&out->sub_input) &&
            data.ReadMulInput(&out->mul_input) &&
@@ -367,10 +406,17 @@
            data.ReadGatherElementsIndices(&out->gather_elements_indices) &&
            data.ReadGeluInput(&out->gelu_input) &&
            data.ReadGemmInput(&out->gemm_input) &&
+           data.ReadGruInput(&out->gru_input) &&
+           data.ReadGruCellInput(&out->gru_cell_input) &&
            data.ReadHardSigmoidInput(&out->hard_sigmoid_input) &&
            data.ReadHardSwishInput(&out->hard_swish_input) &&
+           data.ReadInstanceNormalizationInput(
+               &out->instance_normalization_input) &&
+           data.ReadLayerNormalizationInput(&out->layer_normalization_input) &&
            data.ReadLeakyReluInput(&out->leaky_relu_input) &&
            data.ReadLinearInput(&out->linear_input) &&
+           data.ReadLstmInput(&out->lstm_input) &&
+           data.ReadLstmCellInput(&out->lstm_cell_input) &&
            data.ReadMatmulInput(&out->matmul_input) &&
            data.ReadPadInput(&out->pad_input) &&
            data.ReadAveragePool2dInput(&out->average_pool2d_input) &&
diff --git a/services/webnn/public/mojom/webnn_context_properties.mojom b/services/webnn/public/mojom/webnn_context_properties.mojom
index 22f65d9..c3872e9 100644
--- a/services/webnn/public/mojom/webnn_context_properties.mojom
+++ b/services/webnn/public/mojom/webnn_context_properties.mojom
@@ -46,9 +46,12 @@
   SupportedDataTypes arg_min_max_input;
   SupportedDataTypes arg_min_max_output;
 
+  SupportedDataTypes batch_normalization_input;
   SupportedDataTypes cast_input;
   SupportedDataTypes clamp_input;
   SupportedDataTypes concat_inputs;
+  SupportedDataTypes conv2d_input;
+  SupportedDataTypes conv_transpose2d_input;
 
   // Element-wise binary ops.
   SupportedDataTypes add_input;
@@ -97,10 +100,16 @@
 
   SupportedDataTypes gelu_input;
   SupportedDataTypes gemm_input;
+  SupportedDataTypes gru_input;
+  SupportedDataTypes gru_cell_input;
   SupportedDataTypes hard_sigmoid_input;
   SupportedDataTypes hard_swish_input;
+  SupportedDataTypes instance_normalization_input;
+  SupportedDataTypes layer_normalization_input;
   SupportedDataTypes leaky_relu_input;
   SupportedDataTypes linear_input;
+  SupportedDataTypes lstm_input;
+  SupportedDataTypes lstm_cell_input;
   SupportedDataTypes matmul_input;
   SupportedDataTypes pad_input;
 
diff --git a/services/webnn/tflite/graph_builder_tflite.cc b/services/webnn/tflite/graph_builder_tflite.cc
index 36c3343..fca1c71 100644
--- a/services/webnn/tflite/graph_builder_tflite.cc
+++ b/services/webnn/tflite/graph_builder_tflite.cc
@@ -339,9 +339,12 @@
        /*constant=*/SupportedDataTypes::All(),
        /*arg_min_max_input=*/kFloat32AndInt8To32AndUint8,
        /*arg_min_max_output=*/DataTypeConstraint::kInt32To64,
+       /*batch_normalization_input=*/kFloat32,
        /*cast_input=*/kFloat16To32Ints8To32AndInt64,
        /*clamp_input=*/kFloat32,
        /*concat_inputs=*/SupportedDataTypes::All(),
+       /*conv2d_input=*/kFloat32,
+       /*conv_transpose2d_input=*/kFloat32,
        /*add_input=*/kFloat32AndInt32To64,
        /*sub_input=*/kFloat32AndInt32To64,
        /*mul_input=*/kFloat32AndInt32To64AndUint32,
@@ -380,11 +383,17 @@
        /*gather_elements_indices=*/{},
        /*gelu_input=*/kFloat32,
        /*gemm_input=*/kFloat32,
+       /*gru_input=*/kFloat32,
+       /*gru_cell_input=*/kFloat32,
        /*hard_sigmoid_input=*/kFloat32,
        /*hard_swish_input=*/kFloat32,
+       /*instance_normalization_input=*/kFloat32,
+       /*layer_normalization_input=*/kFloat32,
        /*leaky_relu_input=*/kFloat32,
        // Linear is emulated by mul and add.
        /*linear_input=*/kFloat32AndInt32To64,
+       /*lstm_input=*/kFloat32,
+       /*lstm_cell_input=*/kFloat32,
        /*matmul_input=*/kFloat32AndInt8,
        /*pad_input=*/kFloat32AndInt32To64AndUint8,
        /*average_pool2d_input=*/kFloat32,
@@ -1207,12 +1216,9 @@
     -> base::expected<OperatorOffset, std::string> {
   const mojom::Operand& input_operand =
       GetOperand(batch_normalization.input_operand_id);
-  // TODO(crbug.com/339654398): Support 16-bit float with dequantize operator
-  // https://www.tensorflow.org/mlir/tfl_ops#tfldequantize_tfldequantizeop.
-  if (input_operand.descriptor.data_type() == OperandDataType::kFloat16) {
-    return base::unexpected("The 16-bit float data type is not supported.");
-  }
-  CHECK_EQ(input_operand.descriptor.data_type(), OperandDataType::kFloat32);
+  CHECK(context_properties_.data_type_limits.batch_normalization_input.Has(
+      input_operand.descriptor.data_type()));
+
   // The input shape has been validated to not overflow before creating tensor.
   const auto signed_input_dimensions =
       ToSignedDimensions(input_operand.descriptor.shape());
@@ -1321,21 +1327,26 @@
 
 auto GraphBuilderTflite::SerializeConv2d(const mojom::Conv2d& conv2d)
     -> base::expected<OperatorOffset, std::string> {
-  // TFLite schema doesn't support dilations and groups, they are being
-  // discussed in the issues
-  // https://github.com/tensorflow/tensorflow/issues/70031
-  // https://github.com/tensorflow/tensorflow/issues/69201
-  if (conv2d.kind == mojom::Conv2d::Kind::kTransposed &&
-      (conv2d.dilations->height != 1 || conv2d.dilations->width != 1 ||
-       conv2d.groups != 1)) {
-    return base::unexpected(
-        "convTranspose2d doesn't support dilations and groups.");
-  }
-
   const mojom::Operand& input_operand = GetOperand(conv2d.input_operand_id);
-  // TODO(crbug.com/328733319): Support other tensor data types.
-  if (input_operand.descriptor.data_type() != OperandDataType::kFloat32) {
-    return base::unexpected("The data type of input is not supported.");
+  const OperandDataType input_data_type = input_operand.descriptor.data_type();
+  switch (conv2d.kind) {
+    case mojom::Conv2d::Kind::kDirect:
+      // TODO(crbug.com/328733319): Support other tensor data types.
+      CHECK(context_properties_.data_type_limits.conv2d_input.Has(
+          input_data_type));
+      break;
+    case mojom::Conv2d::Kind::kTransposed:
+      // TODO(crbug.com/328733319): Support other tensor data types.
+      CHECK(context_properties_.data_type_limits.conv_transpose2d_input.Has(
+          input_data_type));
+      // TODO(crbug.com/364348906): Support dilations and groups parameter for
+      // convTranspose2d
+      if (conv2d.dilations->height != 1 || conv2d.dilations->width != 1 ||
+          conv2d.groups != 1) {
+        return base::unexpected(
+            "convTranspose2d doesn't support dilations and groups.");
+      }
+      break;
   }
 
   // Get tflite padding mode with the size2d of input, filter, dilation.
@@ -2153,12 +2164,8 @@
 auto GraphBuilderTflite::SerializeGruCell(const mojom::GruCell& gru_cell)
     -> base::expected<OperatorOffset, std::string> {
   const mojom::Operand& input_operand = GetOperand(gru_cell.input_operand_id);
-  // TODO(crbug.com/339654398): Support 16-bit float with dequantize operator
-  // https://www.tensorflow.org/mlir/tfl_ops#tfldequantize_tfldequantizeop.
-  if (input_operand.descriptor.data_type() == OperandDataType::kFloat16) {
-    return base::unexpected("The 16-bit float data type is not supported.");
-  }
-  CHECK_EQ(input_operand.descriptor.data_type(), OperandDataType::kFloat32);
+  CHECK(context_properties_.data_type_limits.gru_cell_input.Has(
+      input_operand.descriptor.data_type()));
   // The input shape has been validated to not overflow before creating tensor.
   const auto signed_input_dimensions =
       ToSignedDimensions(input_operand.descriptor.shape());
@@ -2516,26 +2523,28 @@
 
 // `RecurrentNetworkType` must be `mojom::Gru` or `mojom::Lstm`.
 template <typename RecurrentNetworkType>
+  requires(std::is_same_v<RecurrentNetworkType, mojom::Gru> ||
+           std::is_same_v<RecurrentNetworkType, mojom::Lstm>)
 auto GraphBuilderTflite::SerializeRecurrentNetwork(
     const RecurrentNetworkType& recurrent_network)
     -> base::expected<OperatorOffset, std::string> {
-  static_assert(std::is_same<RecurrentNetworkType, mojom::Gru>::value ||
-                std::is_same<RecurrentNetworkType, mojom::Lstm>::value);
   const mojom::Operand& input_operand =
       GetOperand(recurrent_network.input_operand_id);
-  // TODO(crbug.com/339654398): Support 16-bit float with dequantize operator
-  // https://www.tensorflow.org/mlir/tfl_ops#tfldequantize_tfldequantizeop.
-  if (input_operand.descriptor.data_type() == OperandDataType::kFloat16) {
-    return base::unexpected("The 16-bit float data type is not supported.");
+  const OperandDataType input_data_type = input_operand.descriptor.data_type();
+
+  if constexpr (std::is_same_v<RecurrentNetworkType, mojom::Lstm>) {
+    CHECK(context_properties_.data_type_limits.lstm_input.Has(input_data_type));
+  } else /* `RecurrentNetworkType` is  `mojom::Gru` */ {
+    CHECK(context_properties_.data_type_limits.gru_input.Has(input_data_type));
   }
-  CHECK_EQ(input_operand.descriptor.data_type(), OperandDataType::kFloat32);
+
   // The input shape has been validated to not overflow before creating tensor.
   const auto signed_input_dimensions =
       ToSignedDimensions(input_operand.descriptor.shape());
   CHECK(signed_input_dimensions.has_value());
   CHECK_EQ(signed_input_dimensions->size(), 3u);
   const ::tflite::TensorType input_tensor_type =
-      OperandDataTypeToTFLite(input_operand.descriptor.data_type());
+      OperandDataTypeToTFLite(input_data_type);
   const auto checked_hidden_size =
       base::MakeCheckedNum<int32_t>(recurrent_network.hidden_size);
   if (!checked_hidden_size.IsValid()) {
@@ -2976,12 +2985,9 @@
     -> base::expected<OperatorOffset, std::string> {
   const mojom::Operand& input_operand =
       GetOperand(instance_normalization.input_operand_id);
-  // TODO(crbug.com/339654398): Support 16-bit float with dequantize operator
-  // https://www.tensorflow.org/mlir/tfl_ops#tfldequantize_tfldequantizeop.
-  if (input_operand.descriptor.data_type() == OperandDataType::kFloat16) {
-    return base::unexpected("The 16-bit float data type is not supported.");
-  }
-  CHECK_EQ(input_operand.descriptor.data_type(), OperandDataType::kFloat32);
+  CHECK(context_properties_.data_type_limits.instance_normalization_input.Has(
+      input_operand.descriptor.data_type()));
+
   // The input shape has been validated to not overflow before creating tensor.
   const auto signed_input_dimensions =
       ToSignedDimensions(input_operand.descriptor.shape());
@@ -3050,12 +3056,9 @@
     -> base::expected<OperatorOffset, std::string> {
   const mojom::Operand& input_operand =
       GetOperand(layer_normalization.input_operand_id);
-  // TODO(crbug.com/339654398): Support 16-bit float with dequantize operator
-  // https://www.tensorflow.org/mlir/tfl_ops#tfldequantize_tfldequantizeop.
-  if (input_operand.descriptor.data_type() == OperandDataType::kFloat16) {
-    return base::unexpected("The 16-bit float data type is not supported.");
-  }
-  CHECK_EQ(input_operand.descriptor.data_type(), OperandDataType::kFloat32);
+  CHECK(context_properties_.data_type_limits.layer_normalization_input.Has(
+      input_operand.descriptor.data_type()));
+
   // The input shape has been validated to not overflow before creating tensor.
   const auto signed_input_dimensions =
       ToSignedDimensions(input_operand.descriptor.shape());
@@ -3169,12 +3172,9 @@
 auto GraphBuilderTflite::SerializeLstmCell(const mojom::LstmCell& lstm_cell)
     -> base::expected<OperatorOffset, std::string> {
   const mojom::Operand& input_operand = GetOperand(lstm_cell.input_operand_id);
-  // TODO(crbug.com/339654398): Support 16-bit float with dequantize operator
-  // https://www.tensorflow.org/mlir/tfl_ops#tfldequantize_tfldequantizeop.
-  if (input_operand.descriptor.data_type() == OperandDataType::kFloat16) {
-    return base::unexpected("The 16-bit float data type is not supported.");
-  }
-  CHECK_EQ(input_operand.descriptor.data_type(), OperandDataType::kFloat32);
+  CHECK(context_properties_.data_type_limits.lstm_cell_input.Has(
+      input_operand.descriptor.data_type()));
+
   // The input shape has been validated to not overflow before creating tensor.
   const auto signed_input_dimensions =
       ToSignedDimensions(input_operand.descriptor.shape());
diff --git a/services/webnn/tflite/graph_builder_tflite.h b/services/webnn/tflite/graph_builder_tflite.h
index ed5cad0..5c0cd4e 100644
--- a/services/webnn/tflite/graph_builder_tflite.h
+++ b/services/webnn/tflite/graph_builder_tflite.h
@@ -335,6 +335,8 @@
 
   // A helper function for serializing WebNN gru and lstm operations.
   template <typename RecurrentNetworkType>
+    requires(std::is_same_v<RecurrentNetworkType, mojom::Gru> ||
+             std::is_same_v<RecurrentNetworkType, mojom::Lstm>)
   base::expected<OperatorOffset, std::string> SerializeRecurrentNetwork(
       const RecurrentNetworkType& recurrent_network);
 
diff --git a/services/webnn/webnn_context_impl.cc b/services/webnn/webnn_context_impl.cc
index 3755b5c..c14774f 100644
--- a/services/webnn/webnn_context_impl.cc
+++ b/services/webnn/webnn_context_impl.cc
@@ -147,6 +147,8 @@
   // Only intersects for ones that have limits defined in the specification.
   // For ones that has no limit, no need to intersect with
   // `SupportedDataTypes::All()`.
+  backend_context_properties.data_type_limits.batch_normalization_input
+      .RetainAll(DataTypeConstraint::kFloat16To32);
   backend_context_properties.data_type_limits.logical_not_input.RetainAll(
       DataTypeConstraint::kUint8);
   backend_context_properties.data_type_limits.logical_output.RetainAll(
@@ -187,14 +189,26 @@
       DataTypeConstraint::kFloat16To32);
   backend_context_properties.data_type_limits.gemm_input.RetainAll(
       DataTypeConstraint::kFloat16To32);
+  backend_context_properties.data_type_limits.gru_input.RetainAll(
+      DataTypeConstraint::kFloat16To32);
+  backend_context_properties.data_type_limits.gru_cell_input.RetainAll(
+      DataTypeConstraint::kFloat16To32);
   backend_context_properties.data_type_limits.hard_sigmoid_input.RetainAll(
       DataTypeConstraint::kFloat16To32);
   backend_context_properties.data_type_limits.hard_swish_input.RetainAll(
       DataTypeConstraint::kFloat16To32);
+  backend_context_properties.data_type_limits.instance_normalization_input
+      .RetainAll(DataTypeConstraint::kFloat16To32);
+  backend_context_properties.data_type_limits.layer_normalization_input
+      .RetainAll(DataTypeConstraint::kFloat16To32);
   backend_context_properties.data_type_limits.leaky_relu_input.RetainAll(
       DataTypeConstraint::kFloat16To32);
   backend_context_properties.data_type_limits.linear_input.RetainAll(
       DataTypeConstraint::kFloat16To32);
+  backend_context_properties.data_type_limits.lstm_input.RetainAll(
+      DataTypeConstraint::kFloat16To32);
+  backend_context_properties.data_type_limits.lstm_cell_input.RetainAll(
+      DataTypeConstraint::kFloat16To32);
   backend_context_properties.data_type_limits.matmul_input.RetainAll(
       DataTypeConstraint::kFloat16To32);
   backend_context_properties.data_type_limits.average_pool2d_input.RetainAll(
diff --git a/services/webnn/webnn_graph_builder_impl.cc b/services/webnn/webnn_graph_builder_impl.cc
index 2cf74e8c..7cd80e5 100644
--- a/services/webnn/webnn_graph_builder_impl.cc
+++ b/services/webnn/webnn_graph_builder_impl.cc
@@ -562,6 +562,7 @@
 }
 
 bool ValidateBatchNormalization(
+    const ContextProperties& context_properties,
     const IdToOperandMap& id_to_operand_map,
     const mojom::BatchNormalization& batch_normalization,
     base::flat_set<uint64_t>& processed_operands) {
@@ -601,7 +602,8 @@
   }
 
   const auto validated_output = ValidateBatchNormalizationAndInferOutput(
-      input->descriptor, mean->descriptor, variance->descriptor,
+      context_properties, input->descriptor, mean->descriptor,
+      variance->descriptor,
       ConvertToBatchNormalizationAttributes(id_to_operand_map,
                                             batch_normalization));
   if (!validated_output.has_value()) {
@@ -744,7 +746,7 @@
   switch (conv2d.kind) {
     case mojom::Conv2d::Kind::kDirect: {
       validated_output = ValidateConv2dAndInferOutput(
-          input->descriptor, filter->descriptor,
+          context_properties, input->descriptor, filter->descriptor,
           ConvertToConv2dAttributes(context_properties, id_to_operand_map,
                                     conv2d, std::move(bias_operand)));
       break;
@@ -752,7 +754,7 @@
 
     case mojom::Conv2d::Kind::kTransposed: {
       validated_output = ValidateConvTranspose2dAndInferOutput(
-          input->descriptor, filter->descriptor,
+          context_properties, input->descriptor, filter->descriptor,
           ConvertToConvTranspose2dAttributes(context_properties,
                                              id_to_operand_map, conv2d,
                                              std::move(bias_operand)));
@@ -1094,7 +1096,8 @@
   return true;
 }
 
-bool ValidateGru(const IdToOperandMap& id_to_operand_map,
+bool ValidateGru(const ContextProperties& context_properties,
+                 const IdToOperandMap& id_to_operand_map,
                  const mojom::Gru& gru,
                  base::flat_set<uint64_t>& processed_operands) {
   if (!processed_operands.contains(gru.input_operand_id) ||
@@ -1146,8 +1149,8 @@
   }
 
   const auto validated_outputs = ValidateGruAndInferOutput(
-      input->descriptor, weight->descriptor, recurrent_weight->descriptor,
-      gru.steps, gru.hidden_size,
+      context_properties, input->descriptor, weight->descriptor,
+      recurrent_weight->descriptor, gru.steps, gru.hidden_size,
       ConvertToGruAttributes(id_to_operand_map, gru));
   if (!validated_outputs.has_value()) {
     return false;
@@ -1169,7 +1172,8 @@
   return true;
 }
 
-bool ValidateGruCell(const IdToOperandMap& id_to_operand_map,
+bool ValidateGruCell(const ContextProperties& context_properties,
+                     const IdToOperandMap& id_to_operand_map,
                      const mojom::GruCell& gru_cell,
                      base::flat_set<uint64_t>& processed_operands) {
   if (!processed_operands.contains(gru_cell.input_operand_id) ||
@@ -1217,8 +1221,9 @@
 
   const base::expected<OperandDescriptor, std::string> validated_output =
       ValidateGruCellAndInferOutput(
-          input->descriptor, weight->descriptor, recurrent_weight->descriptor,
-          hidden_state->descriptor, gru_cell.hidden_size,
+          context_properties, input->descriptor, weight->descriptor,
+          recurrent_weight->descriptor, hidden_state->descriptor,
+          gru_cell.hidden_size,
           ConvertToGruCellAttributes(id_to_operand_map, gru_cell));
   if (!validated_output.has_value()) {
     return false;
@@ -1236,12 +1241,14 @@
   return true;
 }
 
-bool ValidateHardSigmoid(const IdToOperandMap& id_to_operand_map,
+bool ValidateHardSigmoid(const ContextProperties& context_properties,
+                         const IdToOperandMap& id_to_operand_map,
                          const mojom::HardSigmoid& hard_sigmoid,
                          base::flat_set<uint64_t>& processed_operands) {
-  if (!ValidateUnaryOperation(id_to_operand_map, hard_sigmoid,
-                              DataTypeConstraint::kFloat16To32,
-                              processed_operands)) {
+  if (!ValidateUnaryOperation(
+          id_to_operand_map, hard_sigmoid,
+          context_properties.data_type_limits.hard_sigmoid_input,
+          processed_operands)) {
     return false;
   }
   if (!ValidateHardSigmoidAttributes(hard_sigmoid)) {
@@ -1252,6 +1259,7 @@
 }
 
 bool ValidateLayerNormalization(
+    const ContextProperties& context_properties,
     const IdToOperandMap& id_to_operand_map,
     const mojom::LayerNormalization& layer_normalization,
     base::flat_set<uint64_t>& processed_operands) {
@@ -1287,7 +1295,7 @@
   }
 
   const auto validated_output = ValidateLayerNormalizationAndInferOutput(
-      input->descriptor, layer_normalization.axes,
+      context_properties, input->descriptor, layer_normalization.axes,
       ConvertToLayerNormalizationAttributes(id_to_operand_map,
                                             layer_normalization));
   if (!validated_output.has_value()) {
@@ -1333,7 +1341,8 @@
   return true;
 }
 
-bool ValidateLstm(const IdToOperandMap& id_to_operand_map,
+bool ValidateLstm(const ContextProperties& context_properties,
+                  const IdToOperandMap& id_to_operand_map,
                   const mojom::Lstm& lstm,
                   base::flat_set<uint64_t>& processed_operands) {
   if (!processed_operands.contains(lstm.input_operand_id) ||
@@ -1400,8 +1409,8 @@
   }
 
   const auto validated_outputs = ValidateLstmAndInferOutput(
-      input->descriptor, weight->descriptor, recurrent_weight->descriptor,
-      lstm.steps, lstm.hidden_size,
+      context_properties, input->descriptor, weight->descriptor,
+      recurrent_weight->descriptor, lstm.steps, lstm.hidden_size,
       ConvertToLstmAttributes(id_to_operand_map, lstm));
   if (!validated_outputs.has_value()) {
     return false;
@@ -1423,7 +1432,8 @@
   return true;
 }
 
-bool ValidateLstmCell(const IdToOperandMap& id_to_operand_map,
+bool ValidateLstmCell(const ContextProperties& context_properties,
+                      const IdToOperandMap& id_to_operand_map,
                       const mojom::LstmCell& lstm_cell,
                       base::flat_set<uint64_t>& processed_operands) {
   if (!processed_operands.contains(lstm_cell.input_operand_id) ||
@@ -1482,9 +1492,9 @@
 
   const base::expected<std::vector<webnn::OperandDescriptor>, std::string>
       validated_outputs = ValidateLstmCellAndInferOutput(
-          input->descriptor, weight->descriptor, recurrent_weight->descriptor,
-          hidden_state->descriptor, cell_state->descriptor,
-          lstm_cell.hidden_size,
+          context_properties, input->descriptor, weight->descriptor,
+          recurrent_weight->descriptor, hidden_state->descriptor,
+          cell_state->descriptor, lstm_cell.hidden_size,
           ConvertToLstmCellAttributes(id_to_operand_map, lstm_cell));
   if (!validated_outputs.has_value()) {
     return false;
@@ -1507,6 +1517,7 @@
 }
 
 bool ValidateInstanceNormalization(
+    const ContextProperties& context_properties,
     const IdToOperandMap& id_to_operand_map,
     const mojom::InstanceNormalization& instance_normalization,
     base::flat_set<uint64_t>& processed_operands) {
@@ -1541,8 +1552,9 @@
   }
 
   const auto validated_output = ValidateInstanceNormalizationAndInferOutput(
-      input->descriptor, ConvertToInstanceNormalizationAttributes(
-                             id_to_operand_map, instance_normalization));
+      context_properties, input->descriptor,
+      ConvertToInstanceNormalizationAttributes(id_to_operand_map,
+                                               instance_normalization));
   if (!validated_output.has_value()) {
     return false;
   }
@@ -2039,7 +2051,7 @@
                                *operation.get_arg_min_max(),
                                processed_operands);
     case mojom::Operation::Tag::kBatchNormalization:
-      return ValidateBatchNormalization(id_to_operand_map,
+      return ValidateBatchNormalization(context_properties, id_to_operand_map,
                                         *operation.get_batch_normalization(),
                                         processed_operands);
     case mojom::Operation::Tag::kClamp:
@@ -2080,26 +2092,28 @@
       return ValidateGemm(context_properties, id_to_operand_map,
                           *operation.get_gemm(), processed_operands);
     case mojom::Operation::Tag::kGru:
-      return ValidateGru(id_to_operand_map, *operation.get_gru(),
-                         processed_operands);
+      return ValidateGru(context_properties, id_to_operand_map,
+                         *operation.get_gru(), processed_operands);
     case mojom::Operation::Tag::kGruCell:
-      return ValidateGruCell(id_to_operand_map, *operation.get_gru_cell(),
-                             processed_operands);
+      return ValidateGruCell(context_properties, id_to_operand_map,
+                             *operation.get_gru_cell(), processed_operands);
     case mojom::Operation::Tag::kHardSigmoid:
-      return ValidateHardSigmoid(
-          id_to_operand_map, *operation.get_hard_sigmoid(), processed_operands);
+      return ValidateHardSigmoid(context_properties, id_to_operand_map,
+                                 *operation.get_hard_sigmoid(),
+                                 processed_operands);
     case mojom::Operation::Tag::kHardSwish:
       return ValidateUnaryOperation(
           id_to_operand_map, *operation.get_hard_swish(),
-          DataTypeConstraint::kFloat16To32, processed_operands);
+          context_properties.data_type_limits.hard_swish_input,
+          processed_operands);
     case mojom::Operation::Tag::kLayerNormalization:
-      return ValidateLayerNormalization(id_to_operand_map,
+      return ValidateLayerNormalization(context_properties, id_to_operand_map,
                                         *operation.get_layer_normalization(),
                                         processed_operands);
     case mojom::Operation::Tag::kInstanceNormalization:
       return ValidateInstanceNormalization(
-          id_to_operand_map, *operation.get_instance_normalization(),
-          processed_operands);
+          context_properties, id_to_operand_map,
+          *operation.get_instance_normalization(), processed_operands);
     case mojom::Operation::Tag::kLeakyRelu:
       return ValidateLeakyRelu(context_properties, id_to_operand_map,
                                *operation.get_leaky_relu(), processed_operands);
@@ -2107,11 +2121,11 @@
       return ValidateLinear(context_properties, id_to_operand_map,
                             *operation.get_linear(), processed_operands);
     case mojom::Operation::Tag::kLstm:
-      return ValidateLstm(id_to_operand_map, *operation.get_lstm(),
-                          processed_operands);
+      return ValidateLstm(context_properties, id_to_operand_map,
+                          *operation.get_lstm(), processed_operands);
     case mojom::Operation::Tag::kLstmCell:
-      return ValidateLstmCell(id_to_operand_map, *operation.get_lstm_cell(),
-                              processed_operands);
+      return ValidateLstmCell(context_properties, id_to_operand_map,
+                              *operation.get_lstm_cell(), processed_operands);
     case mojom::Operation::Tag::kMatmul:
       return ValidateMatmul(context_properties, id_to_operand_map,
                             *operation.get_matmul(), processed_operands);
diff --git a/services/webnn/webnn_test_utils.cc b/services/webnn/webnn_test_utils.cc
index de23d69..1980720 100644
--- a/services/webnn/webnn_test_utils.cc
+++ b/services/webnn/webnn_test_utils.cc
@@ -463,89 +463,99 @@
 }
 
 ContextProperties GetContextPropertiesForTesting() {
-  return WebNNContextImpl::IntersectWithBaseProperties(
-      ContextProperties(InputOperandLayout::kNchw, Resample2DAxes::kAny,
-                        {/*input=*/SupportedDataTypes::All(),
-                         /*constant=*/SupportedDataTypes::All(),
-                         /*arg_min_max_input=*/SupportedDataTypes::All(),
-                         /*arg_min_max_output=*/
-                         {OperandDataType::kInt32, OperandDataType::kInt64},
-                         /*cast_input=*/SupportedDataTypes::All(),
-                         /*clamp_input=*/SupportedDataTypes::All(),
-                         /*concat_inputs=*/
-                         SupportedDataTypes::All(),
-                         /*add_input=*/SupportedDataTypes::All(),
-                         /*sub_input=*/SupportedDataTypes::All(),
-                         /*mul_input=*/SupportedDataTypes::All(),
-                         /*div_input=*/SupportedDataTypes::All(),
-                         /*max_input=*/SupportedDataTypes::All(),
-                         /*min_input=*/SupportedDataTypes::All(),
-                         /*pow_input=*/SupportedDataTypes::All(),
-                         /*equal_input=*/SupportedDataTypes::All(),
-                         /*greater_input=*/SupportedDataTypes::All(),
-                         /*greater_or_equal_input=*/SupportedDataTypes::All(),
-                         /*lesser_input=*/SupportedDataTypes::All(),
-                         /*lesser_or_equal_input=*/SupportedDataTypes::All(),
-                         /*logical_not_input=*/SupportedDataTypes::All(),
-                         /*logical_output=*/SupportedDataTypes::All(),
-                         /*abs_input=*/SupportedDataTypes::All(),
-                         /*ceil_input=*/SupportedDataTypes::All(),
-                         /*cos_input=*/SupportedDataTypes::All(),
-                         /*erf_input=*/SupportedDataTypes::All(),
-                         /*exp_input=*/SupportedDataTypes::All(),
-                         /*floor_input=*/SupportedDataTypes::All(),
-                         /*identity_input=*/SupportedDataTypes::All(),
-                         /*log_input=*/SupportedDataTypes::All(),
-                         /*neg_input=*/SupportedDataTypes::All(),
-                         /*reciprocal_input=*/SupportedDataTypes::All(),
-                         /*sign_input=*/SupportedDataTypes::All(),
-                         /*sin_input=*/SupportedDataTypes::All(),
-                         /*sqrt_input=*/SupportedDataTypes::All(),
-                         /*tan_input=*/SupportedDataTypes::All(),
-                         /*elu_input=*/SupportedDataTypes::All(),
-                         /*expand_input=*/SupportedDataTypes::All(),
-                         /*gather_input=*/SupportedDataTypes::All(),
-                         /*gather_indices=*/
-                         SupportedDataTypes::All(),
-                         /*gather_elements_input=*/SupportedDataTypes::All(),
-                         /*gather_elements_indices=*/
-                         SupportedDataTypes::All(),
-                         /*gelu_input=*/SupportedDataTypes::All(),
-                         /*gemm_input=*/SupportedDataTypes::All(),
-                         /*hard_sigmoid_input=*/SupportedDataTypes::All(),
-                         /*hard_swish_input=*/SupportedDataTypes::All(),
-                         /*leaky_relu_input=*/SupportedDataTypes::All(),
-                         /*linear_input=*/SupportedDataTypes::All(),
-                         /*matmul_input=*/SupportedDataTypes::All(),
-                         /*pad_input=*/SupportedDataTypes::All(),
-                         /*average_pool2d_input=*/SupportedDataTypes::All(),
-                         /*l2_pool2d_input=*/SupportedDataTypes::All(),
-                         /*max_pool2d_input=*/SupportedDataTypes::All(),
-                         /*prelu_input=*/SupportedDataTypes::All(),
-                         /*reduce_l1_input=*/SupportedDataTypes::All(),
-                         /*reduce_l2_input=*/SupportedDataTypes::All(),
-                         /*reduce_log_sum_input=*/SupportedDataTypes::All(),
-                         /*reduce_log_sum_exp_input=*/SupportedDataTypes::All(),
-                         /*reduce_max_input=*/SupportedDataTypes::All(),
-                         /*reduce_mean_input=*/SupportedDataTypes::All(),
-                         /*reduce_min_input=*/SupportedDataTypes::All(),
-                         /*reduce_product_input=*/SupportedDataTypes::All(),
-                         /*reduce_sum_input=*/SupportedDataTypes::All(),
-                         /*reduce_sum_square_input=*/SupportedDataTypes::All(),
-                         /*relu_input=*/SupportedDataTypes::All(),
-                         /*resample2d_input=*/SupportedDataTypes::All(),
-                         /*reshape_input=*/SupportedDataTypes::All(),
-                         /*sigmoid_input=*/SupportedDataTypes::All(),
-                         /*slice_input=*/SupportedDataTypes::All(),
-                         /*softmax_input=*/SupportedDataTypes::All(),
-                         /*softplus_input=*/SupportedDataTypes::All(),
-                         /*softsign_input=*/SupportedDataTypes::All(),
-                         /*split_input=*/SupportedDataTypes::All(),
-                         /*tanh_input=*/SupportedDataTypes::All(),
-                         /*transpose_input=*/SupportedDataTypes::All(),
-                         /*triangular_input=*/SupportedDataTypes::All(),
-                         /*where_condition=*/SupportedDataTypes::All(),
-                         /*where_value=*/SupportedDataTypes::All()}));
+  return WebNNContextImpl::IntersectWithBaseProperties(ContextProperties(
+      InputOperandLayout::kNchw, Resample2DAxes::kAny,
+      {/*input=*/SupportedDataTypes::All(),
+       /*constant=*/SupportedDataTypes::All(),
+       /*arg_min_max_input=*/SupportedDataTypes::All(),
+       /*arg_min_max_output=*/
+       {OperandDataType::kInt32, OperandDataType::kInt64},
+       /*batch_normalization_input=*/SupportedDataTypes::All(),
+       /*cast_input=*/SupportedDataTypes::All(),
+       /*clamp_input=*/SupportedDataTypes::All(),
+       /*concat_inputs=*/
+       SupportedDataTypes::All(),
+       /*conv2d_input=*/DataTypeConstraint::kFloat16To32,
+       /*conv_transpose2d_input=*/
+       DataTypeConstraint::kFloat16To32,
+       /*add_input=*/SupportedDataTypes::All(),
+       /*sub_input=*/SupportedDataTypes::All(),
+       /*mul_input=*/SupportedDataTypes::All(),
+       /*div_input=*/SupportedDataTypes::All(),
+       /*max_input=*/SupportedDataTypes::All(),
+       /*min_input=*/SupportedDataTypes::All(),
+       /*pow_input=*/SupportedDataTypes::All(),
+       /*equal_input=*/SupportedDataTypes::All(),
+       /*greater_input=*/SupportedDataTypes::All(),
+       /*greater_or_equal_input=*/SupportedDataTypes::All(),
+       /*lesser_input=*/SupportedDataTypes::All(),
+       /*lesser_or_equal_input=*/SupportedDataTypes::All(),
+       /*logical_not_input=*/SupportedDataTypes::All(),
+       /*logical_output=*/SupportedDataTypes::All(),
+       /*abs_input=*/SupportedDataTypes::All(),
+       /*ceil_input=*/SupportedDataTypes::All(),
+       /*cos_input=*/SupportedDataTypes::All(),
+       /*erf_input=*/SupportedDataTypes::All(),
+       /*exp_input=*/SupportedDataTypes::All(),
+       /*floor_input=*/SupportedDataTypes::All(),
+       /*identity_input=*/SupportedDataTypes::All(),
+       /*log_input=*/SupportedDataTypes::All(),
+       /*neg_input=*/SupportedDataTypes::All(),
+       /*reciprocal_input=*/SupportedDataTypes::All(),
+       /*sign_input=*/SupportedDataTypes::All(),
+       /*sin_input=*/SupportedDataTypes::All(),
+       /*sqrt_input=*/SupportedDataTypes::All(),
+       /*tan_input=*/SupportedDataTypes::All(),
+       /*elu_input=*/SupportedDataTypes::All(),
+       /*expand_input=*/SupportedDataTypes::All(),
+       /*gather_input=*/SupportedDataTypes::All(),
+       /*gather_indices=*/
+       SupportedDataTypes::All(),
+       /*gather_elements_input=*/SupportedDataTypes::All(),
+       /*gather_elements_indices=*/
+       SupportedDataTypes::All(),
+       /*gelu_input=*/SupportedDataTypes::All(),
+       /*gemm_input=*/SupportedDataTypes::All(),
+       /*gru_input=*/SupportedDataTypes::All(),
+       /*gru_cell_input=*/SupportedDataTypes::All(),
+       /*hard_sigmoid_input=*/SupportedDataTypes::All(),
+       /*hard_swish_input=*/SupportedDataTypes::All(),
+       /*instance_normalization_input=*/SupportedDataTypes::All(),
+       /*layer_normalization_input=*/SupportedDataTypes::All(),
+       /*leaky_relu_input=*/SupportedDataTypes::All(),
+       /*linear_input=*/SupportedDataTypes::All(),
+       /*lstm_input=*/SupportedDataTypes::All(),
+       /*lstm_cell_input=*/SupportedDataTypes::All(),
+       /*matmul_input=*/SupportedDataTypes::All(),
+       /*pad_input=*/SupportedDataTypes::All(),
+       /*average_pool2d_input=*/SupportedDataTypes::All(),
+       /*l2_pool2d_input=*/SupportedDataTypes::All(),
+       /*max_pool2d_input=*/SupportedDataTypes::All(),
+       /*prelu_input=*/SupportedDataTypes::All(),
+       /*reduce_l1_input=*/SupportedDataTypes::All(),
+       /*reduce_l2_input=*/SupportedDataTypes::All(),
+       /*reduce_log_sum_input=*/SupportedDataTypes::All(),
+       /*reduce_log_sum_exp_input=*/SupportedDataTypes::All(),
+       /*reduce_max_input=*/SupportedDataTypes::All(),
+       /*reduce_mean_input=*/SupportedDataTypes::All(),
+       /*reduce_min_input=*/SupportedDataTypes::All(),
+       /*reduce_product_input=*/SupportedDataTypes::All(),
+       /*reduce_sum_input=*/SupportedDataTypes::All(),
+       /*reduce_sum_square_input=*/SupportedDataTypes::All(),
+       /*relu_input=*/SupportedDataTypes::All(),
+       /*resample2d_input=*/SupportedDataTypes::All(),
+       /*reshape_input=*/SupportedDataTypes::All(),
+       /*sigmoid_input=*/SupportedDataTypes::All(),
+       /*slice_input=*/SupportedDataTypes::All(),
+       /*softmax_input=*/SupportedDataTypes::All(),
+       /*softplus_input=*/SupportedDataTypes::All(),
+       /*softsign_input=*/SupportedDataTypes::All(),
+       /*split_input=*/SupportedDataTypes::All(),
+       /*tanh_input=*/SupportedDataTypes::All(),
+       /*transpose_input=*/SupportedDataTypes::All(),
+       /*triangular_input=*/SupportedDataTypes::All(),
+       /*where_condition=*/SupportedDataTypes::All(),
+       /*where_value=*/SupportedDataTypes::All()}));
 }
 
 }  // namespace webnn
diff --git a/skia/ext/draw_gainmap_image.cc b/skia/ext/draw_gainmap_image.cc
index 27de603..623c1b80 100644
--- a/skia/ext/draw_gainmap_image.cc
+++ b/skia/ext/draw_gainmap_image.cc
@@ -11,7 +11,7 @@
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrRecordingContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrRecordingContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/graphite/Surface.h"
 #include "third_party/skia/include/private/SkGainmapInfo.h"
diff --git a/skia/public/mojom/surface_origin_mojom_traits.h b/skia/public/mojom/surface_origin_mojom_traits.h
index 7c225606..c18eef30 100644
--- a/skia/public/mojom/surface_origin_mojom_traits.h
+++ b/skia/public/mojom/surface_origin_mojom_traits.h
@@ -8,7 +8,7 @@
 #include "base/notreached.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 #include "skia/public/mojom/surface_origin.mojom-shared.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
 
 namespace mojo {
 
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 9fb5260..80ed6909 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -10238,6 +10238,7 @@
         "args": [
           "--emulator-debug-tags=all",
           "--avd-config=../../tools/android/avd/proto/android_35_google_apis_x64.textpb",
+          "--gtest_filter=-All/SharedStorageChromeBrowserTest.CrossOriginWorklet_SelectURL_Success/*",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index d87ab89..f345295 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -21110,6 +21110,103 @@
       "all"
     ]
   },
+  "mac-skia-alt-arm64-rel-tests": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "--flag-specific=enable-skia-graphite",
+          "--skipped=always",
+          "--timeout-ms=20000",
+          "--num-retries=3",
+          "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "graphite_enabled_blink_web_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "results_handler": "layout tests",
+        "swarming": {
+          "dimensions": {
+            "cpu": "arm64",
+            "os": "Mac-14"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "blink_web_tests",
+        "test_id_prefix": "ninja://:blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=enable-skia-graphite",
+          "--skipped=always",
+          "--timeout-ms=20000",
+          "--num-retries=3",
+          "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "graphite_enabled_blink_wpt_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "results_handler": "layout tests",
+        "swarming": {
+          "dimensions": {
+            "cpu": "arm64",
+            "os": "Mac-14"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 7
+        },
+        "test": "blink_wpt_tests",
+        "test_id_prefix": "ninja://:blink_wpt_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=enable-skia-graphite",
+          "--skipped=always",
+          "--timeout-multiplier=2",
+          "--inverted-test-launcher-filter-file=../../third_party/blink/web_tests/TestLists/chrome.filter",
+          "--test-launcher-filter-file=../../third_party/blink/web_tests/TestLists/headless_shell.filter",
+          "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "graphite_enabled_headless_shell_wpt_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "results_handler": "layout tests",
+        "swarming": {
+          "dimensions": {
+            "cpu": "arm64",
+            "os": "Mac-14"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "headless_shell_wpt",
+        "test_id_prefix": "ninja://:headless_shell_wpt/"
+      }
+    ]
+  },
   "mac11-arm64-rel-tests": {
     "gtest_tests": [
       {
@@ -25910,103 +26007,6 @@
       }
     ]
   },
-  "mac13-skia-alt-arm64-rel-tests": {
-    "isolated_scripts": [
-      {
-        "args": [
-          "--flag-specific=enable-skia-graphite",
-          "--skipped=always",
-          "--timeout-ms=20000",
-          "--num-retries=3",
-          "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json"
-        ],
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "graphite_enabled_blink_web_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-13"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "blink_web_tests",
-        "test_id_prefix": "ninja://:blink_web_tests/"
-      },
-      {
-        "args": [
-          "--flag-specific=enable-skia-graphite",
-          "--skipped=always",
-          "--timeout-ms=20000",
-          "--num-retries=3",
-          "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json"
-        ],
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "graphite_enabled_blink_wpt_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-13"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 7
-        },
-        "test": "blink_wpt_tests",
-        "test_id_prefix": "ninja://:blink_wpt_tests/"
-      },
-      {
-        "args": [
-          "--flag-specific=enable-skia-graphite",
-          "--skipped=always",
-          "--timeout-multiplier=2",
-          "--inverted-test-launcher-filter-file=../../third_party/blink/web_tests/TestLists/chrome.filter",
-          "--test-launcher-filter-file=../../third_party/blink/web_tests/TestLists/headless_shell.filter",
-          "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json"
-        ],
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "graphite_enabled_headless_shell_wpt_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-13"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "headless_shell_wpt",
-        "test_id_prefix": "ninja://:headless_shell_wpt/"
-      }
-    ]
-  },
   "mac14-arm64-rel-tests": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 59ec3c3..9a63d93 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -90,6 +90,12 @@
           '--gtest_filter=-All/SharedStorageChromeBrowserTest.CrossOriginWorklet_SelectURL_Success/*',
         ],
       },
+      'android-15-x64-fyi-rel': {
+        'args': [
+          # https://crbug.com/361042311
+          '--gtest_filter=-All/SharedStorageChromeBrowserTest.CrossOriginWorklet_SelectURL_Success/*',
+        ],
+      },
       'android-asan': {
         'swarming': {
           'shards': 2,
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 1cc36d6..d1122620 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -4374,6 +4374,14 @@
           'all',
         ],
       },
+      'mac-skia-alt-arm64-rel-tests': {
+        'mixins': [
+          'mac_default_arm64',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'chromium_web_tests_graphite_isolated_scripts',
+        },
+      },
       'mac11-arm64-rel-tests': {
         'mixins': [
             'mac_11_arm64',
@@ -4405,14 +4413,6 @@
           'isolated_scripts': 'chromium_mac_rel_isolated_scripts',
         },
       },
-      'mac13-skia-alt-arm64-rel-tests': {
-        'mixins': [
-          'mac_13_arm64',
-        ],
-        'test_suites': {
-          'isolated_scripts': 'chromium_web_tests_graphite_isolated_scripts',
-        },
-      },
       'mac14-arm64-rel-tests': {
         'mixins': [
             # Only run selected test suites on CQ. https://crbug.com/1234525.
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index 3c5ecc51..8c9e97f2a 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -145,6 +145,8 @@
     self.benchmark_path = os.path.join(isolated_out_dir, perf_test_name)
 
   def SetUp(self):
+    if os.path.exists(self.benchmark_path):
+      shutil.rmtree(self.benchmark_path)
     os.makedirs(self.benchmark_path)
     return self
 
@@ -954,6 +956,12 @@
                       help='Benchmark name displayed to the user,'
                       ' supported with crossbench only',
                       required=False)
+  # Added to address android flakiness.
+  parser.add_argument('--benchmark-max-runs',
+                      help='Max number of benchmark runs until it succeeds.',
+                      type=int,
+                      required=False,
+                      default=1)
   # crbug.com/1236245: This allows for per-benchmark device logs.
   parser.add_argument('--per-test-logs-dir',
                       help='Require --logs-dir args for test',
@@ -1029,8 +1037,12 @@
                                                       isolated_out_dir,
                                                       test_results_files)
   elif options.executable.endswith(CrossbenchTest.EXECUTABLE):
+    assert options.benchmark_max_runs == 1, (
+        'Benchmark rerun is not supported with CrossbenchTest.')
     overall_return_code = CrossbenchTest(options, isolated_out_dir).execute()
   elif options.non_telemetry:
+    assert options.benchmark_max_runs == 1, (
+        'Benchmark rerun is not supported in non telemetry tests.')
     benchmark_name = options.gtest_benchmark_name
     passthrough_args = options.passthrough_args
     # crbug/1146949#c15
@@ -1061,15 +1073,19 @@
   elif options.benchmarks:
     benchmarks = options.benchmarks.split(',')
     for benchmark in benchmarks:
-      output_paths = OutputFilePaths(isolated_out_dir, benchmark).SetUp()
       command_generator = TelemetryCommandGenerator(benchmark, options)
-      print('\n### {folder} ###'.format(folder=benchmark))
-      return_code = execute_telemetry_benchmark(
-          command_generator,
-          output_paths,
-          options.xvfb,
-          options.ignore_benchmark_exit_code,
-          no_output_conversion=options.no_output_conversion)
+      for run_num in range(options.benchmark_max_runs):
+        print('\n### {folder} (attempt #{num}) ###'.format(folder=benchmark,
+                                                           num=run_num))
+        output_paths = OutputFilePaths(isolated_out_dir, benchmark).SetUp()
+        return_code = execute_telemetry_benchmark(
+            command_generator,
+            output_paths,
+            options.xvfb,
+            options.ignore_benchmark_exit_code,
+            no_output_conversion=options.no_output_conversion)
+        if return_code == 0:
+          break
       overall_return_code = return_code or overall_return_code
       test_results_files.append(output_paths.test_results)
     if options.run_ref_build:
@@ -1096,6 +1112,8 @@
 
 def _run_benchmarks_on_shardmap(shard_map, options, isolated_out_dir,
                                 test_results_files):
+  assert options.benchmark_max_runs == 1, (
+      'Benchmark rerun is not supported with shards.')
   overall_return_code = 0
   # TODO(crbug.com/40631538): shard environment variables are not specified
   # for single-shard shard runs.
@@ -1165,7 +1183,7 @@
     benchmarks = shard_configuration['crossbench']
     # Overwriting the "run_benchmark" with the Crossbench tool.
     options.executable = str(CROSSBENCH_TOOL)
-    original_passthrough_args = options.passthrough_args
+    original_passthrough_args = options.passthrough_args.copy()
     for benchmark, benchmark_config in benchmarks.items():
       display_name = benchmark_config.get('display_name', benchmark)
       print(f'\n### {display_name} ###')
@@ -1178,7 +1196,7 @@
       overall_return_code = return_code or overall_return_code
       test_results_files.append(
           OutputFilePaths(isolated_out_dir, display_name).test_results)
-      options.passthrough_args = original_passthrough_args
+      options.passthrough_args = original_passthrough_args.copy()
 
   return overall_return_code
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index e2891926..ea891ea 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -398,7 +398,8 @@
                         "AndroidBrowserControlsInViz"
                     ],
                     "disable_features": [
-                        "AndroidBcivWithSuppression"
+                        "AndroidBcivWithSuppression",
+                        "AndroidBcivZeroBrowserFrames"
                     ]
                 }
             ]
@@ -12111,6 +12112,21 @@
             ]
         }
     ],
+    "IOSQuickDelete": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "kIOSQuickDelete"
+                    ]
+                }
+            ]
+        }
+    ],
     "IOSSaveToDrive": [
         {
             "platforms": [
@@ -12142,21 +12158,6 @@
             ]
         }
     ],
-    "IOSSaveUsernameInUff": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "IosSaveUsernameInUff"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSSharedHighlightingColorChange": [
         {
             "platforms": [
@@ -16339,28 +16340,6 @@
             ]
         }
     ],
-    "PartitionAllocMakeFreeNoOpOnShutdown": [
-        {
-            "platforms": [
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "EnabledInShutdown",
-                    "params": {
-                        "callsite": "in-shutdown-threads"
-                    },
-                    "enable_features": [
-                        "PartitionAllocMakeFreeNoOpOnShutdown"
-                    ]
-                }
-            ]
-        }
-    ],
     "PartitionAllocMemoryReclaimer": [
         {
             "platforms": [
@@ -20306,13 +20285,8 @@
     "SafetyHubAbusiveNotificationRevocation": [
         {
             "platforms": [
-                "android",
                 "chromeos",
-                "chromeos_lacros",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
+                "chromeos_lacros"
             ],
             "experiments": [
                 {
diff --git a/third_party/.gitignore b/third_party/.gitignore
index 2398e2b..b674ea3 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -144,7 +144,6 @@
 /syzygy
 /syzygy/binaries
 /tint/src
-/turbine/*.jar
 /usb_ids
 /v8-i18n
 /vulkan-headers/src
diff --git a/third_party/abseil-cpp/BUILD.gn b/third_party/abseil-cpp/BUILD.gn
index 9efb5d85..25e2340 100644
--- a/third_party/abseil-cpp/BUILD.gn
+++ b/third_party/abseil-cpp/BUILD.gn
@@ -289,6 +289,7 @@
         "absl/algorithm:container_test",
         "absl/base:config_test",
         "absl/base:no_destructor_test",
+        "absl/base:nullability_default_nonnull_test",
         "absl/base:nullability_test",
         "absl/base:poison_test",
         "absl/base:prefetch_test",
diff --git a/third_party/abseil-cpp/README.chromium b/third_party/abseil-cpp/README.chromium
index dab0ce4..64a0842 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: N/A
-Revision: 40a975ff1e6e19f65846c9700d7371de1672ee16
+Revision: b014bff59dc6e1202fa9144dbabd50285d01cd7a
 Security Critical: yes
 Shipped: yes
 
diff --git a/third_party/abseil-cpp/absl/base/BUILD.bazel b/third_party/abseil-cpp/absl/base/BUILD.bazel
index 38a7652..4c2f68f 100644
--- a/third_party/abseil-cpp/absl/base/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/base/BUILD.bazel
@@ -646,6 +646,16 @@
 )
 
 cc_test(
+    name = "nullability_default_nonnull_test",
+    srcs = ["nullability_default_nonnull_test.cc"],
+    deps = [
+        ":nullability",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
     name = "raw_logging_test",
     srcs = ["raw_logging_test.cc"],
     copts = ABSL_TEST_COPTS,
diff --git a/third_party/abseil-cpp/absl/base/BUILD.gn b/third_party/abseil-cpp/absl/base/BUILD.gn
index 8f3e866..0a7a1b4 100644
--- a/third_party/abseil-cpp/absl/base/BUILD.gn
+++ b/third_party/abseil-cpp/absl/base/BUILD.gn
@@ -387,3 +387,8 @@
     ":nullability",
   ]
 }
+
+absl_test("nullability_default_nonnull_test") {
+  sources = [ "nullability_default_nonnull_test.cc" ]
+  deps = [ ":nullability" ]
+}
diff --git a/third_party/abseil-cpp/absl/base/CMakeLists.txt b/third_party/abseil-cpp/absl/base/CMakeLists.txt
index 835624f..75a7b1a 100644
--- a/third_party/abseil-cpp/absl/base/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/base/CMakeLists.txt
@@ -95,6 +95,18 @@
     GTest::gtest_main
 )
 
+absl_cc_test(
+  NAME
+    nullability_default_nonnull_test
+  SRCS
+    "nullability_default_nonnull_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::nullability
+    GTest::gtest_main
+)
+
 # Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
diff --git a/third_party/abseil-cpp/absl/base/macros.h b/third_party/abseil-cpp/absl/base/macros.h
index b318f1166..ccc86ab2 100644
--- a/third_party/abseil-cpp/absl/base/macros.h
+++ b/third_party/abseil-cpp/absl/base/macros.h
@@ -34,6 +34,7 @@
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/optimization.h"
+#include "absl/base/options.h"
 #include "absl/base/port.h"
 
 // ABSL_ARRAYSIZE()
@@ -120,7 +121,7 @@
 //
 // See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on
 // hardened mode.
-#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG)
+#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG)
 #define ABSL_HARDENING_ASSERT(expr)                 \
   (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \
                              : [] { ABSL_INTERNAL_HARDENING_ABORT(); }())
@@ -128,6 +129,25 @@
 #define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr)
 #endif
 
+// ABSL_HARDENING_ASSERT_SLOW()
+//
+// `ABSL_HARDENING_ASSERT()` is like `ABSL_HARDENING_ASSERT()`,
+//  but specifically for assertions whose predicates are too slow
+//  to be enabled in many applications.
+//
+// When `NDEBUG` is not defined, `ABSL_HARDENING_ASSERT_SLOW()` is identical to
+// `ABSL_ASSERT()`.
+//
+// See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on
+// hardened mode.
+#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG)
+#define ABSL_HARDENING_ASSERT_SLOW(expr)            \
+  (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \
+                             : [] { ABSL_INTERNAL_HARDENING_ABORT(); }())
+#else
+#define ABSL_HARDENING_ASSERT_SLOW(expr) ABSL_ASSERT(expr)
+#endif
+
 #ifdef ABSL_HAVE_EXCEPTIONS
 #define ABSL_INTERNAL_TRY try
 #define ABSL_INTERNAL_CATCH_ANY catch (...)
diff --git a/third_party/abseil-cpp/absl/base/nullability.h b/third_party/abseil-cpp/absl/base/nullability.h
index 34dc083a..6322c1dd 100644
--- a/third_party/abseil-cpp/absl/base/nullability.h
+++ b/third_party/abseil-cpp/absl/base/nullability.h
@@ -161,10 +161,47 @@
 #include "absl/base/config.h"
 #include "absl/base/internal/nullability_impl.h"
 
+// ABSL_POINTERS_DEFAULT_NONNULL
+//
+// This macro specifies that all unannotated pointer types within the given
+// file are designated as nonnull (instead of the default "unknown"). This macro
+// exists as a standalone statement and applies default nonnull behavior to all
+// subsequent pointers; as a result, place this macro as the first non-comment,
+// non-`#include` line in a file.
+//
+// Example:
+//
+//     #include "absl/base/nullability.h"
+//
+//     ABSL_POINTERS_DEFAULT_NONNULL
+//
+//     void FillMessage(Message *m);                  // implicitly non-null
+//     absl::Nullable<T*> GetNullablePtr();           // explicitly nullable
+//     absl::NullabilityUnknown<T*> GetUnknownPtr();  // explicitly unknown
+//
+// The macro can be safely used in header files -- it will not affect any files
+// that include it.
+//
+// In files with the macro, plain `T*` syntax means `absl::Nonnull<T*>`, and the
+// exceptions (`Nullable` and `NullabilityUnknown`) must be marked
+// explicitly. The same holds, correspondingly, for smart pointer types.
+//
+// For comparison, without the macro, all unannotated pointers would default to
+// unknown, and otherwise require explicit annotations to change this behavior:
+//
+//     #include "absl/base/nullability.h"
+//
+//     void FillMessage(absl::Nonnull<Message*> m);  // explicitly non-null
+//     absl::Nullable<T*> GetNullablePtr();          // explicitly nullable
+//     T* GetUnknownPtr();                           // implicitly unknown
+//
+// No-op except for being a human readable signal.
+#define ABSL_POINTERS_DEFAULT_NONNULL
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-// absl::Nonnull
+// absl::Nonnull (default with `ABSL_POINTERS_DEFAULT_NONNULL`)
 //
 // The indicated pointer is never null. It is the responsibility of the provider
 // of this pointer across an API boundary to ensure that the pointer is never
@@ -197,7 +234,7 @@
 template <typename T>
 using Nullable = nullability_internal::NullableImpl<T>;
 
-// absl::NullabilityUnknown (default)
+// absl::NullabilityUnknown (default without `ABSL_POINTERS_DEFAULT_NONNULL`)
 //
 // The indicated pointer has not yet been determined to be definitively
 // "non-null" or "nullable." Providers of such pointers across API boundaries
@@ -208,9 +245,10 @@
 // migrated into one of the above two nullability states: `Nonnull<T>` or
 //  `Nullable<T>`.
 //
-// NOTE: Because this annotation is the global default state, unannotated
-// pointers are assumed to have "unknown" semantics. This assumption is designed
-// to minimize churn and reduce clutter within the codebase.
+// NOTE: For files that do not specify `ABSL_POINTERS_DEFAULT_NONNULL`,
+// because this annotation is the global default state, unannotated pointers are
+// are assumed to have "unknown" semantics. This assumption is designed to
+// minimize churn and reduce clutter within the codebase.
 //
 // Example:
 //
diff --git a/third_party/abseil-cpp/absl/base/nullability_default_nonnull_test.cc b/third_party/abseil-cpp/absl/base/nullability_default_nonnull_test.cc
new file mode 100644
index 0000000..bd5b483
--- /dev/null
+++ b/third_party/abseil-cpp/absl/base/nullability_default_nonnull_test.cc
@@ -0,0 +1,44 @@
+// Copyright 2024 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cassert>
+
+#include "gtest/gtest.h"
+#include "absl/base/nullability.h"
+
+ABSL_POINTERS_DEFAULT_NONNULL
+
+namespace {
+
+void FuncWithDefaultNonnullArg(int* /*arg*/) {}
+template <typename T>
+void FuncWithDeducedDefaultNonnullArg(T* /*arg*/) {}
+
+TEST(DefaultNonnullTest, NonnullArgument) {
+  int var = 0;
+  FuncWithDefaultNonnullArg(&var);
+  FuncWithDeducedDefaultNonnullArg<int>(&var);
+}
+
+int* FuncWithDefaultNonnullReturn() {
+  static int var = 0;
+  return &var;
+}
+
+TEST(DefaultNonnullTest, NonnullReturn) {
+  auto var = FuncWithDefaultNonnullReturn();
+  (void)var;
+}
+
+}  // namespace
diff --git a/third_party/abseil-cpp/absl/base/options.h b/third_party/abseil-cpp/absl/base/options.h
index bd43b6e..8978860 100644
--- a/third_party/abseil-cpp/absl/base/options.h
+++ b/third_party/abseil-cpp/absl/base/options.h
@@ -235,7 +235,10 @@
 //
 // A value of 0 means that "hardened" mode is not enabled.
 //
-// A value of 1 means that "hardened" mode is enabled.
+// A value of 1 means that "hardened" mode is enabled with all checks.
+//
+// A value of 2 means that "hardened" mode is partially enabled, with
+// only a subset of checks chosen to minimize performance impact.
 //
 // Hardened builds have additional security checks enabled when `NDEBUG` is
 // defined. Defining `NDEBUG` is normally used to turn `assert()` macro into a
diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
index 711dabe..f5463fb5 100644
--- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
+++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
@@ -545,6 +545,7 @@
   kAboveMaxValidCapacity = ~size_t{} - 100,
   kReentrance,
   kDestroyed,
+  kMovedFrom,
 };
 
 // Returns a pointer to a control byte group that can be used by empty tables.
@@ -2402,6 +2403,10 @@
            alignof(slot_type) <= alignof(HeapOrSoo);
   }
 
+  constexpr static size_t DefaultCapacity() {
+    return SooEnabled() ? SooCapacity() : 0;
+  }
+
   // Whether `size` fits in the SOO capacity of this table.
   bool fits_in_soo(size_t size) const {
     return SooEnabled() && size <= SooCapacity();
@@ -2639,7 +2644,7 @@
       const allocator_type& alloc = allocator_type())
       : settings_(CommonFields::CreateDefault<SooEnabled()>(), hash, eq,
                   alloc) {
-    if (bucket_count > (SooEnabled() ? SooCapacity() : 0)) {
+    if (bucket_count > DefaultCapacity()) {
       resize(NormalizeCapacity(bucket_count));
     }
   }
@@ -2822,7 +2827,7 @@
       transfer(soo_slot(), that.soo_slot());
     }
     that.common() = CommonFields::CreateDefault<SooEnabled()>();
-    maybe_increment_generation_or_rehash_on_move();
+    annotate_for_bug_detection_on_move(that);
   }
 
   raw_hash_set(raw_hash_set&& that, const allocator_type& a)
@@ -2830,7 +2835,7 @@
                   that.eq_ref(), a) {
     if (a == that.alloc_ref()) {
       swap_common(that);
-      maybe_increment_generation_or_rehash_on_move();
+      annotate_for_bug_detection_on_move(that);
     } else {
       move_elements_allocs_unequal(std::move(that));
     }
@@ -2896,15 +2901,19 @@
   size_t size() const { return common().size(); }
   size_t capacity() const {
     const size_t cap = common().capacity();
-    // Compiler complains when using functions in assume so use local variables.
-    ABSL_ATTRIBUTE_UNUSED static constexpr bool kEnabled = SooEnabled();
-    ABSL_ATTRIBUTE_UNUSED static constexpr size_t kCapacity = SooCapacity();
-    ABSL_ASSUME(!kEnabled || cap >= kCapacity);
+    // Compiler complains when using functions in ASSUME so use local variable.
+    ABSL_ATTRIBUTE_UNUSED static constexpr size_t kDefaultCapacity =
+        DefaultCapacity();
+    ABSL_ASSUME(cap >= kDefaultCapacity);
     return cap;
   }
   size_t max_size() const { return (std::numeric_limits<size_t>::max)(); }
 
   ABSL_ATTRIBUTE_REINITIALIZES void clear() {
+    if (SwisstableGenerationsEnabled() &&
+        capacity() == InvalidCapacity::kMovedFrom) {
+      common().set_capacity(DefaultCapacity());
+    }
     AssertNotDebugCapacity();
     // Iterating over this container is O(bucket_count()). When bucket_count()
     // is much greater than size(), iteration becomes prohibitively expensive.
@@ -3586,6 +3595,10 @@
   }
 
   inline void destructor_impl() {
+    if (SwisstableGenerationsEnabled() &&
+        capacity() == InvalidCapacity::kMovedFrom) {
+      return;
+    }
     if (capacity() == 0) return;
     if (is_soo()) {
       if (!empty()) {
@@ -3759,8 +3772,16 @@
     move_common(that_is_full_soo, that.alloc_ref(), common(), std::move(tmp));
   }
 
-  void maybe_increment_generation_or_rehash_on_move() {
-    if (!SwisstableGenerationsEnabled() || capacity() == 0 || is_soo()) {
+  void annotate_for_bug_detection_on_move(
+      ABSL_ATTRIBUTE_UNUSED raw_hash_set& that) {
+    // We only enable moved-from validation when generations are enabled (rather
+    // than using NDEBUG) to avoid issues in which NDEBUG is enabled in some
+    // translation units but not in others.
+    if (SwisstableGenerationsEnabled()) {
+      that.common().set_capacity(InvalidCapacity::kMovedFrom);
+    }
+    if (!SwisstableGenerationsEnabled() || capacity() == DefaultCapacity() ||
+        capacity() > kAboveMaxValidCapacity) {
       return;
     }
     common().increment_generation();
@@ -3782,7 +3803,7 @@
     CopyAlloc(alloc_ref(), that.alloc_ref(),
               std::integral_constant<bool, propagate_alloc>());
     that.common() = CommonFields::CreateDefault<SooEnabled()>();
-    maybe_increment_generation_or_rehash_on_move();
+    annotate_for_bug_detection_on_move(that);
     return *this;
   }
 
@@ -3796,7 +3817,7 @@
     }
     if (!that.is_soo()) that.dealloc();
     that.common() = CommonFields::CreateDefault<SooEnabled()>();
-    maybe_increment_generation_or_rehash_on_move();
+    annotate_for_bug_detection_on_move(that);
     return *this;
   }
 
@@ -3875,27 +3896,30 @@
   // Asserts for correctness that we run on find/find_or_prepare_insert.
   template <class K>
   void AssertOnFind(ABSL_ATTRIBUTE_UNUSED const K& key) {
-#ifdef NDEBUG
-    return;
-#endif
     AssertHashEqConsistent(key);
     AssertNotDebugCapacity();
   }
 
   // Asserts that the capacity is not a sentinel invalid value.
-  // TODO(b/296061262): also add asserts for moved-from state.
   void AssertNotDebugCapacity() const {
     assert(capacity() != InvalidCapacity::kReentrance &&
-           "reentrant container access during element construction/destruction "
+           "Reentrant container access during element construction/destruction "
            "is not allowed.");
     assert(capacity() != InvalidCapacity::kDestroyed &&
-           "use of destroyed hash table.");
+           "Use of destroyed hash table.");
+    if (SwisstableGenerationsEnabled() &&
+        ABSL_PREDICT_FALSE(capacity() == InvalidCapacity::kMovedFrom)) {
+      ABSL_RAW_LOG(FATAL, "Use of moved-from hash table.");
+    }
   }
 
   // Asserts that hash and equal functors provided by the user are consistent,
   // meaning that `eq(k1, k2)` implies `hash(k1)==hash(k2)`.
   template <class K>
   void AssertHashEqConsistent(const K& key) {
+#ifdef NDEBUG
+    return;
+#endif
     // If the hash/eq functors are known to be consistent, then skip validation.
     if (std::is_same<hasher, absl::container_internal::StringHash>::value &&
         std::is_same<key_equal, absl::container_internal::StringEq>::value) {
diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc
index 81397ad..f69fca3 100644
--- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc
+++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc
@@ -2091,6 +2091,10 @@
   t.emplace("a", "b");
   EXPECT_EQ(1, t.size());
   t = std::move(*&t);
+  if (SwisstableGenerationsEnabled()) {
+    // NOLINTNEXTLINE(bugprone-use-after-move)
+    EXPECT_DEATH_IF_SUPPORTED(t.contains("a"), "");
+  }
   // As long as we don't crash, it's fine.
 }
 
@@ -3674,6 +3678,29 @@
 #endif
 }
 
+TEST(Table, MovedFromCallsFail) {
+  if (!SwisstableGenerationsEnabled()) {
+    GTEST_SKIP() << "Moved-from checks only enabled in sanitizer mode.";
+    return;
+  }
+
+  {
+    ABSL_ATTRIBUTE_UNUSED IntTable t1, t2;
+    t1.insert(1);
+    t2 = std::move(t1);
+    // NOLINTNEXTLINE(bugprone-use-after-move)
+    EXPECT_DEATH_IF_SUPPORTED(t1.contains(1), "");
+  }
+  {
+    ABSL_ATTRIBUTE_UNUSED IntTable t1;
+    t1.insert(1);
+    ABSL_ATTRIBUTE_UNUSED IntTable t2(std::move(t1));
+    // NOLINTNEXTLINE(bugprone-use-after-move)
+    EXPECT_DEATH_IF_SUPPORTED(t1.contains(1), "");
+    t1.clear();  // Clearing a moved-from table is allowed.
+  }
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/third_party/abseil-cpp/absl/flags/flag.h b/third_party/abseil-cpp/absl/flags/flag.h
index a8e0e932..b2c6af2 100644
--- a/third_party/abseil-cpp/absl/flags/flag.h
+++ b/third_party/abseil-cpp/absl/flags/flag.h
@@ -290,8 +290,7 @@
 // arguments unchanged (unless of course you actually want to retire the flag
 // type at this time as well).
 //
-// `default_value` is only used as a double check on the type. `explanation` is
-// unused.
+// `default_value` and `explanation` are unused.
 // TODO(rogeeff): replace RETIRED_FLAGS with FLAGS once forward declarations of
 // retired flags are cleaned up.
 #define ABSL_RETIRED_FLAG(type, name, default_value, explanation)      \
diff --git a/third_party/abseil-cpp/absl/hash/internal/low_level_hash.cc b/third_party/abseil-cpp/absl/hash/internal/low_level_hash.cc
index 6dc71cf7..7f9bfdc 100644
--- a/third_party/abseil-cpp/absl/hash/internal/low_level_hash.cc
+++ b/third_party/abseil-cpp/absl/hash/internal/low_level_hash.cc
@@ -33,8 +33,6 @@
 
 uint64_t LowLevelHashLenGt16(const void* data, size_t len, uint64_t seed,
                              const uint64_t salt[5]) {
-  // Prefetch the cacheline that data resides in.
-  PrefetchToLocalCache(data);
   const uint8_t* ptr = static_cast<const uint8_t*>(data);
   uint64_t starting_length = static_cast<uint64_t>(len);
   const uint8_t* last_16_ptr = ptr + starting_length - 16;
diff --git a/third_party/angle b/third_party/angle
index 177d15b..d1a4b0f 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 177d15b3807b2db8df7168f5fef51705bd600592
+Subproject commit d1a4b0ff5b83be6b9a1d9c4f0c1e3f36948713ce
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 12d3b2e..86165ab 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -2293,51 +2293,11 @@
              "SpeculativeServiceWorkerWarmUp",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// If true, do not actually warm-up service workers.
-const base::FeatureParam<bool> kSpeculativeServiceWorkerWarmUpDryRun{
-    &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_dry_run", false};
-
-// If true, warm-up immediately without waiting for load event.
-const base::FeatureParam<bool> kSpeculativeServiceWorkerWarmUpWaitForLoad{
-    &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_wait_for_load", false};
-
-// kSpeculativeServiceWorkerWarmUp observes anchor events such as visibility,
-// pointerover, and pointerdown. These events could be triggered very often. To
-// reduce the frequency of processing, kSpeculativeServiceWorkerWarmUp uses a
-// timer to batch URL candidates together for this amount of duration.
-const base::FeatureParam<base::TimeDelta>
-    kSpeculativeServiceWorkerWarmUpBatchTimer{&kSpeculativeServiceWorkerWarmUp,
-                                              "sw_warm_up_batch_timer",
-                                              base::Milliseconds(300)};
-
-// Similar to 'kSpeculativeServiceWorkerWarmUpBatchTimer`. But this is used for
-// the first batch in the page.
-const base::FeatureParam<base::TimeDelta>
-    kSpeculativeServiceWorkerWarmUpFirstBatchTimer{
-        &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_first_batch_timer",
-        base::Seconds(1)};
-
-// The maximum URL candidate count (batch size) to notify URL candidates
-// from renderer process to browser process. If URL candidate count
-// exceeds batch size, the remaining URL candidate will be sent later.
-const base::FeatureParam<int> kSpeculativeServiceWorkerWarmUpBatchSize{
-    &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_batch_size", 10};
-
 // kSpeculativeServiceWorkerWarmUp warms up service workers up to this max
 // count.
 const base::FeatureParam<int> kSpeculativeServiceWorkerWarmUpMaxCount{
     &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_max_count", 10};
 
-// kSpeculativeServiceWorkerWarmUp enqueues navigation candidate URLs. This is
-// the queue length of the candidate URLs.
-const base::FeatureParam<int> kSpeculativeServiceWorkerWarmUpRequestQueueLength{
-    &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_request_queue_length", 1000};
-
-// kSpeculativeServiceWorkerWarmUp accept requests of navigation candidate URLs.
-// This is the request count limit per document.
-const base::FeatureParam<int> kSpeculativeServiceWorkerWarmUpRequestLimit{
-    &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_request_limit", 1000};
-
 // Duration to keep worker warmed-up.
 const base::FeatureParam<base::TimeDelta>
     kSpeculativeServiceWorkerWarmUpDuration{&kSpeculativeServiceWorkerWarmUp,
@@ -2352,12 +2312,6 @@
 const base::FeatureParam<bool> kSpeculativeServiceWorkerWarmUpOnPointerdown{
     &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_on_pointerdown", true};
 
-// Warms up service workers from bookmarks, omnibox, etc.
-const base::FeatureParam<bool>
-    kSpeculativeServiceWorkerWarmUpFromLoadingPredictor{
-        &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_from_loading_predictor",
-        true};
-
 // Warms up service worker after service worker is stopped on idle timeout.
 const base::FeatureParam<bool> kSpeculativeServiceWorkerWarmUpOnIdleTimeout{
     &kSpeculativeServiceWorkerWarmUp, "sw_warm_up_on_idle_timeout", true};
diff --git a/third_party/blink/common/media/watch_time_reporter.cc b/third_party/blink/common/media/watch_time_reporter.cc
index 1a2b40ca..a937c1e 100644
--- a/third_party/blink/common/media/watch_time_reporter.cc
+++ b/third_party/blink/common/media/watch_time_reporter.cc
@@ -21,9 +21,8 @@
 constexpr gfx::Size kMinimumVideoSize = gfx::Size(200, 140);
 
 static bool IsOnBatteryPower() {
-  if (base::PowerMonitor::IsInitialized())
-    return base::PowerMonitor::IsOnBatteryPower();
-  return false;
+  auto* power_monitor = base::PowerMonitor::GetInstance();
+  return power_monitor->IsInitialized() && power_monitor->IsOnBatteryPower();
 }
 
 // Helper function for managing property changes. If the watch time timer is
@@ -96,7 +95,7 @@
   if (is_muted_)
     DCHECK_EQ(volume_, 1.0);
 
-  base::PowerMonitor::AddPowerStateObserver(this);
+  base::PowerMonitor::GetInstance()->AddPowerStateObserver(this);
 
   provider->AcquireWatchTimeRecorder(properties_->Clone(),
                                      recorder_.BindNewPipeAndPassReceiver());
@@ -146,7 +145,7 @@
   // This is our last chance, so finalize now if there's anything remaining.
   in_shutdown_ = true;
   MaybeFinalizeWatchTime(FinalizeTime::IMMEDIATELY);
-  base::PowerMonitor::RemovePowerStateObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerStateObserver(this);
 }
 
 void WatchTimeReporter::OnPlaying() {
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index d124a9f9..3cd44977 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -1618,22 +1618,8 @@
 
 // TODO(crbug/1431792): Speculatively warm-up service worker.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kSpeculativeServiceWorkerWarmUp);
-BLINK_COMMON_EXPORT extern const base::FeatureParam<bool>
-    kSpeculativeServiceWorkerWarmUpDryRun;
-BLINK_COMMON_EXPORT extern const base::FeatureParam<bool>
-    kSpeculativeServiceWorkerWarmUpWaitForLoad;
-BLINK_COMMON_EXPORT extern const base::FeatureParam<base::TimeDelta>
-    kSpeculativeServiceWorkerWarmUpBatchTimer;
-BLINK_COMMON_EXPORT extern const base::FeatureParam<base::TimeDelta>
-    kSpeculativeServiceWorkerWarmUpFirstBatchTimer;
-BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
-    kSpeculativeServiceWorkerWarmUpBatchSize;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
     kSpeculativeServiceWorkerWarmUpMaxCount;
-BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
-    kSpeculativeServiceWorkerWarmUpRequestQueueLength;
-BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
-    kSpeculativeServiceWorkerWarmUpRequestLimit;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<base::TimeDelta>
     kSpeculativeServiceWorkerWarmUpDuration;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<bool>
@@ -1641,8 +1627,6 @@
 BLINK_COMMON_EXPORT extern const base::FeatureParam<bool>
     kSpeculativeServiceWorkerWarmUpOnPointerdown;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<bool>
-    kSpeculativeServiceWorkerWarmUpFromLoadingPredictor;
-BLINK_COMMON_EXPORT extern const base::FeatureParam<bool>
     kSpeculativeServiceWorkerWarmUpOnIdleTimeout;
 
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kServiceWorkerStorageSuppressPostTask);
diff --git a/third_party/blink/public/mojom/indexeddb/indexeddb.mojom b/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
index d179c98..3a56e10 100644
--- a/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
+++ b/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
@@ -418,6 +418,12 @@
   // or being blocked by other clients, and disallow the re-activation of
   // the document if so.
   DidBecomeInactive();
+
+  // The IDBDatabase should inherit the priority of the initial connection
+  // request (see IDBFactory::Open) and following transactions will be scheduled
+  // accordingly. This method is called if the client's priority changes and
+  // will impact future transactions on the connection.
+  UpdatePriority(int32 new_priority);
 };
 
 interface IDBFactory {
@@ -431,12 +437,16 @@
   // to the transaction number for this connection's context.
   // Posts request events and results to |client| and database-level
   // events and results to |database_callbacks|.
+  // `priority` represents the scheduling priority of the open request; lower is
+  // higher priority. This can determine relative execution order for open
+  // requests from different clients, but requests from a single client (window,
+  // worker, etc) will always execute in order.
   Open(pending_associated_remote<IDBFactoryClient> client,
        pending_associated_remote<IDBDatabaseCallbacks> database_callbacks,
        mojo_base.mojom.String16 name,
        int64 version,
        pending_associated_receiver<IDBTransaction> version_change_transaction_receiver,
-       int64 transaction_id);
+       int64 transaction_id, int32 priority);
 
   // Deletes a database |name| in the frame's origin. If |force_close|
   // is true, any existing connections to that database will be closed
diff --git a/third_party/blink/public/platform/web_media_player_client.h b/third_party/blink/public/platform/web_media_player_client.h
index 0547d7a..1a1904a 100644
--- a/third_party/blink/public/platform/web_media_player_client.h
+++ b/third_party/blink/public/platform/web_media_player_client.h
@@ -47,6 +47,7 @@
 enum class MediaContentType;
 enum class VideoCodec;
 enum class AudioCodec;
+class MediaTrack;
 }  // namespace media
 
 namespace blink {
@@ -94,18 +95,10 @@
   virtual void DurationChanged() = 0;
   virtual void SizeChanged() = 0;
   virtual void SetCcLayer(cc::Layer*) = 0;
-  virtual WebMediaPlayer::TrackId AddAudioTrack(const WebString& id,
-                                                AudioTrackKind,
-                                                const WebString& label,
-                                                const WebString& language,
-                                                bool enabled) = 0;
-  virtual void RemoveAudioTrack(WebMediaPlayer::TrackId) = 0;
-  virtual WebMediaPlayer::TrackId AddVideoTrack(const WebString& id,
-                                                VideoTrackKind,
-                                                const WebString& label,
-                                                const WebString& language,
-                                                bool selected) = 0;
-  virtual void RemoveVideoTrack(WebMediaPlayer::TrackId) = 0;
+
+  virtual void AddMediaTrack(const media::MediaTrack&) = 0;
+  virtual void RemoveMediaTrack(const media::MediaTrack&) = 0;
+
   virtual void MediaSourceOpened(std::unique_ptr<WebMediaSource>) = 0;
   virtual void RemotePlaybackCompatibilityChanged(const WebURL&,
                                                   bool is_compatible) = 0;
diff --git a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
index 675b81a5..af8d4ad 100644
--- a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
+++ b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
@@ -1811,8 +1811,8 @@
                                               v8::Local<v8::Value> value,
                                               ExceptionState& exception_state) {
     typename T::ReturnType result;
-    if (bindings::internal::TypedArrayElementTraits<ElementType>::IsViewOfType(
-            value)) {
+    using Traits = bindings::internal::TypedArrayElementTraits<ElementType>;
+    if (Traits::IsViewOfType(value)) {
       v8::Local<v8::ArrayBufferView> view = value.As<v8::ArrayBufferView>();
       if (!T::allow_shared && view->HasBuffer() &&
           view->Buffer()->GetBackingStore()->IsShared()) {
@@ -1824,6 +1824,14 @@
           bindings::internal::GetViewData(view, result.GetInlineStorage()));
       return result;
     }
+    if constexpr (T::allow_sequence) {
+      auto&& vec = NativeValueTraits<IDLSequence<typename Traits::IDLType>>::
+          ArgumentValue(isolate, argument_index, value, exception_state);
+      if (!exception_state.HadException()) [[likely]] {
+        result.Assign(std::move(vec));
+      }
+      return result;
+    }
     exception_state.ThrowTypeError(
         ExceptionMessages::ArgumentNotOfType(argument_index, "TypedArray"));
     return result;
diff --git a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl_test.cc b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl_test.cc
index 018b2f7..3ee8c0a 100644
--- a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl_test.cc
@@ -684,5 +684,72 @@
   }
 }
 
+template <typename T>
+using PassAsSpanSequence =
+    PassAsSpan<PassAsSpanMarkerBase::Flags::kAllowSequence, T>;
+
+TEST(NativeValueTraitsImplTest, PassAsSpanAllowSequence) {
+  test::TaskEnvironment task_environment;
+  NonThrowableExceptionState exception_state;
+  V8TestingScope scope;
+  {
+    v8::Local<v8::Object> v8_object =
+        EvaluateScriptForObject(scope, "[1, 2, 3, 4]");
+
+    EXPECT_THAT(NativeValueTraits<PassAsSpanSequence<uint8_t>>::ArgumentValue(
+                    scope.GetIsolate(), 0, v8_object, exception_state)
+                    .as_span(),
+                testing::ElementsAre(1, 2, 3, 4));
+    EXPECT_THAT(NativeValueTraits<PassAsSpanSequence<double>>::ArgumentValue(
+                    scope.GetIsolate(), 0, v8_object, exception_state)
+                    .as_span(),
+                testing::ElementsAre(1.0, 2.0, 3.0, 4.0));
+
+    DummyExceptionStateForTesting thrown_exception;
+    EXPECT_THAT(
+        NativeValueTraits<TypedPassAsSpanShared<uint16_t>>::ArgumentValue(
+            scope.GetIsolate(), 0, v8_object, thrown_exception)
+            .as_span(),
+        testing::IsEmpty());
+    EXPECT_TRUE(thrown_exception.HadException());
+  }
+  {
+    v8::Local<v8::Object> v8_iterable = EvaluateScriptForObject(scope, R"(
+        (function*() {
+            yield 1;
+            yield 2;
+            yield 3;
+        })())");
+    EXPECT_THAT(NativeValueTraits<PassAsSpanSequence<uint8_t>>::ArgumentValue(
+                    scope.GetIsolate(), 0, v8_iterable, exception_state)
+                    .as_span(),
+                testing::ElementsAre(1, 2, 3));
+  }
+}
+
+TEST(NativeValueTraitsImplTest, PassAsSpanSequenceOfUnrestricted) {
+  test::TaskEnvironment task_environment;
+  NonThrowableExceptionState exception_state;
+  V8TestingScope scope;
+
+  v8::Local<v8::Object> v8_object =
+      EvaluateScriptForObject(scope, "[1, -Infinity, NaN, Infinity, 42]");
+
+  using testing::Eq;
+  using testing::IsNan;
+  EXPECT_THAT(
+      NativeValueTraits<PassAsSpanSequence<float>>::ArgumentValue(
+          scope.GetIsolate(), 0, v8_object, exception_state)
+          .as_span(),
+      testing::ElementsAre(1, -std::numeric_limits<float>::infinity(), IsNan(),
+                           std::numeric_limits<float>::infinity(), 42));
+  EXPECT_THAT(
+      NativeValueTraits<PassAsSpanSequence<double>>::ArgumentValue(
+          scope.GetIsolate(), 0, v8_object, exception_state)
+          .as_span(),
+      testing::ElementsAre(1, -std::numeric_limits<double>::infinity(), IsNan(),
+                           std::numeric_limits<double>::infinity(), 42));
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/pass_as_span.h b/third_party/blink/renderer/bindings/core/v8/pass_as_span.h
index bc51e55..a64224a 100644
--- a/third_party/blink/renderer/bindings/core/v8/pass_as_span.h
+++ b/third_party/blink/renderer/bindings/core/v8/pass_as_span.h
@@ -86,33 +86,68 @@
 };
 
 template <typename T>
-struct TypedArrayElementTraits {};
+class SpanOrVector {
+  STACK_ALLOCATED();
 
-#define DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(type, func)      \
-  template <>                                              \
-  struct TypedArrayElementTraits<type> {                   \
-    static bool IsViewOfType(v8::Local<v8::Value> value) { \
-      return value->func();                                \
-    }                                                      \
+ public:
+  SpanOrVector() = default;
+
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  operator base::span<const T>() const& { return as_span(); }
+  operator base::span<const T>() const&& = delete;
+  const base::span<const T> as_span() const { return span_.as_span(); }
+
+  void Assign(base::span<const uint8_t> span) { span_.Assign(span); }
+  void Assign(Vector<T> vec) {
+    vector_ = std::move(vec);
+    span_.Assign(
+        base::make_span(reinterpret_cast<const uint8_t*>(vector_.data()),
+                        vector_.size() * sizeof(T)));
+  }
+  base::span<uint8_t, ByteSpanWithInlineStorage::kInlineStorageSize>
+  GetInlineStorage() {
+    return span_.GetInlineStorage();
   }
 
-DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(int8_t, IsInt8Array);
+ private:
+  SpanWithInlineStorage<T> span_;
+  Vector<T> vector_;
+};
+
+template <typename T>
+struct TypedArrayElementTraits {};
+
+#define DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(type, func, idl_type) \
+  template <>                                                   \
+  struct TypedArrayElementTraits<type> {                        \
+    static bool IsViewOfType(v8::Local<v8::Value> value) {      \
+      return value->func();                                     \
+    }                                                           \
+    using IDLType = idl_type;                                   \
+  }
+
+DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(int8_t, IsInt8Array, IDLByte);
 // Note Uint8 array is special case due to need to account for
 // Uint8 clamped array, so not declared here.
-DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(int16_t, IsInt16Array);
-DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(uint16_t, IsUint16Array);
-DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(int32_t, IsInt32Array);
-DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(uint32_t, IsUint32Array);
-DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(int64_t, IsBigInt64Array);
-DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(uint64_t, IsBigUint64Array);
-DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(float, IsFloat32Array);
-DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(double, IsFloat64Array);
+DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(int16_t, IsInt16Array, IDLShort);
+DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(uint16_t, IsUint16Array, IDLUnsignedShort);
+DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(int32_t, IsInt32Array, IDLLong);
+DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(uint32_t, IsUint32Array, IDLUnsignedLong);
+DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(int64_t, IsBigInt64Array, IDLLongLong);
+DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(uint64_t,
+                                  IsBigUint64Array,
+                                  IDLUnsignedLongLong);
+DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(float, IsFloat32Array, IDLUnrestrictedFloat);
+DEFINE_TYPED_ARRAY_ELEMENT_TRAITS(double,
+                                  IsFloat64Array,
+                                  IDLUnrestrictedDouble);
 
 template <>
 struct TypedArrayElementTraits<uint8_t> {
   static bool IsViewOfType(v8::Local<v8::Value> value) {
     return value->IsUint8Array() || value->IsUint8ClampedArray();
   }
+  using IDLType = IDLOctet;
 };
 
 }  // namespace bindings::internal
@@ -126,6 +161,7 @@
   enum Flags {
     kNone,
     kAllowShared = 1 << 0,
+    kAllowSequence = 1 << 1,
   };
 };
 
@@ -140,12 +176,18 @@
           typename T = void>
 struct PassAsSpan : public PassAsSpanMarkerBase {
   static constexpr bool allow_shared = flags & Flags::kAllowShared;
+  static constexpr bool allow_sequence = flags & Flags::kAllowSequence;
   static constexpr bool is_typed = !std::is_same_v<T, void>;
+
+  static_assert(is_typed || !allow_sequence);
+
   using ElementType = T;
-  using ReturnType =
+  using ReturnType = std::conditional_t<
+      allow_sequence,
+      bindings::internal::SpanOrVector<T>,
       std::conditional_t<is_typed,
                          bindings::internal::SpanWithInlineStorage<T>,
-                         bindings::internal::ByteSpanWithInlineStorage>;
+                         bindings::internal::ByteSpanWithInlineStorage>>;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 64afc55d..0a03ddb 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -772,16 +772,30 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_op_support_limits.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_support_limits.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_support_limits.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_batch_normalization_support_limits.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_batch_normalization_support_limits.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_binary_support_limits.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_binary_support_limits.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_concat_support_limits.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_concat_support_limits.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_conv_2d_support_limits.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_conv_2d_support_limits.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_gather_support_limits.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_gather_support_limits.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_gemm_support_limits.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_gemm_support_limits.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_gru_support_limits.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_gru_support_limits.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_gru_cell_support_limits.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_gru_cell_support_limits.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_logical_not_support_limits.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_logical_not_support_limits.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_lstm_support_limits.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_lstm_support_limits.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_lstm_cell_support_limits.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_lstm_cell_support_limits.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_normalization_support_limits.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_normalization_support_limits.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_prelu_support_limits.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_prelu_support_limits.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ml_single_input_support_limits.cc",
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py b/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py
index 0dbc62b..2438fc3 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py
@@ -217,23 +217,13 @@
 
     real_type = idl_type.unwrap(typedef=True)
 
-    if real_type.is_boolean or real_type.is_numeric:
-        cxx_type = {
-            "boolean": "bool",
-            "byte": "int8_t",
-            "octet": "uint8_t",
-            "short": "int16_t",
-            "unsigned short": "uint16_t",
-            "long": "int32_t",
-            "unsigned long": "uint32_t",
-            "long long": "int64_t",
-            "unsigned long long": "uint64_t",
-            "float": "float",
-            "unrestricted float": "float",
-            "double": "double",
-            "unrestricted double": "double",
-        }
-        return TypeInfo(cxx_type[real_type.keyword_typename],
+    if real_type.is_boolean:
+        return TypeInfo("bool",
+                        const_ref_fmt="{}",
+                        clear_member_var_fmt="{} = false")
+
+    if real_type.is_numeric:
+        return TypeInfo(numeric_type(real_type.keyword_typename),
                         const_ref_fmt="{}",
                         clear_member_var_fmt="{} = 0")
 
@@ -380,6 +370,13 @@
                         is_traceable=True)
 
     if real_type.is_union:
+        if real_type.is_phantom:
+            return TypeInfo("v8::Local<v8::Value>",
+                            ref_fmt="{}*",
+                            value_fmt="{}",
+                            has_null_value=True,
+                            is_gc_type=True)
+
         typename = blink_class_name(real_type.union_definition_object)
         return TypeInfo(typename,
                         member_fmt="Member<{}>",
@@ -432,16 +429,40 @@
     types = real_type.flattened_member_types if real_type.is_union else [
         real_type
     ]
-    is_buffer_source_type = all(t.is_buffer_source_type for t in types)
-    assert is_buffer_source_type, (
-        "PassAsSpan is only supported for buffer source types")
-    native_type = typed_array_element_type(
-        real_type) if real_type.is_typed_array_type else "void"
+    sequence_types = set(
+        map(lambda t: t.element_type.unwrap(typedef=True),
+            filter(lambda t: t.is_sequence, types)))
+    assert len(
+        sequence_types
+    ) < 2, "Unions of sequence types of different types are not supported with [PassAsSpan]"
+    typed_arrays = set(filter(lambda t: t.is_typed_array_type, types))
+    assert len(
+        typed_arrays
+    ) < 2, "Unions of typed arrays of different types are not supported with [PassAsSpan]"
+    native_type = None
+    if typed_arrays:
+        typed_array_type = typed_array_element_type(list(typed_arrays)[0])
+        native_type = numeric_type(typed_array_type)
+        if sequence_types:
+            seq_element_type = list(sequence_types)[0].keyword_typename
+            types_are_compatible = seq_element_type == typed_array_type
+            assert types_are_compatible, "Sequence and typed array types are incompatible (%s vs %s)" % (
+                seq_element_type, typed_array_type)
+    else:
+        assert (not sequence_types
+                ), "Plain sequence<> types are not supported with [PassAsSpan]"
+        native_type = "void"
+        is_buffer_source_type = all(t.is_buffer_source_type for t in types)
+        assert is_buffer_source_type, "All types must be buffer"
+
     flags = []
+    if sequence_types:
+        flags.append("PassAsSpanMarkerBase::Flags::kAllowSequence")
     allow_shared = "AllowShared" in idl_type.effective_annotations or any(
         "AllowShared" in t.effective_annotations for t in types)
     if allow_shared:
         flags.append("PassAsSpanMarkerBase::Flags::kAllowShared")
+
     return [
         " | ".join(flags) or "PassAsSpanMarkerBase::Flags::kNone", native_type
     ]
@@ -916,19 +937,38 @@
 
 
 def typed_array_element_type(idl_type):
-    assert isinstance(idl_type, web_idl.IdlType)
+    assert isinstance(idl_type, web_idl.IdlType), type(idl_type)
     assert idl_type.is_typed_array_type
     element_type_map = {
-        'Int8Array': 'int8_t',
-        'Int16Array': 'int16_t',
-        'Int32Array': 'int32_t',
-        'BigInt64Array': 'int64_t',
-        'Uint8Array': 'uint8_t',
-        'Uint16Array': 'uint16_t',
-        'Uint32Array': 'uint32_t',
-        'BigUint64Array': 'uint64_t',
-        'Uint8ClampedArray': 'uint8_t',
-        'Float32Array': 'float',
-        'Float64Array': 'double',
+        'Int8Array': 'byte',
+        'Int16Array': 'short',
+        'Int32Array': 'long',
+        'BigInt64Array': 'long long',
+        'Uint8Array': 'octet',
+        'Uint16Array': 'unsigned short',
+        'Uint32Array': 'unsigned long',
+        'BigUint64Array': 'unsigned long long',
+        'Uint8ClampedArray': 'octet',
+        'Float32Array': 'unrestricted float',
+        'Float64Array': 'unrestricted double',
     }
     return element_type_map.get(idl_type.keyword_typename)
+
+
+def numeric_type(type_keyword):
+    assert isinstance(type_keyword, str)
+    type_map = {
+        "byte": "int8_t",
+        "octet": "uint8_t",
+        "short": "int16_t",
+        "unsigned short": "uint16_t",
+        "long": "int32_t",
+        "unsigned long": "uint32_t",
+        "long long": "int64_t",
+        "unsigned long long": "uint64_t",
+        "float": "float",
+        "unrestricted float": "float",
+        "double": "double",
+        "unrestricted double": "double",
+    }
+    return type_map[type_keyword]
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 8256ce7c..de48f67b 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -2528,9 +2528,8 @@
 
     def v8_type_and_symbol_node(argument, v8_arg_name, blink_arg_name):
         unwrapped_idl_type = argument.idl_type.unwrap()
-        if unwrapped_idl_type.is_interface or unwrapped_idl_type.is_sequence:
-            return ("v8::Local<v8::Value>" if unwrapped_idl_type.is_interface
-                    else "v8::Local<v8::Array>",
+        if "PassAsSpan" in argument.idl_type.effective_annotations:
+            return ("v8::Local<v8::Value>",
                     make_v8_to_blink_value(
                         blink_arg_name,
                         "${{{}}}".format(v8_arg_name),
@@ -2538,10 +2537,9 @@
                         argument=argument,
                         error_exit_return_statement="return;",
                         cg_context=cg_context))
-        elif unwrapped_idl_type.is_typed_array_type:
-            assert "AllowShared" in argument.idl_type.effective_annotations
-            assert "PassAsSpan" in argument.idl_type.effective_annotations
-            return ("v8::Local<v8::Value>",
+        if unwrapped_idl_type.is_interface or unwrapped_idl_type.is_sequence:
+            return ("v8::Local<v8::Value>" if unwrapped_idl_type.is_interface
+                    else "v8::Local<v8::Array>",
                     make_v8_to_blink_value(
                         blink_arg_name,
                         "${{{}}}".format(v8_arg_name),
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/typedef.py b/third_party/blink/renderer/bindings/scripts/bind_gen/typedef.py
index fc004cc..6150cb2 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/typedef.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/typedef.py
@@ -29,13 +29,15 @@
 
     new_and_old_names = list(
         map(
-            lambda typedef: (blink_class_name(typedef),
-                             blink_class_name(typedef.idl_type.
-                                              union_definition_object)),
+            lambda typedef:
+            (blink_class_name(typedef),
+             blink_class_name(typedef.idl_type.union_definition_object)),
             filter(
                 lambda typedef: component_selector(
                     [typedef, typedef.idl_type.union_definition_object]),
-                filter(lambda typedef: typedef.idl_type.is_union, typedefs))))
+                filter(
+                    lambda typedef: typedef.idl_type.is_union and not typedef.
+                    idl_type.is_phantom, typedefs))))
     node = ListNode([
         TextNode("using {} = {};".format(new_name, old_name))
         for new_name, old_name in new_and_old_names
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
index 2901f340..dc822000d 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
@@ -1027,7 +1027,7 @@
         all_union_types = []  # all instances of UnionType
 
         def collect_unions(idl_type):
-            if idl_type.is_union:
+            if idl_type.is_union and not idl_type.is_phantom:
                 all_union_types.append(idl_type)
 
         self._idl_type_factory.for_each(collect_unions)
@@ -1043,7 +1043,7 @@
 
         all_typedefs = self._db.find_by_kind(DatabaseBody.Kind.TYPEDEF)
         for typedef in all_typedefs.values():
-            if not typedef.idl_type.is_union:
+            if not typedef.idl_type.is_union or typedef.idl_type.is_phantom:
                 continue
             token = Union.unique_token(typedef.idl_type)
             irs[token].typedefs.append(typedef)
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_type.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_type.py
index 8cb68892..1f5024d 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_type.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_type.py
@@ -1340,6 +1340,14 @@
         assert self._union_definition_object is None
         self._union_definition_object = union_definition_object
 
+    @property
+    def is_phantom(self):
+        """Returns True if a class for union should not be generated,
+        as would be the case if enum only exists at the IDL level and
+        is not passed down to implementation. This can happen when all
+        enum variants are coerced to a single type."""
+        return "PassAsSpan" in self.effective_annotations
+
 
 class NullableType(IdlType):
     """https://webidl.spec.whatwg.org/#idl-nullable-type"""
diff --git a/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type.cc
index ca44e860..e7d55cd 100644
--- a/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type.cc
@@ -6,6 +6,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/animation/interpolable_font_palette.h"
+#include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
@@ -68,8 +69,11 @@
     const CSSValue& value,
     const StyleResolverState* state,
     ConversionCheckers& conversion_checkers) const {
-  return ConvertFontPalette(
-      StyleBuilderConverterBase::ConvertFontPalette(value));
+  // TODO(40946458): Don't resolve anything here, rewrite to
+  // interpolate unresolved palettes.
+  return ConvertFontPalette(StyleBuilderConverterBase::ConvertFontPalette(
+      state ? state->CssToLengthConversionData() : CSSToLengthConversionData(),
+      value));
 }
 
 InterpolationValue
diff --git a/third_party/blink/renderer/core/animation/effect_input.cc b/third_party/blink/renderer/core/animation/effect_input.cc
index 1471186..d8ee31da 100644
--- a/third_party/blink/renderer/core/animation/effect_input.cc
+++ b/third_party/blink/renderer/core/animation/effect_input.cc
@@ -143,38 +143,37 @@
     ExceptionState& exception_state) {
   const CSSParserContext* context =
       document.ElementSheet().Contents()->ParserContext();
-  CSSTokenizer tokenizer(css_text);
-  const auto tokens = tokenizer.TokenizeToEOF();
-  CSSParserTokenRange token_range(tokens);
-  token_range.ConsumeWhitespace();
+  CSSParserTokenStream stream(css_text);
+  stream.ConsumeWhitespace();
 
   // <number>
   {
-    CSSParserTokenRange range_copy = token_range;
+    CSSParserTokenStream::State savepoint = stream.Save();
     const CSSPrimitiveValue* primitive = css_parsing_utils::ConsumeNumber(
-        range_copy, *context, CSSPrimitiveValue::ValueRange::kAll);
-    if (primitive && range_copy.AtEnd()) {
+        stream, *context, CSSPrimitiveValue::ValueRange::kAll);
+    if (primitive && stream.AtEnd()) {
       return ParsedOffset(
           {TimelineOffset::NamedRange::kNone, primitive->GetValue<double>()});
     }
+    stream.Restore(savepoint);
   }
 
   // <percent>
   {
-    CSSParserTokenRange range_copy = token_range;
+    CSSParserTokenStream::State savepoint = stream.Save();
     const CSSPrimitiveValue* primitive = css_parsing_utils::ConsumePercent(
-        range_copy, *context, CSSPrimitiveValue::ValueRange::kAll);
-    if (primitive && range_copy.AtEnd()) {
+        stream, *context, CSSPrimitiveValue::ValueRange::kAll);
+    if (primitive && stream.AtEnd()) {
       return ParsedOffset({TimelineOffset::NamedRange::kNone,
                            primitive->GetValue<double>() / 100});
     }
+    stream.Restore(savepoint);
   }
 
   // <range-name> <percent>
-  auto* range_name_percent =
-      To<CSSValueList>(css_parsing_utils::ConsumeTimelineRangeNameAndPercent(
-          token_range, *context));
-  if (!range_name_percent || !token_range.AtEnd()) {
+  auto* range_name_percent = To<CSSValueList>(
+      css_parsing_utils::ConsumeTimelineRangeNameAndPercent(stream, *context));
+  if (!range_name_percent || !stream.AtEnd()) {
     exception_state.ThrowTypeError(
         "timeline offset must be of the form [timeline-range-name] "
         "<percentage>");
diff --git a/third_party/blink/renderer/core/animation/timeline_offset.cc b/third_party/blink/renderer/core/animation/timeline_offset.cc
index ac96872..4648f95 100644
--- a/third_party/blink/renderer/core/animation/timeline_offset.cc
+++ b/third_party/blink/renderer/core/animation/timeline_offset.cc
@@ -91,8 +91,7 @@
 
   Document& document = element->GetDocument();
 
-  CSSTokenizer tokenizer(css_text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(css_text);
   stream.ConsumeWhitespace();
 
   const CSSValue* value = css_parsing_utils::ConsumeAnimationRange(
@@ -237,8 +236,7 @@
     return nullptr;
   }
 
-  CSSTokenizer tokenizer(css_text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(css_text);
   stream.ConsumeWhitespace();
 
   CSSValue* value = css_parsing_utils::ConsumeLengthOrPercent(
diff --git a/third_party/blink/renderer/core/css/css_color_mix_value.cc b/third_party/blink/renderer/core/css/css_color_mix_value.cc
index fd1f222..313fce94 100644
--- a/third_party/blink/renderer/core/css/css_color_mix_value.cc
+++ b/third_party/blink/renderer/core/css/css_color_mix_value.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/css_color_mix_value.h"
 
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
+#include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
@@ -14,14 +15,19 @@
     const CSSPrimitiveValue* percentage1,
     const CSSPrimitiveValue* percentage2,
     double& mix_amount,
-    double& alpha_multiplier) {
+    double& alpha_multiplier,
+    const CSSLengthResolver& length_resolver) {
   double p1 = 0.5;
   if (percentage1) {
-    p1 = ClampTo<double>(percentage1->GetDoubleValue(), 0.0, 100.0) / 100.0;
+    p1 = ClampTo<double>(percentage1->ComputePercentage(length_resolver), 0.0,
+                         100.0) /
+         100.0;
   }
   double p2 = 0.5;
   if (percentage2) {
-    p2 = ClampTo<double>(percentage2->GetDoubleValue(), 0.0, 100.0) / 100.0;
+    p2 = ClampTo<double>(percentage2->ComputePercentage(length_resolver), 0.0,
+                         100.0) /
+         100.0;
   }
 
   if (percentage1 && !percentage2) {
@@ -54,9 +60,14 @@
 }
 
 Color CSSColorMixValue::Mix(const Color& color1, const Color& color2) const {
+  CHECK(!Percentage1() || Percentage1()->IsNumericLiteralValue());
+  CHECK(!Percentage2() || Percentage2()->IsNumericLiteralValue());
   double alpha_multiplier;
   double mix_amount;
-  if (!NormalizePercentages(mix_amount, alpha_multiplier)) {
+  // Using default length resolver here as we only call it with numeric %, no
+  // calc().
+  if (!NormalizePercentages(mix_amount, alpha_multiplier,
+                            CSSToLengthConversionData())) {
     return Color();
   }
   return Color::FromColorMix(ColorInterpolationSpace(),
diff --git a/third_party/blink/renderer/core/css/css_color_mix_value.h b/third_party/blink/renderer/core/css/css_color_mix_value.h
index 0def2be..d468d25 100644
--- a/third_party/blink/renderer/core/css/css_color_mix_value.h
+++ b/third_party/blink/renderer/core/css/css_color_mix_value.h
@@ -58,11 +58,13 @@
   static bool NormalizePercentages(const CSSPrimitiveValue* percentage1,
                                    const CSSPrimitiveValue* percentage2,
                                    double& mix_amount,
-                                   double& alpha_multiplier);
+                                   double& alpha_multiplier,
+                                   const CSSLengthResolver& length_resolver);
   bool NormalizePercentages(double& mix_amount,
-                            double& alpha_multiplier) const {
+                            double& alpha_multiplier,
+                            const CSSLengthResolver& length_resolver) const {
     return NormalizePercentages(Percentage1(), Percentage2(), mix_amount,
-                                alpha_multiplier);
+                                alpha_multiplier, length_resolver);
   }
 
  private:
diff --git a/third_party/blink/renderer/core/css/css_counter_style_rule.cc b/third_party/blink/renderer/core/css/css_counter_style_rule.cc
index 4e0c5d3..1ec218a2 100644
--- a/third_party/blink/renderer/core/css/css_counter_style_rule.cc
+++ b/third_party/blink/renderer/core/css/css_counter_style_rule.cc
@@ -192,8 +192,7 @@
   CSSStyleSheet* style_sheet = parentStyleSheet();
   auto& context = *MakeGarbageCollected<CSSParserContext>(
       ParserContext(execution_context->GetSecureContextMode()), style_sheet);
-  CSSTokenizer tokenizer(text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(text);
   CSSValue* new_value = AtRuleDescriptorParser::ParseAtCounterStyleDescriptor(
       descriptor_id, stream, context);
   if (!new_value ||
@@ -217,8 +216,7 @@
   CSSStyleSheet* style_sheet = parentStyleSheet();
   auto& context = *MakeGarbageCollected<CSSParserContext>(
       ParserContext(execution_context->GetSecureContextMode()), style_sheet);
-  CSSTokenizer tokenizer(text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(text);
   AtomicString name =
       css_parsing_utils::ConsumeCounterStyleNameInPrelude(stream, context);
   if (!name || name == counter_style_rule_->GetName() || !stream.AtEnd()) {
diff --git a/third_party/blink/renderer/core/css/css_gradient_value.cc b/third_party/blink/renderer/core/css/css_gradient_value.cc
index f5b85f1..7ecfaf0 100644
--- a/third_party/blink/renderer/core/css/css_gradient_value.cc
+++ b/third_party/blink/renderer/core/css/css_gradient_value.cc
@@ -365,14 +365,15 @@
   }
 }
 
-static Color ResolveStopColor(const CSSValue& stop_color,
+static Color ResolveStopColor(const CSSLengthResolver& length_resolver,
+                              const CSSValue& stop_color,
                               const Document& document,
                               const ComputedStyle& style) {
   mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
-  const StyleColor style_stop_color =
-      ResolveColorValue(stop_color, document.GetTextLinkColors(), color_scheme,
-                        document.GetColorProviderForPainting(color_scheme),
-                        document.IsInWebAppScope());
+  const StyleColor style_stop_color = ResolveColorValue(
+      length_resolver, stop_color, document.GetTextLinkColors(), color_scheme,
+      document.GetColorProviderForPainting(color_scheme),
+      document.IsInWebAppScope());
   return style_stop_color.Resolve(
       style.VisitedDependentColor(GetCSSPropertyColor()), color_scheme);
 }
@@ -404,7 +405,8 @@
       offset = stop.offset_->ComputeNumber(conversion_data);
     }
 
-    const Color color = ResolveStopColor(*stop.color_, document, style);
+    const Color color =
+        ResolveStopColor(conversion_data, *stop.color_, document, style);
     desc.stops.emplace_back(offset, color);
   }
 }
@@ -419,8 +421,10 @@
   // TODO(crbug.com/40229450): Need to pass an appropriate boolean to say if it
   // is within webapp scope.
   const mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
+  // TODO(40946458): Don't use default length resolver here!
   const StyleColor style_stop_color =
-      ResolveColorValue(color, TextLinkColors(), color_scheme, nullptr,
+      ResolveColorValue(CSSToLengthConversionData(), color, TextLinkColors(),
+                        color_scheme, nullptr,
                         /*is_in_web_app_scope=*/false);
   const Color current_color =
       style.VisitedDependentColor(GetCSSPropertyColor());
@@ -641,7 +645,8 @@
     if (stop.IsHint()) {
       has_hints = true;
     } else {
-      stops[i].color = ResolveStopColor(*stop.color_, document, style);
+      stops[i].color =
+          ResolveStopColor(conversion_data, *stop.color_, document, style);
     }
 
     if (stop.offset_) {
@@ -873,8 +878,10 @@
 bool CSSGradientValue::KnownToBeOpaque(const Document& document,
                                        const ComputedStyle& style) const {
   for (auto& stop : stops_) {
-    if (!stop.IsHint() &&
-        !ResolveStopColor(*stop.color_, document, style).IsOpaque()) {
+    // TODO(40946458): Don't use default length resolver here!
+    if (!stop.IsHint() && !ResolveStopColor(CSSToLengthConversionData(),
+                                            *stop.color_, document, style)
+                               .IsOpaque()) {
       return false;
     }
   }
@@ -910,7 +917,9 @@
   Vector<Color> stop_colors;
   for (const auto& stop : stops_) {
     if (!stop.IsHint()) {
-      stop_colors.push_back(ResolveStopColor(*stop.color_, document, style));
+      // TODO(40946458): Don't use default length resolver here!
+      stop_colors.push_back(ResolveStopColor(CSSToLengthConversionData(),
+                                             *stop.color_, document, style));
     }
   }
   return stop_colors;
@@ -1892,7 +1901,9 @@
 bool CSSConstantGradientValue::KnownToBeOpaque(
     const Document& document,
     const ComputedStyle& style) const {
-  return ResolveStopColor(*color_, document, style).IsOpaque();
+  // TODO(40946458): Don't use default length resolver here!
+  return ResolveStopColor(CSSToLengthConversionData(), *color_, document, style)
+      .IsOpaque();
 }
 
 scoped_refptr<Gradient> CSSConstantGradientValue::CreateGradient(
@@ -1903,7 +1914,8 @@
   DCHECK(!size.IsEmpty());
 
   GradientDesc desc({0.0f, 0.0f}, {1.0f, 1.0f}, kSpreadMethodPad);
-  const Color color = ResolveStopColor(*color_, document, style);
+  const Color color =
+      ResolveStopColor(conversion_data, *color_, document, style);
   desc.stops.emplace_back(0.0f, color);
   desc.stops.emplace_back(1.0f, color);
 
diff --git a/third_party/blink/renderer/core/css/css_syntax_definition.cc b/third_party/blink/renderer/core/css/css_syntax_definition.cc
index 623343f1..bd83f04 100644
--- a/third_party/blink/renderer/core/css/css_syntax_definition.cc
+++ b/third_party/blink/renderer/core/css/css_syntax_definition.cc
@@ -127,8 +127,7 @@
                                                         is_animation_tainted);
   }
   for (const CSSSyntaxComponent& component : syntax_components_) {
-    CSSTokenizer tokenizer(text);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(text);
     stream.ConsumeWhitespace();
     if (const CSSValue* result =
             ConsumeSyntaxComponent(component, stream, context)) {
diff --git a/third_party/blink/renderer/core/css/css_test_helpers.cc b/third_party/blink/renderer/core/css/css_test_helpers.cc
index d59df886..7d6272f 100644
--- a/third_party/blink/renderer/core/css/css_test_helpers.cc
+++ b/third_party/blink/renderer/core/css/css_test_helpers.cc
@@ -202,8 +202,7 @@
   const auto* context = MakeGarbageCollected<CSSParserContext>(document);
   CSSParserLocalContext local_context;
 
-  CSSTokenizer tokenizer(value);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(value);
   return longhand->ParseSingleValue(stream, *context, local_context);
 }
 
@@ -246,8 +245,7 @@
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
   auto* sheet = MakeGarbageCollected<StyleSheetContents>(context);
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   HeapVector<CSSSelector> arena;
   base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
       stream, context, nesting_type, parent_rule_for_nesting, is_within_scope,
diff --git a/third_party/blink/renderer/core/css/css_variable_data.cc b/third_party/blink/renderer/core/css/css_variable_data.cc
index 56e733f..0b812d6 100644
--- a/third_party/blink/renderer/core/css/css_variable_data.cc
+++ b/third_party/blink/renderer/core/css/css_variable_data.cc
@@ -73,8 +73,7 @@
   bool has_font_units = false;
   bool has_root_font_units = false;
   bool has_line_height_units = false;
-  CSSTokenizer tokenizer(original_text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(original_text);
   while (!stream.AtEnd()) {
     ExtractFeatures(stream.ConsumeRaw(), has_font_units, has_root_font_units,
                     has_line_height_units);
@@ -97,8 +96,7 @@
     serialized_text.Append(OriginalText());
     serialized_text.Resize(serialized_text.length() - 1);
 
-    CSSTokenizer tokenizer(OriginalText());
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(OriginalText());
     CSSParserTokenType last_token_type = kEOFToken;
     for (;;) {
       CSSParserTokenType token_type = stream.ConsumeRaw().GetType();
diff --git a/third_party/blink/renderer/core/css/cssom/css_color_value.cc b/third_party/blink/renderer/core/css/cssom/css_color_value.cc
index 360aa6c..c3f9eb3 100644
--- a/third_party/blink/renderer/core/css/cssom/css_color_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_color_value.cc
@@ -110,8 +110,7 @@
     const ExecutionContext* execution_context,
     const String& css_text,
     ExceptionState& exception_state) {
-  CSSTokenizer tokenizer(css_text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(css_text);
   stream.ConsumeWhitespace();
 
   const CSSColorType color_type = DetermineColorType(stream);
diff --git a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
index 74404ff..601b4dea 100644
--- a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
@@ -283,8 +283,7 @@
     const ExecutionContext* execution_context,
     const String& css_text,
     ExceptionState& exception_state) {
-  CSSTokenizer tokenizer(css_text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(css_text);
   stream.ConsumeWhitespace();
   auto range = stream.ConsumeUntilPeekedTypeIs<>();
   stream.ConsumeWhitespace();
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
index e3388fa..5ec791c 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
@@ -90,8 +90,7 @@
 
 CSSUnparsedValue* CSSUnparsedValue::FromCSSVariableData(
     const CSSVariableData& value) {
-  CSSTokenizer tokenizer(value.OriginalText());
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(value.OriginalText());
   return CSSUnparsedValue::Create(ParserTokenStreamToTokens(stream));
 }
 
diff --git a/third_party/blink/renderer/core/css/cssom/style_value_factory.cc b/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
index ce9d32d..7f5e48a 100644
--- a/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
+++ b/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
@@ -304,8 +304,7 @@
   DCHECK_NE(property_id, CSSPropertyID::kInvalid);
   DCHECK_EQ(property_id == CSSPropertyID::kVariable,
             !custom_property_name.IsNull());
-  CSSTokenizer tokenizer(css_text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(css_text);
   stream.EnsureLookAhead();
   CSSParserTokenStream::State savepoint = stream.Save();
 
diff --git a/third_party/blink/renderer/core/css/font_size_functions.cc b/third_party/blink/renderer/core/css/font_size_functions.cc
index 6b65585..c149885 100644
--- a/third_party/blink/renderer/core/css/font_size_functions.cc
+++ b/third_party/blink/renderer/core/css/font_size_functions.cc
@@ -271,18 +271,12 @@
     return std::nullopt;
   }
 
-  double aspect_value =
+  float aspect_value =
       AspectValue(*font_data, size_adjust.GetMetric(), computed_size);
   if (!aspect_value) {
     return std::nullopt;
   }
-
-  double adjusted_size = (size_adjust.Value() / aspect_value) * computed_size;
-  // Depending on font configurations, the retrieved aspect_value may slightly
-  // differ, causing a few pixels' variation from the expected result.
-  // We correct this by rounding the adjusted size to five decimal places.
-  // See https://crbug.com/346773492
-  return static_cast<float>(std::round(adjusted_size * 100000) / 100000.f);
+  return (size_adjust.Value() / aspect_value) * computed_size;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/media_feature_overrides.cc b/third_party/blink/renderer/core/css/media_feature_overrides.cc
index 77502ae..e216df9 100644
--- a/third_party/blink/renderer/core/css/media_feature_overrides.cc
+++ b/third_party/blink/renderer/core/css/media_feature_overrides.cc
@@ -8,7 +8,7 @@
 #include "third_party/blink/renderer/core/css/media_query_exp.h"
 #include "third_party/blink/renderer/core/css/media_values.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
-#include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/platform/graphics/color_space_gamut.h"
@@ -91,10 +91,7 @@
     const AtomicString& feature,
     const String& value_string,
     const Document* document) {
-  CSSTokenizer tokenizer(value_string);
-  auto [tokens, raw_offsets] = tokenizer.TokenizeToEOFWithOffsets();
-  CSSParserTokenRange range(tokens);
-  CSSParserTokenOffsets offsets(tokens, std::move(raw_offsets), value_string);
+  CSSParserTokenStream stream(value_string);
 
   // TODO(xiaochengh): This is a fake CSSParserContext that only passes
   // down the CSSParserMode. Plumb the real CSSParserContext through, so that
@@ -111,7 +108,7 @@
   // Document to get the ExecutionContext so the extra parameter should be
   // removed.
   MediaQueryExpBounds bounds =
-      MediaQueryExp::Create(feature, range, offsets, *fake_context).Bounds();
+      MediaQueryExp::Create(feature, stream, *fake_context).Bounds();
   DCHECK(!bounds.left.IsValid());
   return bounds.right.value;
 }
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
index 656c2b2a..d792b23 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/css/media_list.h"
 #include "third_party/blink/renderer/core/css/media_values.h"
 #include "third_party/blink/renderer/core/css/media_values_cached.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/css/parser/media_query_parser.h"
 #include "third_party/blink/renderer/core/css/resolver/media_query_result.h"
@@ -518,12 +519,9 @@
       query_set = MediaQuerySet::Create();
     } else {
       StringView str(test_cases[i].input);
-      CSSTokenizer tokenizer(StringView(test_cases[i].input));
-      auto [tokens, offsets] = tokenizer.TokenizeToEOFWithOffsets();
-      query_set = MediaQueryParser::ParseMediaQuerySetInMode(
-          CSSParserTokenRange(tokens),
-          CSSParserTokenOffsets(tokens, std::move(offsets), str), mode,
-          nullptr);
+      CSSParserTokenStream stream(str);
+      query_set =
+          MediaQueryParser::ParseMediaQuerySetInMode(stream, mode, nullptr);
     }
     EXPECT_EQ(test_cases[i].output, media_query_evaluator->Eval(*query_set))
         << "Query: " << test_cases[i].input;
@@ -1573,7 +1571,7 @@
   UpdateAllLifecyclePhases();
   EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
       static_cast<int>(IdentifiableSurface::MediaFeatureName::kOrientation)));
-  EXPECT_EQ(collector()->entries().size(), 1u);
+  ASSERT_EQ(collector()->entries().size(), 1u);
 
   auto& entry = collector()->entries().front();
   EXPECT_EQ(entry.metrics.size(), 1u);
diff --git a/third_party/blink/renderer/core/css/media_query_exp.cc b/third_party/blink/renderer/core/css/media_query_exp.cc
index bc9337c..46232a3d 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.cc
+++ b/third_party/blink/renderer/core/css/media_query_exp.cc
@@ -407,12 +407,10 @@
     : media_feature_(media_feature), bounds_(bounds) {}
 
 MediaQueryExp MediaQueryExp::Create(const String& media_feature,
-                                    CSSParserTokenRange& range,
-                                    const CSSParserTokenOffsets& offsets,
+                                    CSSParserTokenStream& stream,
                                     const CSSParserContext& context) {
   String feature = AttemptStaticStringCreation(media_feature);
-  if (auto value =
-          MediaQueryExpValue::Consume(feature, range, offsets, context)) {
+  if (auto value = MediaQueryExpValue::Consume(feature, stream, context)) {
     return MediaQueryExp(feature, *value);
   }
   return Invalid();
@@ -420,8 +418,7 @@
 
 std::optional<MediaQueryExpValue> MediaQueryExpValue::Consume(
     const String& media_feature,
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     const CSSParserContext& context) {
   CSSParserContext::ParserModeOverridingScope scope(context, kHTMLStandardMode);
 
@@ -430,16 +427,11 @@
     // (These look like a declaration, but are really a test as part of
     // a media query expression.) !important, if present, is stripped
     // and ignored.
-    base::span span = range.RemainingSpan();
-    StringView original_string =
-        offsets.StringForTokens(span.data(), span.data() + span.size());
-    CSSTokenizer tokenizer(original_string);
-    CSSParserTokenStream stream(tokenizer);
     if (const CSSValue* value =
             CSSVariableParser::ParseDeclarationIncludingCSSWide(stream, false,
                                                                 context)) {
-      while (!range.AtEnd()) {
-        range.Consume();
+      while (!stream.AtEnd()) {
+        stream.Consume();
       }
       return MediaQueryExpValue(*value);
     }
@@ -451,21 +443,21 @@
          "queries are currently the only case sensitive features";
 
   CSSPrimitiveValue* value = css_parsing_utils::ConsumeInteger(
-      range, context, -std::numeric_limits<double>::max() /* minimum_value */);
+      stream, context, -std::numeric_limits<double>::max() /* minimum_value */);
   if (!value && !FeatureExpectingInteger(media_feature, context)) {
     value = css_parsing_utils::ConsumeNumber(
-        range, context, CSSPrimitiveValue::ValueRange::kAll);
+        stream, context, CSSPrimitiveValue::ValueRange::kAll);
   }
   if (!value) {
     value = css_parsing_utils::ConsumeLength(
-        range, context, CSSPrimitiveValue::ValueRange::kAll);
+        stream, context, CSSPrimitiveValue::ValueRange::kAll);
   }
   if (!value) {
-    value = css_parsing_utils::ConsumeResolution(range, context);
+    value = css_parsing_utils::ConsumeResolution(stream, context);
   }
 
   if (!value) {
-    if (CSSIdentifierValue* ident = css_parsing_utils::ConsumeIdent(range)) {
+    if (CSSIdentifierValue* ident = css_parsing_utils::ConsumeIdent(stream)) {
       CSSValueID ident_id = ident->GetValueID();
       if (!FeatureWithValidIdent(media_feature, ident_id, context)) {
         return std::nullopt;
@@ -481,13 +473,13 @@
     if (value->IsNegative() == CSSPrimitiveValue::BoolStatus::kTrue) {
       return std::nullopt;
     }
-    if (!css_parsing_utils::ConsumeSlashIncludingWhitespace(range)) {
+    if (!css_parsing_utils::ConsumeSlashIncludingWhitespace(stream)) {
       return MediaQueryExpValue(*value,
                                 *CSSNumericLiteralValue::Create(
                                     1, CSSPrimitiveValue::UnitType::kNumber));
     }
     CSSPrimitiveValue* denominator = css_parsing_utils::ConsumeNumber(
-        range, context, CSSPrimitiveValue::ValueRange::kNonNegative);
+        stream, context, CSSPrimitiveValue::ValueRange::kNonNegative);
     if (!denominator) {
       return std::nullopt;
     }
diff --git a/third_party/blink/renderer/core/css/media_query_exp.h b/third_party/blink/renderer/core/css/media_query_exp.h
index cff58631..20d9913 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.h
+++ b/third_party/blink/renderer/core/css/media_query_exp.h
@@ -50,8 +50,7 @@
 namespace blink {
 
 class CSSParserContext;
-class CSSParserTokenRange;
-class CSSParserTokenOffsets;
+class CSSParserTokenStream;
 
 class CORE_EXPORT MediaQueryExpValue {
   DISALLOW_NEW();
@@ -181,8 +180,7 @@
   // std::nullopt is returned on errors.
   static std::optional<MediaQueryExpValue> Consume(
       const String& lower_media_feature,
-      CSSParserTokenRange&,
-      const CSSParserTokenOffsets&,
+      CSSParserTokenStream&,
       const CSSParserContext&);
 
  private:
@@ -286,8 +284,7 @@
  public:
   // Returns an invalid MediaQueryExp if the arguments are invalid.
   static MediaQueryExp Create(const String& media_feature,
-                              CSSParserTokenRange&,
-                              const CSSParserTokenOffsets&,
+                              CSSParserTokenStream&,
                               const CSSParserContext&);
   static MediaQueryExp Create(const String& media_feature,
                               const MediaQueryExpBounds&);
diff --git a/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc b/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc
index 6a80c54..49d3ca50 100644
--- a/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.cc
@@ -404,8 +404,7 @@
     AtRuleDescriptorID id,
     StringView string,
     const CSSParserContext& context) {
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   return ParseFontFaceDescriptor(id, stream, context);
 }
 
diff --git a/third_party/blink/renderer/core/css/parser/container_query_parser.cc b/third_party/blink/renderer/core/css/parser/container_query_parser.cc
index 26b7a1e..196cfb7 100644
--- a/third_party/blink/renderer/core/css/parser/container_query_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/container_query_parser.cc
@@ -30,20 +30,20 @@
 // https://drafts.csswg.org/css-contain-3/#typedef-container-condition
 template <typename Func>
 const MediaQueryExpNode* ConsumeNotAndOr(Func func,
-                                         CSSParserTokenRange& range) {
-  if (ConsumeIfIdent(range, "not")) {
-    return MediaQueryExpNode::Not(func(range));
+                                         CSSParserTokenStream& stream) {
+  if (ConsumeIfIdent(stream, "not")) {
+    return MediaQueryExpNode::Not(func(stream));
   }
 
-  const MediaQueryExpNode* result = func(range);
+  const MediaQueryExpNode* result = func(stream);
 
-  if (AtIdent(range.Peek(), "and")) {
-    while (result && ConsumeIfIdent(range, "and")) {
-      result = MediaQueryExpNode::And(result, func(range));
+  if (AtIdent(stream.Peek(), "and")) {
+    while (result && ConsumeIfIdent(stream, "and")) {
+      result = MediaQueryExpNode::And(result, func(stream));
     }
-  } else if (AtIdent(range.Peek(), "or")) {
-    while (ConsumeIfIdent(range, "or")) {
-      result = MediaQueryExpNode::Or(result, func(range));
+  } else if (AtIdent(stream.Peek(), "or")) {
+    while (ConsumeIfIdent(stream, "or")) {
+      result = MediaQueryExpNode::Or(result, func(stream));
     }
   }
 
@@ -132,110 +132,108 @@
                           MediaQueryParser::SyntaxLevel::kLevel4) {}
 
 const MediaQueryExpNode* ContainerQueryParser::ParseCondition(String value) {
-  CSSTokenizer tokenizer(value);
-  auto [tokens, raw_offsets] = tokenizer.TokenizeToEOFWithOffsets();
-  CSSParserTokenRange range(tokens);
-  CSSParserTokenOffsets offsets(tokens, std::move(raw_offsets), value);
-  return ParseCondition(range, offsets);
-}
-
-const MediaQueryExpNode* ContainerQueryParser::ParseCondition(
-    CSSParserTokenRange range,
-    const CSSParserTokenOffsets& offsets) {
-  range.ConsumeWhitespace();
-  const MediaQueryExpNode* node = ConsumeContainerCondition(range, offsets);
-  if (!range.AtEnd()) {
+  CSSParserTokenStream stream(value);
+  const MediaQueryExpNode* node = ParseCondition(stream);
+  if (!stream.AtEnd()) {
     return nullptr;
   }
   return node;
 }
 
+const MediaQueryExpNode* ContainerQueryParser::ParseCondition(
+    CSSParserTokenStream& stream) {
+  stream.ConsumeWhitespace();
+  return ConsumeContainerCondition(stream);
+}
+
 // <query-in-parens> = ( <container-condition> )
 //                   | ( <size-feature> )
 //                   | style( <style-query> )
 //                   | <general-enclosed>
 const MediaQueryExpNode* ContainerQueryParser::ConsumeQueryInParens(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets) {
-  CSSParserTokenRange original_range = range;
+    CSSParserTokenStream& stream) {
+  CSSParserTokenStream::State savepoint = stream.Save();
 
-  if (range.Peek().GetType() == kLeftParenthesisToken) {
+  if (stream.Peek().GetType() == kLeftParenthesisToken) {
     // ( <size-feature> ) | ( <container-condition> )
-    CSSParserTokenRange block = range.ConsumeBlock();
-    block.ConsumeWhitespace();
-    range.ConsumeWhitespace();
-
-    CSSParserTokenRange original_block = block;
-    // <size-feature>
-    const MediaQueryExpNode* query =
-        ConsumeFeature(block, offsets, SizeFeatureSet());
-    if (query && block.AtEnd()) {
-      return MediaQueryExpNode::Nested(query);
+    {
+      CSSParserTokenStream::RestoringBlockGuard guard(stream);
+      stream.ConsumeWhitespace();
+      // <size-feature>
+      const MediaQueryExpNode* query = ConsumeFeature(stream, SizeFeatureSet());
+      if (query && stream.AtEnd()) {
+        guard.Release();
+        stream.ConsumeWhitespace();
+        return MediaQueryExpNode::Nested(query);
+      }
     }
-    block = original_block;
 
-    // <container-condition>
-    const MediaQueryExpNode* condition =
-        ConsumeContainerCondition(block, offsets);
-    if (condition && block.AtEnd()) {
-      return MediaQueryExpNode::Nested(condition);
+    {
+      CSSParserTokenStream::RestoringBlockGuard guard(stream);
+      stream.ConsumeWhitespace();
+      // <container-condition>
+      const MediaQueryExpNode* condition = ConsumeContainerCondition(stream);
+      if (condition && stream.AtEnd()) {
+        guard.Release();
+        stream.ConsumeWhitespace();
+        return MediaQueryExpNode::Nested(condition);
+      }
     }
-  } else if (range.Peek().GetType() == kFunctionToken &&
-             range.Peek().FunctionId() == CSSValueID::kStyle) {
+  } else if (stream.Peek().GetType() == kFunctionToken &&
+             stream.Peek().FunctionId() == CSSValueID::kStyle) {
     // style( <style-query> )
-    CSSParserTokenRange block = range.ConsumeBlock();
-    block.ConsumeWhitespace();
-    range.ConsumeWhitespace();
+    CSSParserTokenStream::RestoringBlockGuard guard(stream);
+    stream.ConsumeWhitespace();
 
     if (const MediaQueryExpNode* query =
-            ConsumeFeatureQuery(block, offsets, StyleFeatureSet())) {
+            ConsumeFeatureQuery(stream, StyleFeatureSet())) {
       context_.Count(WebFeature::kCSSStyleContainerQuery);
+      guard.Release();
+      stream.ConsumeWhitespace();
       return MediaQueryExpNode::Function(query, AtomicString("style"));
     }
   } else if (RuntimeEnabledFeatures::CSSScrollStateContainerQueriesEnabled() &&
-             range.Peek().GetType() == kFunctionToken &&
-             range.Peek().FunctionId() == CSSValueID::kScrollState) {
+             stream.Peek().GetType() == kFunctionToken &&
+             stream.Peek().FunctionId() == CSSValueID::kScrollState) {
     // scroll-state(stuck: [ none | top | left | right | bottom | inset-* ] )
     // scroll-state(snapped: [ none | block | inline | x | y ] )
-    CSSParserTokenRange block = range.ConsumeBlock();
-    block.ConsumeWhitespace();
-    range.ConsumeWhitespace();
+    CSSParserTokenStream::RestoringBlockGuard guard(stream);
+    stream.ConsumeWhitespace();
 
     if (const MediaQueryExpNode* query =
-            ConsumeFeatureQuery(block, offsets, StateFeatureSet())) {
+            ConsumeFeatureQuery(stream, StateFeatureSet())) {
+      guard.Release();
+      stream.ConsumeWhitespace();
       return MediaQueryExpNode::Function(query, AtomicString("scroll-state"));
     }
   }
-  range = original_range;
+  stream.Restore(savepoint);
 
   // <general-enclosed>
-  return media_query_parser_.ConsumeGeneralEnclosed(range);
+  return media_query_parser_.ConsumeGeneralEnclosed(stream);
 }
 
 const MediaQueryExpNode* ContainerQueryParser::ConsumeContainerCondition(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets) {
+    CSSParserTokenStream& stream) {
   return ConsumeNotAndOr(
-      [this, offsets](CSSParserTokenRange& range) {
-        return this->ConsumeQueryInParens(range, offsets);
+      [this](CSSParserTokenStream& stream) {
+        return this->ConsumeQueryInParens(stream);
       },
-      range);
+      stream);
 }
 
 const MediaQueryExpNode* ContainerQueryParser::ConsumeFeatureQuery(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     const FeatureSet& feature_set) {
-  CSSParserTokenRange original_range = range;
-
-  if (const MediaQueryExpNode* feature =
-          ConsumeFeature(range, offsets, feature_set)) {
+  stream.EnsureLookAhead();
+  CSSParserTokenStream::State savepoint = stream.Save();
+  if (const MediaQueryExpNode* feature = ConsumeFeature(stream, feature_set)) {
     return feature;
   }
-  range = original_range;
+  stream.Restore(savepoint);
 
   if (const MediaQueryExpNode* node =
-          ConsumeFeatureCondition(range, offsets, feature_set)) {
+          ConsumeFeatureCondition(stream, feature_set)) {
     return node;
   }
 
@@ -243,42 +241,38 @@
 }
 
 const MediaQueryExpNode* ContainerQueryParser::ConsumeFeatureQueryInParens(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     const FeatureSet& feature_set) {
-  CSSParserTokenRange original_range = range;
-
-  if (range.Peek().GetType() == kLeftParenthesisToken) {
-    auto block = range.ConsumeBlock();
-    block.ConsumeWhitespace();
-    range.ConsumeWhitespace();
-    const MediaQueryExpNode* query =
-        ConsumeFeatureQuery(block, offsets, feature_set);
-    if (query && block.AtEnd()) {
+  CSSParserTokenStream::State savepoint = stream.Save();
+  if (stream.Peek().GetType() == kLeftParenthesisToken) {
+    CSSParserTokenStream::RestoringBlockGuard guard(stream);
+    stream.ConsumeWhitespace();
+    const MediaQueryExpNode* query = ConsumeFeatureQuery(stream, feature_set);
+    if (query && stream.AtEnd()) {
+      guard.Release();
+      stream.ConsumeWhitespace();
       return MediaQueryExpNode::Nested(query);
     }
   }
-  range = original_range;
+  stream.Restore(savepoint);
 
-  return media_query_parser_.ConsumeGeneralEnclosed(range);
+  return media_query_parser_.ConsumeGeneralEnclosed(stream);
 }
 
 const MediaQueryExpNode* ContainerQueryParser::ConsumeFeatureCondition(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     const FeatureSet& feature_set) {
   return ConsumeNotAndOr(
-      [this, &offsets, &feature_set](CSSParserTokenRange& range) {
-        return this->ConsumeFeatureQueryInParens(range, offsets, feature_set);
+      [this, &feature_set](CSSParserTokenStream& stream) {
+        return this->ConsumeFeatureQueryInParens(stream, feature_set);
       },
-      range);
+      stream);
 }
 
 const MediaQueryExpNode* ContainerQueryParser::ConsumeFeature(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     const FeatureSet& feature_set) {
-  return media_query_parser_.ConsumeFeature(range, offsets, feature_set);
+  return media_query_parser_.ConsumeFeature(stream, feature_set);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/parser/container_query_parser.h b/third_party/blink/renderer/core/css/parser/container_query_parser.h
index 29ce49d..075eaed 100644
--- a/third_party/blink/renderer/core/css/parser/container_query_parser.h
+++ b/third_party/blink/renderer/core/css/parser/container_query_parser.h
@@ -25,34 +25,22 @@
 
   // https://drafts.csswg.org/css-contain-3/#typedef-container-condition
   const MediaQueryExpNode* ParseCondition(String);
-  const MediaQueryExpNode* ParseCondition(CSSParserTokenRange,
-                                          const CSSParserTokenOffsets&);
+  const MediaQueryExpNode* ParseCondition(CSSParserTokenStream&);
 
  private:
   friend class ContainerQueryParserTest;
 
   using FeatureSet = MediaQueryParser::FeatureSet;
 
-  const MediaQueryExpNode* ConsumeQueryInParens(
-      CSSParserTokenRange&,
-      const CSSParserTokenOffsets& offsets);
-  const MediaQueryExpNode* ConsumeContainerCondition(
-      CSSParserTokenRange&,
-      const CSSParserTokenOffsets&);
-  const MediaQueryExpNode* ConsumeFeatureQuery(
-      CSSParserTokenRange&,
-      const CSSParserTokenOffsets& offsets,
-      const FeatureSet&);
-  const MediaQueryExpNode* ConsumeFeatureQueryInParens(
-      CSSParserTokenRange&,
-      const CSSParserTokenOffsets&,
-      const FeatureSet&);
-  const MediaQueryExpNode* ConsumeFeatureCondition(
-      CSSParserTokenRange&,
-      const CSSParserTokenOffsets& offsets,
-      const FeatureSet&);
-  const MediaQueryExpNode* ConsumeFeature(CSSParserTokenRange&,
-                                          const CSSParserTokenOffsets& offsets,
+  const MediaQueryExpNode* ConsumeQueryInParens(CSSParserTokenStream&);
+  const MediaQueryExpNode* ConsumeContainerCondition(CSSParserTokenStream&);
+  const MediaQueryExpNode* ConsumeFeatureQuery(CSSParserTokenStream&,
+                                               const FeatureSet&);
+  const MediaQueryExpNode* ConsumeFeatureQueryInParens(CSSParserTokenStream&,
+                                                       const FeatureSet&);
+  const MediaQueryExpNode* ConsumeFeatureCondition(CSSParserTokenStream&,
+                                                   const FeatureSet&);
+  const MediaQueryExpNode* ConsumeFeature(CSSParserTokenStream&,
                                           const FeatureSet&);
 
   const CSSParserContext& context_;
diff --git a/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc b/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc
index 1672403..210216f2 100644
--- a/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc
@@ -6,7 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
-#include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -48,15 +48,11 @@
   // E.g. https://drafts.csswg.org/css-contain-3/#typedef-style-query
   String ParseFeatureQuery(String feature_query) {
     const auto* context = MakeGarbageCollected<CSSParserContext>(GetDocument());
-    auto [tokens, raw_offsets] =
-        CSSTokenizer(feature_query).TokenizeToEOFWithOffsets();
-    CSSParserTokenRange range(tokens);
-    CSSParserTokenOffsets offsets(tokens, std::move(raw_offsets),
-                                  feature_query);
+    CSSParserTokenStream stream(feature_query);
     const MediaQueryExpNode* node =
-        ContainerQueryParser(*context).ConsumeFeatureQuery(range, offsets,
+        ContainerQueryParser(*context).ConsumeFeatureQuery(stream,
                                                            TestFeatureSet());
-    if (!node || !range.AtEnd()) {
+    if (!node || !stream.AtEnd()) {
       return g_null_atom;
     }
     return node->Serialize();
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.cc b/third_party/blink/renderer/core/css/parser/css_parser.cc
index dd5d283..25533b5 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser.cc
@@ -60,8 +60,7 @@
     StyleSheetContents* style_sheet_contents,
     const String& selector,
     HeapVector<CSSSelector>& arena) {
-  CSSTokenizer tokenizer(selector);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(selector);
   return CSSSelectorParser::ParseSelector(
       stream, context, nesting_type, parent_rule_for_nesting, is_within_scope,
       /* semicolon_aborts_nested_selector */ false, style_sheet_contents,
@@ -72,8 +71,7 @@
     const CSSParserContext& context,
     StyleSheetContents* style_sheet_contents,
     const String& selector) {
-  CSSTokenizer tokenizer(selector);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(selector);
   CSSSelectorList* selector_list =
       CSSParserImpl::ParsePageSelector(stream, style_sheet_contents, context);
   if (!stream.AtEnd()) {
@@ -199,8 +197,7 @@
   const CSSProperty& property = CSSProperty::Get(resolved_property);
   if (parser_mode == kHTMLStandardMode && property.IsProperty() &&
       !property.IsShorthand()) {
-    CSSTokenizer tokenizer(string);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(string);
     value =
         CSSPropertyParser::ParseSingleValue(resolved_property, stream, context);
     if (value != nullptr) {
@@ -264,8 +261,7 @@
           CSSParserFastPaths::MaybeParseValue(property_id, string, context)) {
     return value;
   }
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   return CSSPropertyParser::ParseSingleValue(property_id, stream, context);
 }
 
@@ -307,8 +303,7 @@
     const ExecutionContext* execution_context) {
   // window.CSS.supports requires to parse as-if it was wrapped in parenthesis.
   String wrapped_condition = "(" + condition + ")";
-  CSSTokenizer tokenizer(wrapped_condition);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(wrapped_condition);
   DCHECK(execution_context);
   // Create parser context using document so it can check for origin trial
   // enabled property/value.
@@ -402,15 +397,13 @@
   if (string.empty() || !context) {
     return nullptr;
   }
-  CSSTokenizer tokenizer(string);
-  const auto tokens = tokenizer.TokenizeToEOF();
-  CSSParserTokenRange range(tokens);
+  CSSParserTokenStream stream(string);
   // Trim whitespace from the string. It's only necessary to consume leading
   // whitespaces, since ConsumeLengthOrPercent always consumes trailing ones.
-  range.ConsumeWhitespace();
+  stream.ConsumeWhitespace();
   CSSPrimitiveValue* parsed_value =
-      css_parsing_utils::ConsumeLengthOrPercent(range, *context, value_range);
-  return range.AtEnd() ? parsed_value : nullptr;
+      css_parsing_utils::ConsumeLengthOrPercent(stream, *context, value_range);
+  return stream.AtEnd() ? parsed_value : nullptr;
 }
 
 MutableCSSPropertyValueSet* CSSParser::ParseFont(
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
index 2254746..51e682c8 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -180,8 +180,7 @@
     const CSSParserContext* context) {
   STACK_UNINITIALIZED CSSParserImpl parser(context);
   StyleRule::RuleType rule_type = RuleTypeForMutableDeclaration(declaration);
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   parser.ConsumeDeclarationValue(stream, unresolved_property,
                                  /*is_in_declaration_list=*/false, rule_type);
   if (parser.parsed_properties_.empty()) {
@@ -203,8 +202,7 @@
     const CSSParserContext* context,
     bool is_animation_tainted) {
   STACK_UNINITIALIZED CSSParserImpl parser(context);
-  CSSTokenizer tokenizer(value);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(value);
   if (!parser.ConsumeVariableValue(stream, property_name,
                                    /*allow_important_annotation=*/false,
                                    is_animation_tainted)) {
@@ -320,8 +318,7 @@
                            : kHTMLQuirksMode;
   context->SetMode(mode);
   CSSParserImpl parser(context, document.ElementSheet().Contents());
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   parser.ConsumeDeclarationList(stream, StyleRule::kStyle,
                                 CSSNestingType::kNone,
                                 /*parent_rule_for_nesting=*/nullptr,
@@ -338,8 +335,7 @@
   auto* context =
       MakeGarbageCollected<CSSParserContext>(parser_mode, secure_context_mode);
   CSSParserImpl parser(context);
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   parser.ConsumeDeclarationList(stream, StyleRule::kStyle,
                                 CSSNestingType::kNone,
                                 /*parent_rule_for_nesting=*/nullptr,
@@ -355,8 +351,7 @@
     const CSSParserContext* context) {
   CSSParserImpl parser(context);
   StyleRule::RuleType rule_type = RuleTypeForMutableDeclaration(declaration);
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   // See function declaration comment for why parent_rule_for_nesting ==
   // nullptr.
   parser.ConsumeDeclarationList(stream, rule_type, CSSNestingType::kNone,
@@ -387,8 +382,7 @@
     StyleRule* parent_rule_for_nesting,
     StringView text) {
   CSSParserImpl parser(context);
-  CSSTokenizer tokenizer(text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(text);
 
   HeapVector<Member<StyleRuleBase>, 4> child_rules;
 
@@ -413,8 +407,7 @@
                                         StyleSheetContents* style_sheet,
                                         AllowedRulesType allowed_rules) {
   CSSParserImpl parser(context, style_sheet);
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   stream.ConsumeWhitespace();
   if (stream.UncheckedAtEnd()) {
     return nullptr;  // Parse error, empty rule
@@ -460,8 +453,7 @@
 
   TRACE_EVENT_BEGIN0("blink,blink_style",
                      "CSSParserImpl::parseStyleSheet.parse");
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   CSSParserImpl parser(context, style_sheet);
   if (defer_property_parsing == CSSDeferPropertyParsing::kYes) {
     parser.lazy_state_ = MakeGarbageCollected<CSSLazyParsingState>(
@@ -496,7 +488,7 @@
   TRACE_EVENT_END0("blink,blink_style", "CSSParserImpl::parseStyleSheet.parse");
 
   TRACE_EVENT_END2("blink,blink_style", "CSSParserImpl::parseStyleSheet",
-                   "tokenCount", tokenizer.TokenCount(), "length",
+                   "tokenCount", stream.TokenCount(), "length",
                    string.length());
   return result;
 }
@@ -553,10 +545,14 @@
 std::unique_ptr<Vector<KeyframeOffset>> CSSParserImpl::ParseKeyframeKeyList(
     const CSSParserContext* context,
     const String& key_list) {
-  CSSTokenizer tokenizer(key_list);
-  // TODO(crbug.com/661854): Use streams instead of ranges
-  return ConsumeKeyframeKeyList(context,
-                                CSSParserTokenRange(tokenizer.TokenizeToEOF()));
+  CSSParserTokenStream stream(key_list);
+  std::unique_ptr<Vector<KeyframeOffset>> result =
+      ConsumeKeyframeKeyList(context, stream);
+  if (stream.AtEnd()) {
+    return result;
+  } else {
+    return nullptr;
+  }
 }
 
 String CSSParserImpl::ParseCustomPropertyName(const String& name_text) {
@@ -593,10 +589,9 @@
     CSSParserObserver& observer) {
   CSSParserImpl parser(context);
   parser.observer_ = &observer;
-  CSSTokenizer tokenizer(declaration);
   observer.StartRuleHeader(StyleRule::kStyle, 0);
   observer.EndRuleHeader(1);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(declaration);
   observer.StartRuleBody(stream.Offset());
   parser.ConsumeDeclarationList(stream, StyleRule::kStyle,
                                 CSSNestingType::kNone,
@@ -612,8 +607,7 @@
                                                 CSSParserObserver& observer) {
   CSSParserImpl parser(context, style_sheet);
   parser.observer_ = &observer;
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   bool first_rule_valid =
       parser.ConsumeRuleList(stream, kTopLevelRuleList, CSSNestingType::kNone,
                              /*parent_rule_for_nesting=*/nullptr,
@@ -636,8 +630,7 @@
   // so parent_rule_for_nesting is always nullptr here. The parser
   // explicitly makes sure we do not invoke lazy parsing for rules
   // with child rules in them.
-  CSSTokenizer tokenizer(string, offset);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string, offset);
   CSSParserTokenStream::BlockGuard guard(stream);
   CSSParserImpl parser(context);
   parser.ConsumeDeclarationList(stream, StyleRule::kStyle,
@@ -739,10 +732,6 @@
   return first_rule_valid;
 }
 
-CSSParserTokenRange ConsumeAtRulePrelude(CSSParserTokenStream& stream) {
-  return stream.ConsumeUntilPeekedTypeIs<kLeftBraceToken, kSemicolonToken>();
-}
-
 // Same as ConsumeEndOfPreludeForAtRuleWithBlock() below, but for at-rules
 // that don't have a block and are terminated only by semicolon.
 bool CSSParserImpl::ConsumeEndOfPreludeForAtRuleWithoutBlock(
@@ -987,17 +976,23 @@
   if (allowed_rules == kKeyframeRules) {
     stream.EnsureLookAhead();
     const wtf_size_t prelude_offset_start = stream.LookAheadOffset();
-    const CSSParserTokenRange prelude =
-        stream.ConsumeUntilPeekedTypeIs<kLeftBraceToken>();
+    std::unique_ptr<Vector<KeyframeOffset>> key_list =
+        ConsumeKeyframeKeyList(context_, stream);
+    stream.ConsumeWhitespace();
     const RangeOffset prelude_offset(prelude_offset_start,
                                      stream.LookAheadOffset());
 
+    if (stream.Peek().GetType() != kLeftBraceToken) {
+      key_list = nullptr;  // Parse error, junk after prelude
+      stream.SkipUntilPeekedTypeIs<kLeftBraceToken>();
+    }
     if (stream.AtEnd()) {
       return nullptr;  // Parse error, EOF instead of qualified rule block
     }
 
     CSSParserTokenStream::BlockGuard guard(stream);
-    return ConsumeKeyframeStyleRule(prelude, prelude_offset, stream);
+    return ConsumeKeyframeStyleRule(std::move(key_list), prelude_offset,
+                                    stream);
   }
   if (allowed_rules == kFontFeatureRules) {
     // We get here if something other than an at rule (e.g. @swash,
@@ -1063,15 +1058,6 @@
   return MakeGarbageCollected<StyleRuleCharset>();
 }
 
-// We need the token offsets for MediaQueryParser, so re-parse the prelude.
-static CSSParserTokenOffsets ReparseForOffsets(
-    const StringView prelude,
-    const CSSParserTokenRange range) {
-  Vector<wtf_size_t, 32> raw_offsets =
-      CSSTokenizer(prelude).TokenizeToEOFWithOffsets().second;
-  return {range.RemainingSpan(), std::move(raw_offsets), prelude};
-}
-
 StyleRuleImport* CSSParserImpl::ConsumeImportRule(
     const AtomicString& uri,
     CSSParserTokenStream& stream) {
@@ -1356,13 +1342,53 @@
     CSSParserTokenStream& stream,
     CSSNestingType nesting_type,
     StyleRule* parent_rule_for_nesting) {
+  // Consume the prelude.
+
+  // First just get the string for the prelude to see if we've got a cached
+  // version of this. (This is mainly to save memory in certain page with
+  // lots of duplicate media queries.)
+  CSSParserTokenStream::State savepoint = stream.Save();
   wtf_size_t prelude_offset_start = stream.LookAheadOffset();
-  CSSParserTokenRange prelude = ConsumeAtRulePrelude(stream);
+  stream.SkipUntilPeekedTypeIs<kLeftBraceToken, kSemicolonToken>();
   wtf_size_t prelude_offset_end = stream.LookAheadOffset();
+
+  String prelude_string =
+      stream
+          .StringRangeAt(prelude_offset_start,
+                         prelude_offset_end - prelude_offset_start)
+          .ToString();
+  const MediaQuerySet* media;
+  Member<const MediaQuerySet>& cached_media =
+      media_query_cache_.insert(prelude_string, nullptr).stored_value->value;
+  if (cached_media) {
+    media = cached_media.Get();
+  } else {
+    // Not in the cache, so we'll have to rewind and actually parse it.
+    // Note that the media query set grammar doesn't really have an idea
+    // of when the stream should end; if it sees something it doesn't
+    // understand (which includes a left brace), it will just forward to
+    // the next comma, skipping over the entire stylesheet until the end.
+    // The grammar is generally written in the understanding that the prelude
+    // is extracted as a string and only then parsed, whereas we do fully
+    // streaming prelude parsing. Thus, we need to set some boundaries
+    // here ourselves to make sure we end when the prelude does; the alternative
+    // would be to teach the media query set parser to stop there itself.
+    stream.Restore(savepoint);
+    CSSParserTokenStream::Boundary boundary(stream, kLeftBraceToken);
+    CSSParserTokenStream::Boundary boundary2(stream, kSemicolonToken);
+    media = MediaQueryParser::ParseMediaQuerySet(
+        stream, context_->GetExecutionContext());
+  }
+  DCHECK(media);
+
   if (!ConsumeEndOfPreludeForAtRuleWithBlock(stream,
                                              CSSAtRuleID::kCSSAtRuleMedia)) {
     return nullptr;
   }
+
+  cached_media = media;
+
+  // Consume the actual block.
   CSSParserTokenStream::BlockGuard guard(stream);
 
   if (observer_) {
@@ -1375,16 +1401,6 @@
     style_sheet_->SetHasMediaQueries();
   }
 
-  String prelude_string =
-      stream
-          .StringRangeAt(prelude_offset_start,
-                         prelude_offset_end - prelude_offset_start)
-          .ToString();
-  CSSParserTokenOffsets offsets = ReparseForOffsets(prelude_string, prelude);
-  const MediaQuerySet* media =
-      CachedMediaQuerySet(prelude_string, prelude, offsets);
-  DCHECK(media);
-
   HeapVector<Member<StyleRuleBase>, 4> rules;
   ConsumeRuleListOrNestedDeclarationList(
       stream,
@@ -2045,46 +2061,40 @@
     CSSParserTokenStream& stream,
     CSSNestingType nesting_type,
     StyleRule* parent_rule_for_nesting) {
+  // Consume the prelude.
   wtf_size_t prelude_offset_start = stream.LookAheadOffset();
-  CSSParserTokenRange prelude = ConsumeAtRulePrelude(stream);
-  wtf_size_t prelude_offset_end = stream.LookAheadOffset();
-  if (!ConsumeEndOfPreludeForAtRuleWithBlock(
-          stream, CSSAtRuleID::kCSSAtRuleContainer)) {
-    return nullptr;
-  }
-  CSSParserTokenStream::BlockGuard guard(stream);
-
-  if (observer_) {
-    observer_->StartRuleHeader(StyleRule::kContainer, prelude_offset_start);
-    observer_->EndRuleHeader(prelude_offset_end);
-  }
-
   ContainerQueryParser query_parser(*context_);
 
-  CSSParserTokenOffsets offsets = ReparseForOffsets(
-      stream.StringRangeAt(prelude_offset_start,
-                           prelude_offset_end - prelude_offset_start),
-      prelude);
-
   // <container-name>
   AtomicString name;
-  if (prelude.Peek().GetType() == kIdentToken) {
+  if (stream.Peek().GetType() == kIdentToken) {
     auto* ident = DynamicTo<CSSCustomIdentValue>(
-        css_parsing_utils::ConsumeSingleContainerName(prelude, *context_));
+        css_parsing_utils::ConsumeSingleContainerName(stream, *context_));
     if (ident) {
       name = ident->Value();
     }
   }
 
-  const MediaQueryExpNode* query =
-      query_parser.ParseCondition(prelude, offsets);
+  const MediaQueryExpNode* query = query_parser.ParseCondition(stream);
   if (!query) {
+    ConsumeErroneousAtRule(stream, CSSAtRuleID::kCSSAtRuleContainer);
     return nullptr;
   }
   ContainerQuery* container_query = MakeGarbageCollected<ContainerQuery>(
       ContainerSelector(std::move(name), *query), query);
 
+  wtf_size_t prelude_offset_end = stream.LookAheadOffset();
+  if (!ConsumeEndOfPreludeForAtRuleWithBlock(
+          stream, CSSAtRuleID::kCSSAtRuleContainer)) {
+    return nullptr;
+  }
+
+  // Consume the actual block.
+  CSSParserTokenStream::BlockGuard guard(stream);
+
   if (observer_) {
+    observer_->StartRuleHeader(StyleRule::kContainer, prelude_offset_start);
+    observer_->EndRuleHeader(prelude_offset_end);
     observer_->StartRuleBody(stream.Offset());
   }
 
@@ -2522,11 +2532,9 @@
 }
 
 StyleRuleKeyframe* CSSParserImpl::ConsumeKeyframeStyleRule(
-    const CSSParserTokenRange prelude,
+    std::unique_ptr<Vector<KeyframeOffset>> key_list,
     const RangeOffset& prelude_offset,
     CSSParserTokenStream& block) {
-  std::unique_ptr<Vector<KeyframeOffset>> key_list =
-      ConsumeKeyframeKeyList(context_, prelude);
   if (!key_list) {
     return nullptr;
   }
@@ -2710,8 +2718,8 @@
       // cannot use the CachedCSSTokenizer if that would otherwise be in use.
       if (MayContainNestedRules(lazy_state_->SheetText(), block_start_offset,
                                 block_length)) {
-        CSSTokenizer tokenizer(lazy_state_->SheetText(), block_start_offset);
-        CSSParserTokenStream block_stream(tokenizer);
+        CSSParserTokenStream block_stream(lazy_state_->SheetText(),
+                                          block_start_offset);
         CSSParserTokenStream::BlockGuard sub_guard(
             block_stream);  // Consume the {, and open the block stack.
         return ConsumeStyleRuleContents(selector_vector, block_stream);
@@ -3161,71 +3169,55 @@
 
 std::unique_ptr<Vector<KeyframeOffset>> CSSParserImpl::ConsumeKeyframeKeyList(
     const CSSParserContext* context,
-    CSSParserTokenRange range) {
+    CSSParserTokenStream& stream) {
   std::unique_ptr<Vector<KeyframeOffset>> result =
       std::make_unique<Vector<KeyframeOffset>>();
   while (true) {
-    range.ConsumeWhitespace();
-    const CSSParserToken& token = range.Peek();
+    stream.ConsumeWhitespace();
+    const CSSParserToken& token = stream.Peek();
     if (token.GetType() == kPercentageToken && token.NumericValue() >= 0 &&
         token.NumericValue() <= 100) {
       result->push_back(KeyframeOffset(TimelineOffset::NamedRange::kNone,
                                        token.NumericValue() / 100));
-      range.ConsumeIncludingWhitespace();
+      stream.ConsumeIncludingWhitespace();
     } else if (token.GetType() == kIdentToken) {
       if (EqualIgnoringASCIICase(token.Value(), "from")) {
         result->push_back(KeyframeOffset(TimelineOffset::NamedRange::kNone, 0));
-        range.ConsumeIncludingWhitespace();
+        stream.ConsumeIncludingWhitespace();
       } else if (EqualIgnoringASCIICase(token.Value(), "to")) {
         result->push_back(KeyframeOffset(TimelineOffset::NamedRange::kNone, 1));
-        range.ConsumeIncludingWhitespace();
+        stream.ConsumeIncludingWhitespace();
       } else {
-        auto* range_name_percent = To<CSSValueList>(
-            css_parsing_utils::ConsumeTimelineRangeNameAndPercent(range,
+        auto* stream_name_percent = To<CSSValueList>(
+            css_parsing_utils::ConsumeTimelineRangeNameAndPercent(stream,
                                                                   *context));
-        if (!range_name_percent) {
+        if (!stream_name_percent) {
           return nullptr;
         }
 
-        auto range_name = To<CSSIdentifierValue>(range_name_percent->Item(0))
-                              .ConvertTo<TimelineOffset::NamedRange>();
+        auto stream_name = To<CSSIdentifierValue>(stream_name_percent->Item(0))
+                               .ConvertTo<TimelineOffset::NamedRange>();
         auto percent =
-            To<CSSPrimitiveValue>(range_name_percent->Item(1)).GetFloatValue();
+            To<CSSPrimitiveValue>(stream_name_percent->Item(1)).GetFloatValue();
 
         if (!RuntimeEnabledFeatures::ScrollTimelineEnabled() &&
-            range_name != TimelineOffset::NamedRange::kNone) {
+            stream_name != TimelineOffset::NamedRange::kNone) {
           return nullptr;
         }
 
-        result->push_back(KeyframeOffset(range_name, percent / 100.0));
+        result->push_back(KeyframeOffset(stream_name, percent / 100.0));
       }
     } else {
       return nullptr;
     }
 
-    if (range.AtEnd()) {
+    if (stream.Peek().GetType() != kCommaToken) {
       return result;
     }
-    if (range.Consume().GetType() != kCommaToken) {
-      return nullptr;  // Parser error
-    }
+    stream.Consume();
   }
 }
 
-const MediaQuerySet* CSSParserImpl::CachedMediaQuerySet(
-    String prelude_string,
-    CSSParserTokenRange prelude,
-    const CSSParserTokenOffsets& offsets) {
-  Member<const MediaQuerySet>& media =
-      media_query_cache_.insert(prelude_string, nullptr).stored_value->value;
-  if (!media) {
-    media = MediaQueryParser::ParseMediaQuerySet(
-        prelude, offsets, context_->GetExecutionContext());
-  }
-  DCHECK(media);
-  return media.Get();
-}
-
 CSSParserMode CSSParserImpl::GetMode() const {
   return context_->Mode();
 }
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.h b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
index d77c157f..5f63343 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
@@ -256,9 +256,10 @@
   StyleRuleMixin* ConsumeMixinRule(CSSParserTokenStream& stream);
   StyleRuleApplyMixin* ConsumeApplyMixinRule(CSSParserTokenStream& stream);
 
-  StyleRuleKeyframe* ConsumeKeyframeStyleRule(CSSParserTokenRange prelude,
-                                              const RangeOffset& prelude_offset,
-                                              CSSParserTokenStream& block);
+  StyleRuleKeyframe* ConsumeKeyframeStyleRule(
+      std::unique_ptr<Vector<KeyframeOffset>> key_list,
+      const RangeOffset& prelude_offset,
+      CSSParserTokenStream& block);
   StyleRule* ConsumeStyleRule(CSSParserTokenStream&,
                               CSSNestingType,
                               StyleRule* parent_rule_for_nesting,
@@ -303,15 +304,7 @@
 
   static std::unique_ptr<Vector<KeyframeOffset>> ConsumeKeyframeKeyList(
       const CSSParserContext*,
-      CSSParserTokenRange);
-
-  // Finds a previously parsed MediaQuerySet for the given `prelude_string`
-  // and returns it. If no MediaQuerySet is found, parses one using `prelude`,
-  // and returns the result after caching it.
-  const MediaQuerySet* CachedMediaQuerySet(
-      String prelude_string,
-      CSSParserTokenRange prelude,
-      const CSSParserTokenOffsets& offsets);
+      CSSParserTokenStream&);
 
   // Create an implicit & {} rule to wrap properties in, and insert every
   // property from parsed_properties_ in it. Used when there are properties
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc
index 76d1515c..fdc221e9 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc
@@ -687,8 +687,7 @@
   };
   for (auto current_case : test_cases) {
     SCOPED_TRACE(current_case.input);
-    CSSTokenizer tokenizer(current_case.input);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(current_case.input);
     bool is_important;
     CSSVariableData* data = CSSVariableParser::ConsumeUnparsedDeclaration(
         stream, /*allow_important_annotation=*/true,
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_token_range.h b/third_party/blink/renderer/core/css/parser/css_parser_token_range.h
index 37d3076..bbb7da4 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_token_range.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_token_range.h
@@ -15,9 +15,6 @@
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
-#include <algorithm>
-#include <utility>
-
 namespace blink {
 
 CORE_EXPORT extern const CSSParserToken& g_static_eof_token;
@@ -92,47 +89,6 @@
   const CSSParserToken* last_;
 };
 
-// An auxiliary class that can recover the exact string used for a set of
-// tokens. It stores per-token offsets (such as from
-// CSSTokenizer::TokenizeToEOFWithOffsets()) and a pointer to the original
-// string (which must live for at least as long as this class), and from that,
-// it can give you the exact string that a given token range came from.
-class CSSParserTokenOffsets {
- public:
-  template <wtf_size_t InlineBuffer>
-  CSSParserTokenOffsets(const Vector<CSSParserToken, InlineBuffer>& vector,
-                        Vector<wtf_size_t, 32> offsets,
-                        StringView string)
-      : first_(vector.data()), offsets_(std::move(offsets)), string_(string) {
-    DCHECK_EQ(vector.size() + 1, offsets_.size());
-  }
-  CSSParserTokenOffsets(base::span<const CSSParserToken> tokens,
-                        Vector<wtf_size_t, 32> offsets,
-                        StringView string)
-      : first_(tokens.data()), offsets_(std::move(offsets)), string_(string) {
-    DCHECK_EQ(tokens.size() + 1, offsets_.size());
-  }
-
-  wtf_size_t OffsetFor(const CSSParserToken* token) const {
-    DCHECK_GE(token, first_);
-    DCHECK_LT(token, first_ + offsets_.size() - 1);
-    wtf_size_t token_index = static_cast<wtf_size_t>(token - first_);
-    return offsets_[token_index];
-  }
-
-  StringView StringForTokens(const CSSParserToken* begin,
-                             const CSSParserToken* end) const {
-    wtf_size_t begin_offset = OffsetFor(begin);
-    wtf_size_t end_offset = OffsetFor(end);
-    return StringView(string_, begin_offset, end_offset - begin_offset);
-  }
-
- private:
-  const CSSParserToken* first_;
-  Vector<wtf_size_t, 32> offsets_;
-  StringView string_;
-};
-
 bool NeedsInsertedComment(const CSSParserToken& a, const CSSParserToken& b);
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_token_stream.h b/third_party/blink/renderer/core/css/parser/css_parser_token_stream.h
index 000a029..0271516a 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_token_stream.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_token_stream.h
@@ -133,10 +133,12 @@
   // only needed for declarations which are easier to think about?
   static constexpr int kInitialBufferSize = 128;
 
-  explicit CSSParserTokenStream(CSSTokenizer& tokenizer)
-      : tokenizer_(tokenizer), next_(kEOFToken) {}
+  explicit CSSParserTokenStream(const String& text, wtf_size_t offset = 0)
+      : tokenizer_(text, offset), next_(kEOFToken) {}
+  explicit CSSParserTokenStream(StringView text)
+      : tokenizer_(text), next_(kEOFToken) {}
 
-  CSSParserTokenStream(CSSParserTokenStream&&) = default;
+  CSSParserTokenStream(CSSParserTokenStream&&) = delete;
   CSSParserTokenStream(const CSSParserTokenStream&) = delete;
   CSSParserTokenStream& operator=(const CSSParserTokenStream&) = delete;
 
@@ -635,6 +637,8 @@
     bool released_ = false;
   };
 
+  wtf_size_t TokenCount() const { return tokenizer_.TokenCount(); }
+
  private:
   template <CSSParserTokenType... EndTypes>
   ALWAYS_INLINE bool TokenMarksEnd(const CSSParserToken& token) {
@@ -682,7 +686,7 @@
   void PopBlockStack() { tokenizer_.block_stack_.pop_back(); }
 
   Vector<CSSParserToken, kInitialBufferSize> buffer_;
-  CSSTokenizer& tokenizer_;
+  CSSTokenizer tokenizer_;
   CSSParserToken next_;
   wtf_size_t offset_ = 0;
   bool has_look_ahead_ = false;
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_token_stream_test.cc b/third_party/blink/renderer/core/css/parser/css_parser_token_stream_test.cc
index da6fd50..95422af 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_token_stream_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_token_stream_test.cc
@@ -13,31 +13,27 @@
 namespace {
 
 TEST(CSSParserTokenStreamTest, EmptyStream) {
-  CSSTokenizer tokenizer(String(""));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String(""));
   EXPECT_TRUE(stream.Consume().IsEOF());
   EXPECT_TRUE(stream.Peek().IsEOF());
   EXPECT_TRUE(stream.AtEnd());
 }
 
 TEST(CSSParserTokenStreamTest, PeekThenConsume) {
-  CSSTokenizer tokenizer(String("A"));  // kIdent
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("A"));  // kIdent
   EXPECT_EQ(kIdentToken, stream.Peek().GetType());
   EXPECT_EQ(kIdentToken, stream.Consume().GetType());
   EXPECT_TRUE(stream.AtEnd());
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeThenPeek) {
-  CSSTokenizer tokenizer(String("A"));  // kIdent
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("A"));  // kIdent
   EXPECT_EQ(kIdentToken, stream.Consume().GetType());
   EXPECT_TRUE(stream.AtEnd());
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeMultipleTokens) {
-  CSSTokenizer tokenizer(String("A 1"));  // kIdent kWhitespace kNumber
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("A 1"));  // kIdent kWhitespace kNumber
   EXPECT_EQ(kIdentToken, stream.Consume().GetType());
   EXPECT_EQ(kWhitespaceToken, stream.Consume().GetType());
   EXPECT_EQ(kNumberToken, stream.Consume().GetType());
@@ -45,8 +41,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, UncheckedPeekAndConsumeAfterPeek) {
-  CSSTokenizer tokenizer(String("A"));  // kIdent
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("A"));  // kIdent
   EXPECT_EQ(kIdentToken, stream.Peek().GetType());
   EXPECT_EQ(kIdentToken, stream.UncheckedPeek().GetType());
   EXPECT_EQ(kIdentToken, stream.UncheckedConsume().GetType());
@@ -54,8 +49,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, UncheckedPeekAndConsumeAfterAtEnd) {
-  CSSTokenizer tokenizer(String("A"));  // kIdent
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("A"));  // kIdent
   EXPECT_FALSE(stream.AtEnd());
   EXPECT_EQ(kIdentToken, stream.UncheckedPeek().GetType());
   EXPECT_EQ(kIdentToken, stream.UncheckedConsume().GetType());
@@ -63,8 +57,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, UncheckedConsumeComponentValue) {
-  CSSTokenizer tokenizer(String("A{1}{2{3}}B"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("A{1}{2{3}}B"));
 
   EXPECT_EQ(kIdentToken, stream.Peek().GetType());
   stream.UncheckedConsumeComponentValue();
@@ -79,16 +72,14 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeWhitespace) {
-  CSSTokenizer tokenizer(String(" \t\n"));  // kWhitespace
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String(" \t\n"));  // kWhitespace
 
   EXPECT_EQ(kWhitespaceToken, stream.Consume().GetType());
   EXPECT_TRUE(stream.AtEnd());
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeIncludingWhitespace) {
-  CSSTokenizer tokenizer(String("A \t\n"));  // kIdent kWhitespace
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("A \t\n"));  // kIdent kWhitespace
 
   EXPECT_EQ(kIdentToken, stream.ConsumeIncludingWhitespace().GetType());
   EXPECT_TRUE(stream.AtEnd());
@@ -101,8 +92,7 @@
     s.Append("A ");
   }
 
-  CSSTokenizer tokenizer(s.ToString());
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(s.ToString());
 
   // Consume a single token range.
   auto range = stream.ConsumeUntilPeekedTypeIs<kIdentToken>();
@@ -119,8 +109,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, BlockErrorRecoveryConsumesRestOfBlock) {
-  CSSTokenizer tokenizer(String("{B }1"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("{B }1"));
 
   {
     CSSParserTokenStream::BlockGuard guard(stream);
@@ -132,8 +121,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, BlockErrorRecoveryOnSuccess) {
-  CSSTokenizer tokenizer(String("{B }1"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("{B }1"));
 
   {
     CSSParserTokenStream::BlockGuard guard(stream);
@@ -146,8 +134,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, BlockErrorRecoveryConsumeComponentValue) {
-  CSSTokenizer tokenizer(String("{{B} C}1"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("{{B} C}1"));
 
   {
     CSSParserTokenStream::BlockGuard guard(stream);
@@ -159,8 +146,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, OffsetAfterPeek) {
-  CSSTokenizer tokenizer(String("ABC"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("ABC"));
 
   EXPECT_EQ(0U, stream.Offset());
   EXPECT_EQ(kIdentToken, stream.Peek().GetType());
@@ -168,8 +154,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, OffsetAfterConsumes) {
-  CSSTokenizer tokenizer(String("ABC 1 {23 }"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("ABC 1 {23 }"));
 
   EXPECT_EQ(0U, stream.Offset());
   EXPECT_EQ(kIdentToken, stream.Consume().GetType());
@@ -184,8 +169,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, LookAheadOffset) {
-  CSSTokenizer tokenizer(String("ABC/* *//* */1"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("ABC/* *//* */1"));
 
   stream.EnsureLookAhead();
   EXPECT_EQ(0U, stream.Offset());
@@ -198,8 +182,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeUntilPeekedTypeOffset) {
-  CSSTokenizer tokenizer(String("a b c;d e f"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("a b c;d e f"));
 
   // a
   EXPECT_EQ(kIdentToken, stream.Peek().GetType());
@@ -216,8 +199,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeUntilPeekedTypeOffsetEndOfFile) {
-  CSSTokenizer tokenizer(String("a b c"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("a b c"));
 
   // a
   EXPECT_EQ(kIdentToken, stream.Peek().GetType());
@@ -234,8 +216,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeUntilPeekedTypeOffsetEndOfBlock) {
-  CSSTokenizer tokenizer(String("a { a b c } d ;"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("a { a b c } d ;"));
 
   // a
   EXPECT_EQ(0u, stream.Offset());
@@ -270,8 +251,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeUntilPeekedTypeIsEmpty) {
-  CSSTokenizer tokenizer(String("{23 }"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("{23 }"));
 
   auto range = stream.ConsumeUntilPeekedTypeIs<>();
   EXPECT_TRUE(stream.AtEnd());
@@ -284,8 +264,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeComponentValueEOF) {
-  CSSTokenizer tokenizer(String(""));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String(""));
 
   CSSParserTokenRange range = stream.ConsumeComponentValue();
   EXPECT_TRUE(range.AtEnd());
@@ -293,8 +272,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeComponentValueToken) {
-  CSSTokenizer tokenizer(String("foo"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("foo"));
 
   CSSParserTokenRange range = stream.ConsumeComponentValue();
   EXPECT_TRUE(stream.AtEnd());
@@ -303,8 +281,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeComponentValueWhitespace) {
-  CSSTokenizer tokenizer(String(" foo"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String(" foo"));
 
   CSSParserTokenRange range = stream.ConsumeComponentValue();
   EXPECT_FALSE(stream.AtEnd());
@@ -316,8 +293,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeComponentValueTrailingWhitespace) {
-  CSSTokenizer tokenizer(String("foo "));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("foo "));
 
   CSSParserTokenRange range = stream.ConsumeComponentValue();
   EXPECT_FALSE(stream.AtEnd());
@@ -329,8 +305,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeComponentValueBlock) {
-  CSSTokenizer tokenizer(String("{ foo }"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("{ foo }"));
 
   CSSParserTokenRange range = stream.ConsumeComponentValue();
   EXPECT_TRUE(stream.AtEnd());
@@ -344,8 +319,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeComponentValueMultiBlock) {
-  CSSTokenizer tokenizer(String("{} []"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("{} []"));
 
   CSSParserTokenRange range = stream.ConsumeComponentValue();
   EXPECT_FALSE(stream.AtEnd());
@@ -361,8 +335,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, ConsumeComponentValueFunction) {
-  CSSTokenizer tokenizer(String(": foo(42) ;"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String(": foo(42) ;"));
 
   {
     CSSParserTokenRange range = stream.ConsumeComponentValue();
@@ -403,8 +376,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, Boundary) {
-  CSSTokenizer tokenizer(String("foo:red;bar:blue;asdf"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("foo:red;bar:blue;asdf"));
 
   {
     CSSParserTokenStream::Boundary boundary(stream, kSemicolonToken);
@@ -435,8 +407,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, MultipleBoundaries) {
-  CSSTokenizer tokenizer(String("a:b,c;d:,;e"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("a:b,c;d:,;e"));
 
   {
     CSSParserTokenStream::Boundary boundary_semicolon(stream, kSemicolonToken);
@@ -484,8 +455,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, IneffectiveBoundary) {
-  CSSTokenizer tokenizer(String("a:b|"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("a:b|"));
 
   {
     CSSParserTokenStream::Boundary boundary_colon(stream, kColonToken);
@@ -511,8 +481,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, BoundaryBlockGuard) {
-  CSSTokenizer tokenizer(String("a[b;c]d;e"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("a[b;c]d;e"));
 
   {
     CSSParserTokenStream::Boundary boundary(stream, kSemicolonToken);
@@ -533,8 +502,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, BoundaryRestoringBlockGuard) {
-  CSSTokenizer tokenizer(String("a[b;c]d;e"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("a[b;c]d;e"));
 
   {
     CSSParserTokenStream::Boundary boundary(stream, kSemicolonToken);
@@ -557,8 +525,7 @@
 }
 
 TEST(CSSParserTokenStreamTest, SavePointRestoreWithoutLookahead) {
-  CSSTokenizer tokenizer(String("a b c"));
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(String("a b c"));
   stream.EnsureLookAhead();
 
   {
@@ -786,8 +753,7 @@
   Vector<CSSParserToken, 32> ref_tokens = TokenizeAll(ref);
 
   String input(param.input);
-  CSSTokenizer tokenizer(input);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(input);
 
   auto [restart_target, restart_offset] = ParseRestart(String(param.restart));
   Vector<CSSParserToken, 32> actual_tokens;
@@ -822,8 +788,7 @@
   Vector<CSSParserToken, 32> ref_tokens = TokenizeAll(ref);
 
   String input(param.input);
-  CSSTokenizer tokenizer(input);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(input);
 
   CSSParserTokenStream::Boundary boundary(stream, kSemicolonToken);
 
@@ -862,8 +827,7 @@
 
   for (wtf_size_t restart_offset = 0; restart_offset <= input.length();
        ++restart_offset) {
-    CSSTokenizer tokenizer(input);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(input);
 
     Vector<CSSParserToken, 32> actual_tokens;
     TokenizeInto(stream, /* restart_target */ restart_offset, restart_offset,
@@ -887,8 +851,7 @@
   STACK_ALLOCATED();
 
  public:
-  explicit TestStream(String input)
-      : input_(input), tokenizer_(input_), stream_(tokenizer_) {
+  explicit TestStream(String input) : input_(input), stream_(input) {
     stream_.EnsureLookAhead();
   }
 
@@ -919,7 +882,6 @@
   friend class TestBlockGuard;
   friend class TestBoundary;
   String input_;
-  CSSTokenizer tokenizer_;
   CSSParserTokenStream stream_;
 };
 
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
index 5396eed..e1a82cd 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
@@ -46,8 +46,7 @@
 
 static bool IsValidPropertyValueForStyleRule(CSSPropertyID property_id,
                                              const String& value) {
-  CSSTokenizer tokenizer(value);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(value);
   HeapVector<CSSPropertyValue, 64> parsed_properties;
   return CSSPropertyParser::ParseValue(
       property_id, /*allow_important_annotation=*/false, stream,
@@ -964,8 +963,7 @@
 bool ParseCSSValue(CSSPropertyID property_id,
                    const String& value,
                    const CSSParserContext* context) {
-  CSSTokenizer tokenizer(value);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(value);
   HeapVector<CSSPropertyValue, 64> parsed_properties;
   return CSSPropertyParser::ParseValue(
       property_id, /*allow_important_annotation=*/false, stream, context,
@@ -1007,8 +1005,7 @@
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
 
   String string = " revert";
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
 
   const CSSValue* value = CSSPropertyParser::ParseSingleValue(
       CSSPropertyID::kMarginLeft, stream, context);
@@ -1021,8 +1018,7 @@
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
 
   String string = " revert-layer";
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
 
   const CSSValue* value = CSSPropertyParser::ParseSingleValue(
       CSSPropertyID::kMarginLeft, stream, context);
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index 02aef498..900111d9 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -985,8 +985,7 @@
   // For old pseudos (before, after, first-letter, first-line), we
   // allow the legacy behavior of single-colon / no-colon.
   {
-    CSSTokenizer tokenizer(selector_string);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(selector_string);
     stream.EnsureLookAhead();
     int num_colons = 0;
     if (stream.Peek().GetType() == kColonToken) {
@@ -1043,8 +1042,7 @@
       /*style_sheet=*/nullptr, arena);
 
   ResetVectorAfterScope reset_vector(parser.output_);
-  CSSTokenizer tokenizer(selector_string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(selector_string);
   if (!parser.ConsumePseudo(stream)) {
     return kPseudoIdInvalid;
   }
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
index b809e038..8d8b2d8 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
@@ -118,8 +118,7 @@
     SCOPED_TRACE(test_case.input);
 
     std::pair<int, int> ab;
-    CSSTokenizer tokenizer(StringView(test_case.input));
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(StringView(test_case.input));
     bool passed = CSSSelectorParser::ConsumeANPlusB(stream, ab);
     EXPECT_TRUE(passed);
     EXPECT_EQ(test_case.a, ab.first);
@@ -141,8 +140,7 @@
     SCOPED_TRACE(test_case);
 
     std::pair<int, int> ab;
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     bool passed = CSSSelectorParser::ConsumeANPlusB(stream, ab);
     EXPECT_FALSE(passed);
   }
@@ -161,8 +159,7 @@
 
   HeapVector<CSSSelector> arena;
   for (StringView test_case : test_cases) {
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream,
         MakeGarbageCollected<CSSParserContext>(
@@ -188,8 +185,7 @@
 
   HeapVector<CSSSelector> arena;
   for (StringView test_case : test_cases) {
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream,
         MakeGarbageCollected<CSSParserContext>(
@@ -228,8 +224,7 @@
 
   HeapVector<CSSSelector> arena;
   for (StringView test_case : test_cases) {
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream,
         MakeGarbageCollected<CSSParserContext>(
@@ -274,8 +269,7 @@
   HeapVector<CSSSelector> arena;
   for (const auto& test_case : test_cases) {
     SCOPED_TRACE(test_case.selector);
-    CSSTokenizer tokenizer(StringView(test_case.selector));
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(StringView(test_case.selector));
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream,
         MakeGarbageCollected<CSSParserContext>(
@@ -313,8 +307,7 @@
 
   HeapVector<CSSSelector> arena;
   for (StringView test_case : test_cases) {
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream,
         MakeGarbageCollected<CSSParserContext>(
@@ -334,8 +327,7 @@
 
   HeapVector<CSSSelector> arena;
   for (StringView test_case : test_cases) {
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream,
         MakeGarbageCollected<CSSParserContext>(
@@ -357,8 +349,7 @@
 
   HeapVector<CSSSelector> arena;
   for (StringView test_case : test_cases) {
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream, context, CSSNestingType::kNone,
         /*parent_rule_for_nesting=*/nullptr, /*is_within_scope=*/false,
@@ -377,8 +368,7 @@
 
   HeapVector<CSSSelector> arena;
   for (StringView test_case : test_cases) {
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream, context, CSSNestingType::kNone,
         /*parent_rule_for_nesting=*/nullptr, /*is_within_scope=*/false,
@@ -409,8 +399,7 @@
   HeapVector<CSSSelector> arena;
   for (auto** test_case : test_cases) {
     SCOPED_TRACE(test_case[0]);
-    CSSTokenizer tokenizer(StringView{test_case[0]});
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(StringView{test_case[0]});
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream, context, CSSNestingType::kNone,
         /*parent_rule_for_nesting=*/nullptr, /*is_within_scope=*/false,
@@ -432,8 +421,7 @@
   HeapVector<CSSSelector> arena;
   for (String test_case : test_cases) {
     SCOPED_TRACE(test_case);
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream, context, CSSNestingType::kNone,
         /*parent_rule_for_nesting=*/nullptr, /*is_within_scope=*/false,
@@ -458,8 +446,7 @@
   for (String test_case : test_cases) {
     SCOPED_TRACE(test_case);
     {
-      CSSTokenizer tokenizer(test_case);
-      CSSParserTokenStream stream(tokenizer);
+      CSSParserTokenStream stream(test_case);
       base::span<CSSSelector> author_vector = CSSSelectorParser::ParseSelector(
           stream,
           MakeGarbageCollected<CSSParserContext>(
@@ -471,8 +458,7 @@
     }
 
     {
-      CSSTokenizer tokenizer(test_case);
-      CSSParserTokenStream stream(tokenizer);
+      CSSParserTokenStream stream(test_case);
       base::span<CSSSelector> ua_vector = CSSSelectorParser::ParseSelector(
           stream,
           MakeGarbageCollected<CSSParserContext>(
@@ -502,8 +488,7 @@
   HeapVector<CSSSelector> arena;
   for (const auto& test_case : test_cases) {
     SCOPED_TRACE(test_case.selector);
-    CSSTokenizer tokenizer(StringView(test_case.selector));
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(StringView(test_case.selector));
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream,
         MakeGarbageCollected<CSSParserContext>(
@@ -707,8 +692,7 @@
   HeapVector<CSSSelector> arena;
   for (auto test_case : test_cases) {
     SCOPED_TRACE(test_case.input);
-    CSSTokenizer tokenizer(StringView(test_case.input));
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(StringView(test_case.input));
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream, context, CSSNestingType::kNone,
         /*parent_rule_for_nesting=*/nullptr, /*is_within_scope=*/false,
@@ -737,8 +721,7 @@
   HeapVector<CSSSelector> arena;
   for (auto test_case : test_cases) {
     SCOPED_TRACE(test_case.input);
-    CSSTokenizer tokenizer(StringView(test_case.input));
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(StringView(test_case.input));
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream, context, CSSNestingType::kNone,
         /*parent_rule_for_nesting=*/nullptr, /*is_within_scope=*/false,
@@ -760,8 +743,7 @@
   HeapVector<CSSSelector> arena;
   for (String test_case : test_cases) {
     SCOPED_TRACE(test_case);
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream,
         MakeGarbageCollected<CSSParserContext>(
@@ -784,8 +766,7 @@
   HeapVector<CSSSelector> arena;
   for (String test_case : test_cases) {
     SCOPED_TRACE(test_case);
-    CSSTokenizer tokenizer(test_case);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(test_case);
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream,
         MakeGarbageCollected<CSSParserContext>(
@@ -812,8 +793,7 @@
 
   DCHECK(!doc->IsUseCounted(feature));
 
-  CSSTokenizer tokenizer(StringView{selector});
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(StringView{selector});
   HeapVector<CSSSelector> arena;
   CSSSelectorParser::ParseSelector(stream, context, CSSNestingType::kNone,
                                    /*parent_rule_for_nesting=*/nullptr,
@@ -1022,8 +1002,7 @@
   HeapVector<CSSSelector> arena;
   for (auto test_case : test_cases) {
     SCOPED_TRACE(test_case.input);
-    CSSTokenizer tokenizer(StringView(test_case.input));
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(StringView(test_case.input));
     base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
         stream, context, CSSNestingType::kNone,
         /*parent_rule_for_nesting=*/nullptr, /*is_within_scope=*/false,
diff --git a/third_party/blink/renderer/core/css/parser/css_supports_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_supports_parser_test.cc
index e72e7d9..d2a6cefc 100644
--- a/third_party/blink/renderer/core/css/parser/css_supports_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_supports_parser_test.cc
@@ -28,8 +28,7 @@
 
   Result StaticConsumeSupportsCondition(String string) {
     CSSParserImpl impl(MakeContext());
-    CSSTokenizer tokenizer(string);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(string);
     Result result = CSSSupportsParser::ConsumeSupportsCondition(stream, impl);
     return stream.AtEnd() ? result : Result::kParseFailure;
   }
@@ -46,48 +45,42 @@
   Result ConsumeSupportsCondition(String string) {
     CSSParserImpl impl(MakeContext());
     CSSSupportsParser parser(impl);
-    CSSTokenizer tokenizer(string);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(string);
     return parser.ConsumeSupportsCondition(stream);
   }
 
   Result ConsumeSupportsInParens(String string) {
     CSSParserImpl impl(MakeContext());
     CSSSupportsParser parser(impl);
-    CSSTokenizer tokenizer(string);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(string);
     return parser.ConsumeSupportsInParens(stream);
   }
 
   bool ConsumeSupportsFeature(String string) {
     CSSParserImpl impl(MakeContext());
     CSSSupportsParser parser(impl);
-    CSSTokenizer tokenizer(string);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(string);
     return parser.ConsumeSupportsFeature(stream);
   }
 
   bool ConsumeSupportsSelectorFn(String string) {
     CSSParserImpl impl(MakeContext());
     CSSSupportsParser parser(impl);
-    CSSTokenizer tokenizer(string);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(string);
     return parser.ConsumeSupportsSelectorFn(stream);
   }
 
   bool ConsumeSupportsDecl(String string) {
     CSSParserImpl impl(MakeContext());
     CSSSupportsParser parser(impl);
-    CSSTokenizer tokenizer(string);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(string);
     return parser.ConsumeSupportsDecl(stream);
   }
 
   bool ConsumeGeneralEnclosed(String string) {
     CSSParserImpl impl(MakeContext());
     CSSSupportsParser parser(impl);
-    CSSTokenizer tokenizer(string);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(string);
     return parser.ConsumeGeneralEnclosed(stream);
   }
 };
diff --git a/third_party/blink/renderer/core/css/parser/css_tokenizer.cc b/third_party/blink/renderer/core/css/parser/css_tokenizer.cc
index f9adb02..82e4e8e 100644
--- a/third_party/blink/renderer/core/css/parser/css_tokenizer.cc
+++ b/third_party/blink/renderer/core/css/parser/css_tokenizer.cc
@@ -62,27 +62,6 @@
   }
 }
 
-std::pair<Vector<CSSParserToken, 32>, Vector<wtf_size_t, 32>>
-CSSTokenizer::TokenizeToEOFWithOffsets() {
-  wtf_size_t estimated_tokens =
-      (input_.length() - Offset()) / kEstimatedCharactersPerToken;
-  Vector<CSSParserToken, 32> tokens;
-  tokens.ReserveInitialCapacity(estimated_tokens);
-  Vector<wtf_size_t, 32> offsets;
-  offsets.ReserveInitialCapacity(estimated_tokens + 1);
-
-  while (true) {
-    offsets.push_back(input_.Offset());
-    const CSSParserToken token =
-        NextToken</*SkipComments=*/true, /*StoreOffset=*/false>();
-    if (token.GetType() == kEOFToken) {
-      return {tokens, offsets};
-    } else {
-      tokens.push_back(token);
-    }
-  }
-}
-
 StringView CSSTokenizer::StringRangeFrom(wtf_size_t start) const {
   return input_.RangeFrom(start);
 }
@@ -100,7 +79,7 @@
   return NextToken</*SkipComments=*/false, /*StoreOffset=*/true>();
 }
 
-wtf_size_t CSSTokenizer::TokenCount() {
+wtf_size_t CSSTokenizer::TokenCount() const {
   return token_count_;
 }
 
diff --git a/third_party/blink/renderer/core/css/parser/css_tokenizer.h b/third_party/blink/renderer/core/css/parser/css_tokenizer.h
index 5acd58e..aa8f992d 100644
--- a/third_party/blink/renderer/core/css/parser/css_tokenizer.h
+++ b/third_party/blink/renderer/core/css/parser/css_tokenizer.h
@@ -35,16 +35,7 @@
   // object, or the string data referenced by the CSSTokenizer. Do not use the
   // tokens after the CSSTokenizer or its underlying String goes out of scope.
   Vector<CSSParserToken, 32> TokenizeToEOF();
-  wtf_size_t TokenCount();
-
-  // Like TokenizeToEOF(), but also returns the start byte for each token.
-  // There's an extra offset at the very end that returns the end byte
-  // of the last token, i.e., the length of the input string.
-  // This matches the convention CSSParserTokenOffsets expects.
-  //
-  // See the warning about holding a reference in TokenizeToEOF().
-  std::pair<Vector<CSSParserToken, 32>, Vector<wtf_size_t, 32>>
-  TokenizeToEOFWithOffsets();
+  wtf_size_t TokenCount() const;
 
   wtf_size_t Offset() const { return input_.Offset(); }
   wtf_size_t PreviousOffset() const { return prev_offset_; }
diff --git a/third_party/blink/renderer/core/css/parser/css_tokenizer_test.cc b/third_party/blink/renderer/core/css/parser/css_tokenizer_test.cc
index 95e5290..6d7c878 100644
--- a/third_party/blink/renderer/core/css/parser/css_tokenizer_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_tokenizer_test.cc
@@ -73,8 +73,7 @@
 
   CSSParserTokenRange expected(expected_tokens);
 
-  CSSTokenizer tokenizer(string);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(string);
   CSSParserTokenStream::EnableUnicodeRanges enable(stream,
                                                    unicode_ranges_allowed);
   CSSParserTokenRange actual = stream.ConsumeUntilPeekedTypeIs();
diff --git a/third_party/blink/renderer/core/css/parser/css_variable_parser.cc b/third_party/blink/renderer/core/css/parser/css_variable_parser.cc
index 3f1fc14..284a2d0 100644
--- a/third_party/blink/renderer/core/css/parser/css_variable_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_variable_parser.cc
@@ -60,8 +60,7 @@
     const CSSParserContext& context) {
   // Note that positioned braces are allowed in custom property declarations
   // (i.e., restricted_value=false).
-  CSSTokenizer tokenizer(text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(text);
   bool important;
   CSSVariableData* variable_data = ConsumeUnparsedDeclaration(
       stream,
@@ -453,8 +452,7 @@
     StringView text,
     const CSSParserContext& context,
     bool is_animation_tainted) {
-  CSSTokenizer tokenizer(text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(text);
   stream.EnsureLookAhead();
 
   bool important;
diff --git a/third_party/blink/renderer/core/css/parser/css_variable_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_variable_parser_test.cc
index 0937164a..802f2426 100644
--- a/third_party/blink/renderer/core/css/parser/css_variable_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_variable_parser_test.cc
@@ -80,8 +80,7 @@
 
 TEST_P(ValidVariableReferenceTest, ConsumeUnparsedDeclaration) {
   SCOPED_TRACE(GetParam());
-  CSSTokenizer tokenizer{String(GetParam())};
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream{String(GetParam())};
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
   bool important;
@@ -114,8 +113,7 @@
 
 TEST_P(InvalidVariableReferenceTest, ConsumeUnparsedDeclaration) {
   SCOPED_TRACE(GetParam());
-  CSSTokenizer tokenizer{String(GetParam())};
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream{String(GetParam())};
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
   bool important;
@@ -167,8 +165,7 @@
 TEST_P(ValidAttrTest, ContainsValidAttr) {
   ScopedCSSAdvancedAttrFunctionForTest scoped_feature(true);
   SCOPED_TRACE(GetParam());
-  CSSTokenizer tokenizer{String(GetParam())};
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream{String(GetParam())};
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
   bool important;
@@ -190,8 +187,7 @@
   ScopedCSSAdvancedAttrFunctionForTest scoped_feature(true);
 
   SCOPED_TRACE(GetParam());
-  CSSTokenizer tokenizer{String(GetParam())};
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream{String(GetParam())};
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
   bool important;
diff --git a/third_party/blink/renderer/core/css/parser/media_condition_test.cc b/third_party/blink/renderer/core/css/parser/media_condition_test.cc
index 5d90628..7d0ffbe 100644
--- a/third_party/blink/renderer/core/css/parser/media_condition_test.cc
+++ b/third_party/blink/renderer/core/css/parser/media_condition_test.cc
@@ -10,6 +10,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/media_list.h"
 #include "third_party/blink/renderer/core/css/media_query.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/css/parser/media_query_parser.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -54,13 +55,11 @@
   for (unsigned i = 0; test_cases[i].input; ++i) {
     SCOPED_TRACE(test_cases[i].input);
     StringView str(test_cases[i].input);
-    CSSTokenizer tokenizer(str);
-    const auto [tokens, offsets] = tokenizer.TokenizeToEOFWithOffsets();
+    CSSParserTokenStream stream(str);
     MediaQuerySet* media_condition_query_set =
-        MediaQueryParser::ParseMediaCondition(
-            CSSParserTokenRange(tokens),
-            CSSParserTokenOffsets(tokens, std::move(offsets), str), nullptr);
-    String query_text = media_condition_query_set->MediaText();
+        MediaQueryParser::ParseMediaCondition(stream, nullptr);
+    String query_text =
+        stream.AtEnd() ? media_condition_query_set->MediaText() : "not all";
     const char* expected_text =
         test_cases[i].output ? test_cases[i].output : test_cases[i].input;
     EXPECT_EQ(String(expected_text), query_text);
diff --git a/third_party/blink/renderer/core/css/parser/media_query_parser.cc b/third_party/blink/renderer/core/css/parser/media_query_parser.cc
index 2abe942..79b3768 100644
--- a/third_party/blink/renderer/core/css/parser/media_query_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/media_query_parser.cc
@@ -121,38 +121,32 @@
 MediaQuerySet* MediaQueryParser::ParseMediaQuerySet(
     const String& query_string,
     const ExecutionContext* execution_context) {
-  CSSTokenizer tokenizer(query_string);
-  auto [tokens, raw_offsets] = tokenizer.TokenizeToEOFWithOffsets();
-  CSSParserTokenRange range(tokens);
-  CSSParserTokenOffsets offsets(tokens, std::move(raw_offsets), query_string);
-  return ParseMediaQuerySet(range, offsets, execution_context);
+  CSSParserTokenStream stream(query_string);
+  return ParseMediaQuerySet(stream, execution_context);
 }
 
 MediaQuerySet* MediaQueryParser::ParseMediaQuerySet(
-    CSSParserTokenRange range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     const ExecutionContext* execution_context) {
   return MediaQueryParser(kMediaQuerySetParser, kHTMLStandardMode,
                           execution_context)
-      .ParseImpl(range, offsets);
+      .ParseImpl(stream);
 }
 
 MediaQuerySet* MediaQueryParser::ParseMediaQuerySetInMode(
-    CSSParserTokenRange range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     CSSParserMode mode,
     const ExecutionContext* execution_context) {
   return MediaQueryParser(kMediaQuerySetParser, mode, execution_context)
-      .ParseImpl(range, offsets);
+      .ParseImpl(stream);
 }
 
 MediaQuerySet* MediaQueryParser::ParseMediaCondition(
-    CSSParserTokenRange range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     const ExecutionContext* execution_context) {
   return MediaQueryParser(kMediaConditionParser, kHTMLStandardMode,
                           execution_context)
-      .ParseImpl(range, offsets);
+      .ParseImpl(stream);
 }
 
 MediaQueryParser::MediaQueryParser(ParserType parser_type,
@@ -170,8 +164,6 @@
               ? DynamicTo<LocalDOMWindow>(execution_context)->document()
               : nullptr)) {}
 
-MediaQueryParser::~MediaQueryParser() = default;
-
 namespace {
 
 bool IsRestrictorOrLogicalOperator(const CSSParserToken& token) {
@@ -183,13 +175,13 @@
          EqualIgnoringASCIICase(token.Value(), "layer");
 }
 
-bool ConsumeUntilCommaInclusive(CSSParserTokenRange& range) {
-  while (!range.AtEnd()) {
-    if (range.Peek().GetType() == kCommaToken) {
-      range.ConsumeIncludingWhitespace();
+bool ConsumeUntilCommaInclusive(CSSParserTokenStream& stream) {
+  while (!stream.AtEnd()) {
+    if (stream.Peek().GetType() == kCommaToken) {
+      stream.ConsumeIncludingWhitespace();
       return true;
     }
-    range.ConsumeComponentValue();
+    stream.ConsumeComponentValue();
   }
   return false;
 }
@@ -198,18 +190,16 @@
   return c == '<' || c == '>' || c == '=';
 }
 
-CSSParserTokenRange ConsumeUntilComparisonOrColon(CSSParserTokenRange& range) {
-  const CSSParserToken* first = range.begin();
-  while (!range.AtEnd()) {
-    const CSSParserToken& token = range.Peek();
+void SkipUntilComparisonOrColon(CSSParserTokenStream& stream) {
+  while (!stream.AtEnd()) {
+    const CSSParserToken& token = stream.Peek();
     if ((token.GetType() == kDelimiterToken &&
          IsComparisonDelimiter(token.Delimiter())) ||
         token.GetType() == kColonToken) {
       break;
     }
-    range.ConsumeComponentValue();
+    stream.ConsumeComponentValue();
   }
-  return range.MakeSubRange(first, range.begin());
 }
 
 bool IsLtLe(MediaQueryOperator op) {
@@ -223,50 +213,50 @@
 }  // namespace
 
 MediaQuery::RestrictorType MediaQueryParser::ConsumeRestrictor(
-    CSSParserTokenRange& range) {
-  if (ConsumeIfIdent(range, "not")) {
+    CSSParserTokenStream& stream) {
+  if (ConsumeIfIdent(stream, "not")) {
     return MediaQuery::RestrictorType::kNot;
   }
-  if (ConsumeIfIdent(range, "only")) {
+  if (ConsumeIfIdent(stream, "only")) {
     return MediaQuery::RestrictorType::kOnly;
   }
   return MediaQuery::RestrictorType::kNone;
 }
 
-String MediaQueryParser::ConsumeType(CSSParserTokenRange& range) {
-  if (range.Peek().GetType() != kIdentToken) {
+String MediaQueryParser::ConsumeType(CSSParserTokenStream& stream) {
+  if (stream.Peek().GetType() != kIdentToken) {
     return g_null_atom;
   }
-  if (IsRestrictorOrLogicalOperator(range.Peek())) {
+  if (IsRestrictorOrLogicalOperator(stream.Peek())) {
     return g_null_atom;
   }
-  return range.ConsumeIncludingWhitespace().Value().ToString();
+  return stream.ConsumeIncludingWhitespace().Value().ToString();
 }
 
 MediaQueryOperator MediaQueryParser::ConsumeComparison(
-    CSSParserTokenRange& range) {
-  const CSSParserToken& first = range.Peek();
-  if (first.GetType() != kDelimiterToken) {
+    CSSParserTokenStream& stream) {
+  const CSSParserToken& first = stream.Peek();
+  if (first.GetType() != kDelimiterToken ||
+      !IsComparisonDelimiter(first.Delimiter())) {
     return MediaQueryOperator::kNone;
   }
-  DCHECK(IsComparisonDelimiter(first.Delimiter()));
   switch (first.Delimiter()) {
     case '=':
-      range.ConsumeIncludingWhitespace();
+      stream.ConsumeIncludingWhitespace();
       return MediaQueryOperator::kEq;
     case '<':
-      range.Consume();
-      if (ConsumeIfDelimiter(range, '=')) {
+      stream.Consume();
+      if (ConsumeIfDelimiter(stream, '=')) {
         return MediaQueryOperator::kLe;
       }
-      range.ConsumeWhitespace();
+      stream.ConsumeWhitespace();
       return MediaQueryOperator::kLt;
     case '>':
-      range.Consume();
-      if (ConsumeIfDelimiter(range, '=')) {
+      stream.Consume();
+      if (ConsumeIfDelimiter(stream, '=')) {
         return MediaQueryOperator::kGe;
       }
-      range.ConsumeWhitespace();
+      stream.ConsumeWhitespace();
       return MediaQueryOperator::kGt;
   }
 
@@ -274,12 +264,12 @@
   return MediaQueryOperator::kNone;
 }
 
-String MediaQueryParser::ConsumeAllowedName(CSSParserTokenRange& range,
+String MediaQueryParser::ConsumeAllowedName(CSSParserTokenStream& stream,
                                             const FeatureSet& feature_set) {
-  if (range.Peek().GetType() != kIdentToken) {
+  if (stream.Peek().GetType() != kIdentToken) {
     return g_null_atom;
   }
-  String name = range.Peek().Value().ToString();
+  String name = stream.Peek().Value().ToString();
   if (!feature_set.IsCaseSensitive(name)) {
     name = name.LowerASCII();
   }
@@ -287,13 +277,13 @@
   if (!feature_set.IsAllowed(name)) {
     return g_null_atom;
   }
-  range.ConsumeIncludingWhitespace();
+  stream.ConsumeIncludingWhitespace();
   return name;
 }
 
-String MediaQueryParser::ConsumeUnprefixedName(CSSParserTokenRange& range,
+String MediaQueryParser::ConsumeUnprefixedName(CSSParserTokenStream& stream,
                                                const FeatureSet& feature_set) {
-  String name = ConsumeAllowedName(range, feature_set);
+  String name = ConsumeAllowedName(stream, feature_set);
   if (name.IsNull()) {
     return name;
   }
@@ -303,83 +293,39 @@
   return name;
 }
 
-const MediaQueryExpNode* MediaQueryParser::ParseNameValueComparison(
-    CSSParserTokenRange lhs,
-    MediaQueryOperator op,
-    CSSParserTokenRange rhs,
-    const CSSParserTokenOffsets& offsets,
-    NameAffinity name_affinity,
-    const FeatureSet& feature_set) {
-  if (name_affinity == NameAffinity::kRight) {
-    std::swap(lhs, rhs);
-  }
-
-  String feature_name = ConsumeUnprefixedName(lhs, feature_set);
-  if (feature_name.IsNull() || !lhs.AtEnd()) {
-    return nullptr;
-  }
-
-  auto value =
-      MediaQueryExpValue::Consume(feature_name, rhs, offsets, fake_context_);
-
-  if (!value || !rhs.AtEnd()) {
-    return nullptr;
-  }
-
-  auto left = MediaQueryExpComparison();
-  auto right = MediaQueryExpComparison(*value, op);
-
-  if (name_affinity == NameAffinity::kRight) {
-    std::swap(left, right);
-  }
-
-  return MakeGarbageCollected<MediaQueryFeatureExpNode>(
-      MediaQueryExp::Create(feature_name, MediaQueryExpBounds(left, right)));
-}
-
 const MediaQueryExpNode* MediaQueryParser::ConsumeFeature(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     const FeatureSet& feature_set) {
-  // Because we don't know exactly where <mf-name> appears in the grammar, we
-  // split |range| on top-level separators, and parse each segment
-  // individually.
+  // There are several possible grammars for media queries, and we don't
+  // know where <mf-name> appears. Thus, our only strategy is to just try them
+  // one by one and restart if we got it wrong.
   //
-  // Local variables names in this function are chosen with the expectation
-  // that we are heading towards the most complicated form of <mf-range>:
-  //
-  //  <mf-value> <mf-gt> <mf-name> <mf-gt> <mf-value>
-  //
-  // Which corresponds to the local variables:
-  //
-  //  <segment1> <op1> <segment2> <op2> <segment3>
 
-  CSSParserTokenRange segment1 = ConsumeUntilComparisonOrColon(range);
+  CSSParserTokenStream::State start = stream.Save();
 
-  // <mf-boolean> = <mf-name>
-  if (range.AtEnd()) {
-    String feature_name = ConsumeAllowedName(segment1, feature_set);
-    if (feature_name.IsNull() || !segment1.AtEnd() ||
-        !feature_set.IsAllowedWithoutValue(feature_name, execution_context_)) {
-      return nullptr;
-    }
-    return MakeGarbageCollected<MediaQueryFeatureExpNode>(
-        MediaQueryExp::Create(feature_name, MediaQueryExpBounds()));
-  }
+  {
+    String feature_name = ConsumeAllowedName(stream, feature_set);
 
-  // <mf-plain> = <mf-name> : <mf-value>
-  if (range.Peek().GetType() == kColonToken) {
-    range.ConsumeIncludingWhitespace();
-    String feature_name = ConsumeAllowedName(segment1, feature_set);
-    if (feature_name.IsNull() || !segment1.AtEnd()) {
-      return nullptr;
+    // <mf-boolean> = <mf-name>
+    if (!feature_name.IsNull() && stream.AtEnd() &&
+        feature_set.IsAllowedWithoutValue(feature_name, execution_context_)) {
+      return MakeGarbageCollected<MediaQueryFeatureExpNode>(
+          MediaQueryExp::Create(feature_name, MediaQueryExpBounds()));
     }
-    auto exp =
-        MediaQueryExp::Create(feature_name, range, offsets, fake_context_);
-    if (!exp.IsValid() || !range.AtEnd()) {
-      return nullptr;
+
+    // <mf-plain> = <mf-name> : <mf-value>
+    if (!feature_name.IsNull() && stream.Peek().GetType() == kColonToken) {
+      stream.ConsumeIncludingWhitespace();
+
+      // NOTE: We do not check for stream.AtEnd() here, as an empty mf-value is
+      // legal.
+      auto exp = MediaQueryExp::Create(feature_name, stream, fake_context_);
+      if (exp.IsValid() && stream.AtEnd()) {
+        return MakeGarbageCollected<MediaQueryFeatureExpNode>(exp);
+      }
     }
-    return MakeGarbageCollected<MediaQueryFeatureExpNode>(exp);
+
+    stream.Restore(start);
   }
 
   if (!feature_set.SupportsRange()) {
@@ -393,38 +339,87 @@
   //            | <mf-value> <mf-lt> <mf-name> <mf-lt> <mf-value>
   //            | <mf-value> <mf-gt> <mf-name> <mf-gt> <mf-value>
 
-  MediaQueryOperator op1 = ConsumeComparison(range);
-  DCHECK_NE(op1, MediaQueryOperator::kNone);
+  {
+    // Try: <mf-name> <mf-comparison> <mf-value> (e.g., “width <= 10px”)
+    String feature_name = ConsumeUnprefixedName(stream, feature_set);
+    if (!feature_name.IsNull() && !stream.AtEnd()) {
+      MediaQueryOperator op = ConsumeComparison(stream);
+      if (op != MediaQueryOperator::kNone) {
+        auto value =
+            MediaQueryExpValue::Consume(feature_name, stream, fake_context_);
+        if (value && stream.AtEnd()) {
+          auto left = MediaQueryExpComparison();
+          auto right = MediaQueryExpComparison(*value, op);
 
-  CSSParserTokenRange segment2 = ConsumeUntilComparisonOrColon(range);
-
-  // If the range ended, the feature must be on the following form:
-  //
-  //  <segment1> <op1> <segment2>
-  //
-  // We don't know which of <segment1> and <segment2> should be interpreted as
-  // the <mf-name> and which should be interpreted as <mf-value>. We have to
-  // try both.
-  if (range.AtEnd()) {
-    // Try: <mf-name> <mf-comparison> <mf-value>
-    if (const MediaQueryExpNode* node =
-            ParseNameValueComparison(segment1, op1, segment2, offsets,
-                                     NameAffinity::kLeft, feature_set)) {
-      return node;
+          return MakeGarbageCollected<MediaQueryFeatureExpNode>(
+              MediaQueryExp::Create(feature_name,
+                                    MediaQueryExpBounds(left, right)));
+        }
+      }
     }
-
-    // Otherwise: <mf-value> <mf-comparison> <mf-name>
-    return ParseNameValueComparison(segment1, op1, segment2, offsets,
-                                    NameAffinity::kRight, feature_set);
+    stream.Restore(start);
   }
 
-  // Otherwise, the feature must be on the form:
+  // It must be one of these three:
   //
-  // <segment1> <op1> <segment2> <op2> <segment3>
+  // <mf-value> <mf-comparison> <mf-name>  (e.g., “10px = width”)
+  // <mf-value> <mf-lt> <mf-name> <mf-lt> <mf-value>
+  // <mf-value> <mf-gt> <mf-name> <mf-gt> <mf-value>
   //
-  // This grammar is easier to deal with, since <mf-name> can only appear
-  // at <segment2>.
-  MediaQueryOperator op2 = ConsumeComparison(range);
+  // We don't know how to parse <mf-value> yet, so we need to skip it
+  // and parse <mf-name> first, then return to (the first) <mf-value>
+  // afterwards.
+  //
+  // Local variables names from here on are chosen with the expectation
+  // that we are heading towards the most complicated form of <mf-range>
+  // (the latter in the list), which corresponds to the local variables:
+  //
+  //  <value1> <op1> <feature_name> <op2> <value2>
+  SkipUntilComparisonOrColon(stream);
+  if (stream.AtEnd()) {
+    return nullptr;
+  }
+  wtf_size_t offset_after_value1 = stream.LookAheadOffset();
+
+  MediaQueryOperator op1 = ConsumeComparison(stream);
+  if (op1 == MediaQueryOperator::kNone) {
+    return nullptr;
+  }
+
+  String feature_name = ConsumeUnprefixedName(stream, feature_set);
+  if (feature_name.IsNull()) {
+    return nullptr;
+  }
+
+  stream.ConsumeWhitespace();
+  CSSParserTokenStream::State after_feature_name = stream.Save();
+
+  stream.Restore(start);
+  auto value1 =
+      MediaQueryExpValue::Consume(feature_name, stream, fake_context_);
+  if (!value1) {
+    return nullptr;
+  }
+
+  if (stream.LookAheadOffset() != offset_after_value1) {
+    // There was junk between <value1> and <op1>.
+    return nullptr;
+  }
+
+  // Skip over the comparison and name again.
+  stream.Restore(after_feature_name);
+
+  if (stream.AtEnd()) {
+    // Must be: <mf-value> <mf-comparison> <mf-name>
+    auto left = MediaQueryExpComparison(*value1, op1);
+    auto right = MediaQueryExpComparison();
+
+    return MakeGarbageCollected<MediaQueryFeatureExpNode>(
+        MediaQueryExp::Create(feature_name, MediaQueryExpBounds(left, right)));
+  }
+
+  // Parse the last <mf-value>.
+  MediaQueryOperator op2 = ConsumeComparison(stream);
   if (op2 == MediaQueryOperator::kNone) {
     return nullptr;
   }
@@ -436,56 +431,39 @@
     return nullptr;
   }
 
-  if (range.AtEnd()) {
-    return nullptr;
-  }
-
-  String feature_name = ConsumeUnprefixedName(segment2, feature_set);
-  if (feature_name.IsNull() || !segment2.AtEnd()) {
-    return nullptr;
-  }
-
-  auto left_value = MediaQueryExpValue::Consume(feature_name, segment1, offsets,
-                                                fake_context_);
-  if (!left_value || !segment1.AtEnd()) {
-    return nullptr;
-  }
-
-  CSSParserTokenRange& segment3 = range;
-  auto right_value = MediaQueryExpValue::Consume(feature_name, segment3,
-                                                 offsets, fake_context_);
-  if (!right_value || !segment3.AtEnd()) {
+  auto value2 =
+      MediaQueryExpValue::Consume(feature_name, stream, fake_context_);
+  if (!value2) {
     return nullptr;
   }
 
   return MakeGarbageCollected<MediaQueryFeatureExpNode>(MediaQueryExp::Create(
       feature_name,
-      MediaQueryExpBounds(MediaQueryExpComparison(*left_value, op1),
-                          MediaQueryExpComparison(*right_value, op2))));
+      MediaQueryExpBounds(MediaQueryExpComparison(*value1, op1),
+                          MediaQueryExpComparison(*value2, op2))));
 }
 
 const MediaQueryExpNode* MediaQueryParser::ConsumeCondition(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets,
+    CSSParserTokenStream& stream,
     ConditionMode mode) {
   // <media-not>
-  if (ConsumeIfIdent(range, "not")) {
-    return MediaQueryExpNode::Not(ConsumeInParens(range, offsets));
+  if (ConsumeIfIdent(stream, "not")) {
+    return MediaQueryExpNode::Not(ConsumeInParens(stream));
   }
 
   // Otherwise:
   // <media-in-parens> [ <media-and>* | <media-or>* ]
 
-  const MediaQueryExpNode* result = ConsumeInParens(range, offsets);
+  const MediaQueryExpNode* result = ConsumeInParens(stream);
 
-  if (AtIdent(range.Peek(), "and")) {
-    while (result && ConsumeIfIdent(range, "and")) {
-      result = MediaQueryExpNode::And(result, ConsumeInParens(range, offsets));
+  if (AtIdent(stream.Peek(), "and")) {
+    while (result && ConsumeIfIdent(stream, "and")) {
+      result = MediaQueryExpNode::And(result, ConsumeInParens(stream));
     }
-  } else if (result && AtIdent(range.Peek(), "or") &&
+  } else if (result && AtIdent(stream.Peek(), "or") &&
              mode == ConditionMode::kNormal) {
-    while (result && ConsumeIfIdent(range, "or")) {
-      result = MediaQueryExpNode::Or(result, ConsumeInParens(range, offsets));
+    while (result && ConsumeIfIdent(stream, "or")) {
+      result = MediaQueryExpNode::Or(result, ConsumeInParens(stream));
     }
   }
 
@@ -493,129 +471,127 @@
 }
 
 const MediaQueryExpNode* MediaQueryParser::ConsumeInParens(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets) {
-  CSSParserTokenRange original_range = range;
+    CSSParserTokenStream& stream) {
+  if (stream.Peek().GetType() == kLeftParenthesisToken) {
+    {
+      CSSParserTokenStream::RestoringBlockGuard guard(stream);
+      stream.ConsumeWhitespace();
 
-  if (range.Peek().GetType() == kLeftParenthesisToken) {
-    CSSParserTokenRange block = range.ConsumeBlock();
-    block.ConsumeWhitespace();
-    range.ConsumeWhitespace();
-
-    CSSParserTokenRange original_block = block;
-
-    // ( <media-condition> )
-    const MediaQueryExpNode* condition = ConsumeCondition(block, offsets);
-    if (condition && block.AtEnd()) {
-      return MediaQueryExpNode::Nested(condition);
+      // ( <media-condition> )
+      const MediaQueryExpNode* condition = ConsumeCondition(stream);
+      if (condition && guard.Release()) {
+        stream.ConsumeWhitespace();
+        return MediaQueryExpNode::Nested(condition);
+      }
     }
-    block = original_block;
 
-    // ( <media-feature> )
-    const MediaQueryExpNode* feature =
-        ConsumeFeature(block, offsets, MediaQueryFeatureSet());
-    if (feature && block.AtEnd()) {
-      return MediaQueryExpNode::Nested(feature);
+    {
+      CSSParserTokenStream::RestoringBlockGuard guard(stream);
+      stream.ConsumeWhitespace();
+      // ( <media-feature> )
+      const MediaQueryExpNode* feature =
+          ConsumeFeature(stream, MediaQueryFeatureSet());
+      if (feature && guard.Release()) {
+        stream.ConsumeWhitespace();
+        return MediaQueryExpNode::Nested(feature);
+      }
     }
   }
-  range = original_range;
 
   // <general-enclosed>
-  return ConsumeGeneralEnclosed(range);
+  return ConsumeGeneralEnclosed(stream);
 }
 
 const MediaQueryExpNode* MediaQueryParser::ConsumeGeneralEnclosed(
-    CSSParserTokenRange& range) {
-  if (range.Peek().GetType() != kLeftParenthesisToken &&
-      range.Peek().GetType() != kFunctionToken) {
+    CSSParserTokenStream& stream) {
+  if (stream.Peek().GetType() != kLeftParenthesisToken &&
+      stream.Peek().GetType() != kFunctionToken) {
     return nullptr;
   }
 
-  const CSSParserToken* first = range.begin();
+  wtf_size_t start_offset = stream.Offset();
+  StringView general_enclosed;
+  {
+    CSSParserTokenStream::BlockGuard guard(stream);
 
-  CSSParserTokenRange block = range.ConsumeBlock();
-  block.ConsumeWhitespace();
+    stream.ConsumeWhitespace();
 
-  // Note that <any-value> is optional in <general-enclosed>, so having an
-  // empty block is fine.
-  if (!block.AtEnd()) {
-    if (!ConsumeAnyValue(block) || !block.AtEnd()) {
+    // Note that <any-value> is optional in <general-enclosed>, so having an
+    // empty block is fine.
+    ConsumeAnyValue(stream);
+    if (!stream.AtEnd()) {
       return nullptr;
     }
   }
 
+  wtf_size_t end_offset = stream.Offset();
+
   // TODO(crbug.com/962417): This is not well specified.
-  String general_enclosed =
-      range.MakeSubRange(first, range.begin()).Serialize();
-  range.ConsumeWhitespace();
-  return MakeGarbageCollected<MediaQueryUnknownExpNode>(general_enclosed);
+  general_enclosed =
+      stream.StringRangeAt(start_offset, end_offset - start_offset);
+
+  stream.ConsumeWhitespace();
+  return MakeGarbageCollected<MediaQueryUnknownExpNode>(
+      general_enclosed.ToString());
 }
 
 MediaQuerySet* MediaQueryParser::ConsumeSingleCondition(
-    CSSParserTokenRange range,
-    const CSSParserTokenOffsets& offsets) {
+    CSSParserTokenStream& stream) {
   DCHECK_EQ(parser_type_, kMediaConditionParser);
-  DCHECK(!range.AtEnd());
-
-  const MediaQueryExpNode* node = ConsumeCondition(range, offsets);
+  DCHECK(!stream.AtEnd());
 
   HeapVector<Member<const MediaQuery>> queries;
-
-  if (!node || !range.AtEnd()) {
+  const MediaQueryExpNode* node = ConsumeCondition(stream);
+  if (!node) {
     queries.push_back(MediaQuery::CreateNotAll());
   } else {
     queries.push_back(MakeGarbageCollected<MediaQuery>(
         MediaQuery::RestrictorType::kNone, media_type_names::kAll, node));
   }
-
   return MakeGarbageCollected<MediaQuerySet>(std::move(queries));
 }
 
-MediaQuery* MediaQueryParser::ConsumeQuery(
-    CSSParserTokenRange& range,
-    const CSSParserTokenOffsets& offsets) {
+MediaQuery* MediaQueryParser::ConsumeQuery(CSSParserTokenStream& stream) {
   DCHECK_EQ(parser_type_, kMediaQuerySetParser);
-  CSSParserTokenRange original_range = range;
+  CSSParserTokenStream::State savepoint = stream.Save();
 
   // First try to parse following grammar:
   //
   // [ not | only ]? <media-type> [ and <media-condition-without-or> ]?
-  MediaQuery::RestrictorType restrictor = ConsumeRestrictor(range);
-  String type = ConsumeType(range);
+  MediaQuery::RestrictorType restrictor = ConsumeRestrictor(stream);
+  String type = ConsumeType(stream);
 
   if (!type.IsNull()) {
-    if (!ConsumeIfIdent(range, "and")) {
+    if (!ConsumeIfIdent(stream, "and")) {
       return MakeGarbageCollected<MediaQuery>(restrictor, type, nullptr);
     }
     if (const MediaQueryExpNode* node =
-            ConsumeCondition(range, offsets, ConditionMode::kWithoutOr)) {
+            ConsumeCondition(stream, ConditionMode::kWithoutOr)) {
       return MakeGarbageCollected<MediaQuery>(restrictor, type, node);
     }
     return nullptr;
   }
-  range = original_range;
+  stream.Restore(savepoint);
 
   // Otherwise, <media-condition>
-  if (const MediaQueryExpNode* node = ConsumeCondition(range, offsets)) {
+  if (const MediaQueryExpNode* node = ConsumeCondition(stream)) {
     return MakeGarbageCollected<MediaQuery>(MediaQuery::RestrictorType::kNone,
                                             media_type_names::kAll, node);
   }
   return nullptr;
 }
 
-MediaQuerySet* MediaQueryParser::ParseImpl(
-    CSSParserTokenRange range,
-    const CSSParserTokenOffsets& offsets) {
-  range.ConsumeWhitespace();
+MediaQuerySet* MediaQueryParser::ParseImpl(CSSParserTokenStream& stream) {
+  stream.ConsumeWhitespace();
 
   // Note that we currently expect an empty input to evaluate to an empty
   // MediaQuerySet, rather than "not all".
-  if (range.AtEnd()) {
+  if (stream.AtEnd()) {
     return MakeGarbageCollected<MediaQuerySet>();
   }
 
   if (parser_type_ == kMediaConditionParser) {
-    return ConsumeSingleCondition(range, offsets);
+    return ConsumeSingleCondition(stream);
   }
 
   DCHECK_EQ(parser_type_, kMediaQuerySetParser);
@@ -623,10 +599,11 @@
   HeapVector<Member<const MediaQuery>> queries;
 
   do {
-    MediaQuery* query = ConsumeQuery(range, offsets);
-    bool ok = query && (range.AtEnd() || range.Peek().GetType() == kCommaToken);
+    MediaQuery* query = ConsumeQuery(stream);
+    bool ok =
+        query && (stream.AtEnd() || stream.Peek().GetType() == kCommaToken);
     queries.push_back(ok ? query : MediaQuery::CreateNotAll());
-  } while (!range.AtEnd() && ConsumeUntilCommaInclusive(range));
+  } while (!stream.AtEnd() && ConsumeUntilCommaInclusive(stream));
 
   return MakeGarbageCollected<MediaQuerySet>(std::move(queries));
 }
diff --git a/third_party/blink/renderer/core/css/parser/media_query_parser.h b/third_party/blink/renderer/core/css/parser/media_query_parser.h
index 984891d..4f1ff8e3 100644
--- a/third_party/blink/renderer/core/css/parser/media_query_parser.h
+++ b/third_party/blink/renderer/core/css/parser/media_query_parser.h
@@ -24,16 +24,16 @@
   STACK_ALLOCATED();
 
  public:
+  MediaQueryParser(const MediaQueryParser&) = delete;
+  MediaQueryParser& operator=(const MediaQueryParser&) = delete;
+
   static MediaQuerySet* ParseMediaQuerySet(const String&,
                                            const ExecutionContext*);
-  static MediaQuerySet* ParseMediaQuerySet(CSSParserTokenRange,
-                                           const CSSParserTokenOffsets&,
+  static MediaQuerySet* ParseMediaQuerySet(CSSParserTokenStream&,
                                            const ExecutionContext*);
-  static MediaQuerySet* ParseMediaCondition(CSSParserTokenRange,
-                                            const CSSParserTokenOffsets&,
+  static MediaQuerySet* ParseMediaCondition(CSSParserTokenStream&,
                                             const ExecutionContext*);
-  static MediaQuerySet* ParseMediaQuerySetInMode(CSSParserTokenRange,
-                                                 const CSSParserTokenOffsets&,
+  static MediaQuerySet* ParseMediaQuerySetInMode(CSSParserTokenStream&,
                                                  CSSParserMode,
                                                  const ExecutionContext*);
 
@@ -76,28 +76,25 @@
                    CSSParserMode,
                    const ExecutionContext*,
                    SyntaxLevel = SyntaxLevel::kAuto);
-  MediaQueryParser(const MediaQueryParser&) = delete;
-  MediaQueryParser& operator=(const MediaQueryParser&) = delete;
-  virtual ~MediaQueryParser();
 
   // [ not | only ]
-  static MediaQuery::RestrictorType ConsumeRestrictor(CSSParserTokenRange&);
+  static MediaQuery::RestrictorType ConsumeRestrictor(CSSParserTokenStream&);
 
   // https://drafts.csswg.org/mediaqueries-4/#typedef-media-type
-  static String ConsumeType(CSSParserTokenRange&);
+  static String ConsumeType(CSSParserTokenStream&);
 
   // https://drafts.csswg.org/mediaqueries-4/#typedef-mf-comparison
-  static MediaQueryOperator ConsumeComparison(CSSParserTokenRange&);
+  static MediaQueryOperator ConsumeComparison(CSSParserTokenStream&);
 
   // https://drafts.csswg.org/mediaqueries-4/#typedef-mf-name
   //
   // The <mf-name> is only consumed if the name is allowed by the specified
   // FeatureSet.
-  String ConsumeAllowedName(CSSParserTokenRange&, const FeatureSet&);
+  String ConsumeAllowedName(CSSParserTokenStream&, const FeatureSet&);
 
   // Like ConsumeAllowedName, except returns null if the name has a min-
   // or max- prefix.
-  String ConsumeUnprefixedName(CSSParserTokenRange&, const FeatureSet&);
+  String ConsumeUnprefixedName(CSSParserTokenStream&, const FeatureSet&);
 
   enum class NameAffinity {
     // <mf-name> appears on the left, e.g. width < 10px.
@@ -106,28 +103,10 @@
     kRight
   };
 
-  // Helper function for parsing features with a single MediaQueryOperator,
-  // for example 'width <= 10px', or '10px = width'.
-  //
-  // NameAffinity::kLeft means |lhs| will be interpreted as the <mf-name>,
-  // otherwise |rhs| will be interpreted as the <mf-name>.
-  //
-  // Note that this function accepts CSSParserTokenRanges by *value*, unlike
-  // Consume* functions, and that nullptr is returned if either |lhs|
-  // or |rhs| aren't fully consumed.
-  const MediaQueryExpNode* ParseNameValueComparison(
-      CSSParserTokenRange lhs,
-      MediaQueryOperator op,
-      CSSParserTokenRange rhs,
-      const CSSParserTokenOffsets& offsets,
-      NameAffinity,
-      const FeatureSet&);
-
   // https://drafts.csswg.org/mediaqueries-4/#typedef-media-feature
   //
   // Currently, only <mf-boolean> and <mf-plain> productions are supported.
-  const MediaQueryExpNode* ConsumeFeature(CSSParserTokenRange&,
-                                          const CSSParserTokenOffsets& offsets,
+  const MediaQueryExpNode* ConsumeFeature(CSSParserTokenStream&,
                                           const FeatureSet&);
 
   enum class ConditionMode {
@@ -139,29 +118,26 @@
 
   // https://drafts.csswg.org/mediaqueries-4/#typedef-media-condition
   const MediaQueryExpNode* ConsumeCondition(
-      CSSParserTokenRange&,
-      const CSSParserTokenOffsets&,
+      CSSParserTokenStream&,
       ConditionMode = ConditionMode::kNormal);
 
   // https://drafts.csswg.org/mediaqueries-4/#typedef-media-in-parens
-  const MediaQueryExpNode* ConsumeInParens(CSSParserTokenRange&,
-                                           const CSSParserTokenOffsets&);
+  const MediaQueryExpNode* ConsumeInParens(CSSParserTokenStream&);
 
   // https://drafts.csswg.org/mediaqueries-4/#typedef-general-enclosed
-  const MediaQueryExpNode* ConsumeGeneralEnclosed(CSSParserTokenRange&);
+  const MediaQueryExpNode* ConsumeGeneralEnclosed(CSSParserTokenStream&);
 
   // https://drafts.csswg.org/mediaqueries-4/#typedef-media-query
-  MediaQuery* ConsumeQuery(CSSParserTokenRange&, const CSSParserTokenOffsets&);
+  MediaQuery* ConsumeQuery(CSSParserTokenStream&);
 
   // Used for ParserType::kMediaConditionParser.
   //
   // Parsing a single condition is useful for the 'sizes' attribute.
   //
   // https://html.spec.whatwg.org/multipage/images.html#sizes-attribute
-  MediaQuerySet* ConsumeSingleCondition(CSSParserTokenRange,
-                                        const CSSParserTokenOffsets&);
+  MediaQuerySet* ConsumeSingleCondition(CSSParserTokenStream&);
 
-  MediaQuerySet* ParseImpl(CSSParserTokenRange, const CSSParserTokenOffsets&);
+  MediaQuerySet* ParseImpl(CSSParserTokenStream&);
 
   ParserType parser_type_;
   CSSParserMode mode_;
diff --git a/third_party/blink/renderer/core/css/parser/sizes_attribute_parser.cc b/third_party/blink/renderer/core/css/parser/sizes_attribute_parser.cc
index 30777fb..2ff1ff8 100644
--- a/third_party/blink/renderer/core/css/parser/sizes_attribute_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/sizes_attribute_parser.cc
@@ -24,20 +24,16 @@
   DCHECK(media_values_->Width().has_value());
   DCHECK(media_values_->Height().has_value());
 
-  CSSTokenizer tokenizer(attribute);
-  auto [tokens, offsets] = tokenizer.TokenizeToEOFWithOffsets();
-  is_valid_ =
-      Parse(CSSParserTokenRange(tokens),
-            CSSParserTokenOffsets(tokens, std::move(offsets), attribute));
+  CSSParserTokenStream stream(attribute);
+  is_valid_ = Parse(stream);
 }
 
-bool SizesAttributeParser::Parse(CSSParserTokenRange range,
-                                 const CSSParserTokenOffsets& offsets) {
-  // Split on a comma token and parse the result tokens as (media-condition,
-  // length) pairs
-  while (!range.AtEnd()) {
+bool SizesAttributeParser::Parse(CSSParserTokenStream& stream) {
+  while (!stream.AtEnd()) {
+    stream.ConsumeWhitespace();
+
     if (RuntimeEnabledFeatures::AutoSizeLazyLoadedImagesEnabled() &&
-        css_parsing_utils::AtIdent(range.Peek(), "auto")) {
+        css_parsing_utils::AtIdent(stream.Peek(), "auto")) {
       // Spec: "For better backwards-compatibility with legacy user
       // agents that don't support the auto keyword, fallback sizes
       // can be specified if desired."
@@ -46,35 +42,43 @@
       return true;
     }
 
-    const CSSParserToken* media_condition_start = &range.Peek();
-    // The length is the last component value before the comma which isn't
-    // whitespace or a comment
-    const CSSParserToken* length_token_start = &range.Peek();
-    const CSSParserToken* length_token_end = &range.Peek();
-    while (!range.AtEnd() && range.Peek().GetType() != kCommaToken) {
-      length_token_start = &range.Peek();
-      range.ConsumeComponentValue();
-      length_token_end = &range.Peek();
-      range.ConsumeWhitespace();
-    }
-    range.Consume();
-
-    float length;
-    if (!CalculateLengthInPixels(
-            range.MakeSubRange(length_token_start, length_token_end), length)) {
-      continue;
-    }
-
-    MediaQuerySet* media_condition = MediaQueryParser::ParseMediaCondition(
-        range.MakeSubRange(media_condition_start, length_token_start), offsets,
-        execution_context_);
+    CSSParserTokenStream::State savepoint = stream.Save();
+    MediaQuerySet* media_condition =
+        MediaQueryParser::ParseMediaCondition(stream, execution_context_);
     if (!media_condition || !MediaConditionMatches(*media_condition)) {
-      continue;
+      // If we failed to parse a media condition, most likely there
+      // simply wasn't any and we won't have moved in the stream.
+      // However, there are certain edge cases where we _thought_
+      // we would have parsed a media condition but it was actually
+      // meant as a size; in particular, a calc() expression would
+      // count as <general-enclosed> and thus be parsed as a media
+      // condition, then promptly fail, whereas we should really
+      // parse it as a size. Thus, we need to rewind in this case.
+      // If it really were a valid but failing media condition,
+      // this rewinding is harmless; we'd try parsing the media
+      // condition as a size and then fail (if nothing else, because
+      // the comma is not immediately after it).
+      stream.EnsureLookAhead();
+      stream.Restore(savepoint);
     }
 
-    size_ = length;
-    size_was_set_ = true;
-    return true;
+    if (stream.Peek().GetType() != kCommaToken) {
+      CSSParserTokenRange length_tokens = stream.ConsumeComponentValue();
+      stream.ConsumeWhitespace();
+      if (stream.AtEnd() || stream.Peek().GetType() == kCommaToken) {
+        float length;
+        if (CalculateLengthInPixels(length_tokens, length)) {
+          size_ = length;
+          size_was_set_ = true;
+          return true;
+        }
+      }
+    }
+
+    stream.SkipUntilPeekedTypeIs<kCommaToken>();
+    if (!stream.AtEnd()) {
+      stream.Consume();
+    }
   }
 
   return false;
diff --git a/third_party/blink/renderer/core/css/parser/sizes_attribute_parser.h b/third_party/blink/renderer/core/css/parser/sizes_attribute_parser.h
index 78b8940..a5782357 100644
--- a/third_party/blink/renderer/core/css/parser/sizes_attribute_parser.h
+++ b/third_party/blink/renderer/core/css/parser/sizes_attribute_parser.h
@@ -29,7 +29,7 @@
   float Size();
 
  private:
-  bool Parse(CSSParserTokenRange, const CSSParserTokenOffsets&);
+  bool Parse(CSSParserTokenStream&);
   float EffectiveSize();
   bool CalculateLengthInPixels(CSSParserTokenRange, float& result);
   bool MediaConditionMatches(const MediaQuerySet& media_condition);
diff --git a/third_party/blink/renderer/core/css/parser/sizes_attribute_parser_test.cc b/third_party/blink/renderer/core/css/parser/sizes_attribute_parser_test.cc
index 88be9f8..d564278 100644
--- a/third_party/blink/renderer/core/css/parser/sizes_attribute_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/sizes_attribute_parser_test.cc
@@ -107,7 +107,8 @@
 
   for (unsigned i = 0; test_cases[i].input; ++i) {
     SizesAttributeParser parser(media_values, test_cases[i].input, nullptr);
-    ASSERT_EQ(test_cases[i].effective_size, parser.Size());
+    EXPECT_EQ(test_cases[i].effective_size, parser.Size())
+        << test_cases[i].input;
   }
 }
 
@@ -174,7 +175,8 @@
 
   for (unsigned i = 0; test_cases[i].input; ++i) {
     SizesAttributeParser parser(media_values, test_cases[i].input, nullptr);
-    ASSERT_EQ(test_cases[i].effective_size, parser.Size());
+    EXPECT_EQ(test_cases[i].effective_size, parser.Size())
+        << test_cases[i].input;
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/properties/css_color_function_parser_test.cc b/third_party/blink/renderer/core/css/properties/css_color_function_parser_test.cc
index a6b089da..f0ea569 100644
--- a/third_party/blink/renderer/core/css/properties/css_color_function_parser_test.cc
+++ b/third_party/blink/renderer/core/css/properties/css_color_function_parser_test.cc
@@ -16,8 +16,7 @@
 
 TEST(ColorFunctionParserTest, RelativeColorWithKeywordBase) {
   const String test_case = "rgb(from red r g b)";
-  CSSTokenizer tokenizer(test_case);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(test_case);
 
   const CSSParserContext* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -33,8 +32,7 @@
 
 TEST(ColorFunctionParserTest, RelativeColorWithInvalidChannelReference) {
   const String test_case = "rgb(from red h s l)";
-  CSSTokenizer tokenizer(test_case);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(test_case);
 
   const CSSParserContext* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -50,8 +48,7 @@
       false);
 
   const String test_case = "rgb(from currentcolor r g b)";
-  CSSTokenizer tokenizer(test_case);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(test_case);
 
   const CSSParserContext* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -67,8 +64,7 @@
       true);
 
   const String test_case = "rgb(from currentcolor 1 calc(g) b)";
-  CSSTokenizer tokenizer(test_case);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(test_case);
 
   const CSSParserContext* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -108,8 +104,7 @@
 
   const String test_case =
       "rgb(from currentcolor 1 calc(g) b / calc(alpha / 2))";
-  CSSTokenizer tokenizer(test_case);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(test_case);
 
   const CSSParserContext* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -150,8 +145,7 @@
       true);
 
   const String test_case = "rgb(from currentcolor none none none / none)";
-  CSSTokenizer tokenizer(test_case);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(test_case);
 
   const CSSParserContext* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 33a0bc92..60f95ed2 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -962,26 +962,6 @@
   return contents;
 }
 
-bool ConsumeAnyValue(CSSParserTokenRange& range) {
-  bool result = IsTokenAllowedForAnyValue(range.Peek());
-  unsigned nesting_level = 0;
-
-  while (nesting_level || result) {
-    const CSSParserToken& token = range.Consume();
-    if (token.GetBlockType() == CSSParserToken::kBlockStart) {
-      nesting_level++;
-    } else if (token.GetBlockType() == CSSParserToken::kBlockEnd) {
-      nesting_level--;
-    }
-    if (range.AtEnd()) {
-      return result;
-    }
-    result = result && IsTokenAllowedForAnyValue(range.Peek());
-  }
-
-  return result;
-}
-
 namespace {
 
 bool ConsumeAnyComponentValue(CSSParserTokenStream& stream) {
@@ -4424,10 +4404,7 @@
                       CSSValueID::kExit, CSSValueID::kExitCrossing>(range);
 }
 
-template <typename T>
-  requires std::is_same_v<T, CSSParserTokenStream> ||
-           std::is_same_v<T, CSSParserTokenRange>
-CSSValue* ConsumeTimelineRangeNameAndPercent(T& stream,
+CSSValue* ConsumeTimelineRangeNameAndPercent(CSSParserTokenStream& stream,
                                              const CSSParserContext& context) {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   CSSValue* range_name = ConsumeTimelineRangeName(stream);
@@ -4444,10 +4421,6 @@
   return list;
 }
 
-template CSSValue* ConsumeTimelineRangeNameAndPercent(
-    CSSParserTokenRange& stream,
-    const CSSParserContext& context);
-
 CSSValue* ConsumeAnimationDelay(CSSParserTokenStream& stream,
                                 const CSSParserContext& context) {
   return ConsumeTime(stream, context, CSSPrimitiveValue::ValueRange::kAll);
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
index fdb30a1c..4c482318 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
@@ -95,7 +95,6 @@
 //
 // Consumes component values until it reaches a token that is not allowed
 // for <any-value>.
-CORE_EXPORT bool ConsumeAnyValue(CSSParserTokenRange&);
 CORE_EXPORT void ConsumeAnyValue(CSSParserTokenStream&);
 
 CSSPrimitiveValue* ConsumeInteger(
@@ -383,10 +382,8 @@
 // https://drafts.csswg.org/scroll-animations-1/#typedef-timeline-range-name
 CSSValue* ConsumeTimelineRangeName(CSSParserTokenStream&);
 CSSValue* ConsumeTimelineRangeName(CSSParserTokenRange&);
-template <typename T>
-  requires std::is_same_v<T, CSSParserTokenStream> ||
-           std::is_same_v<T, CSSParserTokenRange>
-CSSValue* ConsumeTimelineRangeNameAndPercent(T&, const CSSParserContext&);
+CSSValue* ConsumeTimelineRangeNameAndPercent(CSSParserTokenStream&,
+                                             const CSSParserContext&);
 CSSValue* ConsumeAnimationDelay(CSSParserTokenStream&, const CSSParserContext&);
 CSSValue* ConsumeAnimationRange(CSSParserTokenStream&,
                                 const CSSParserContext&,
@@ -752,12 +749,11 @@
          EqualIgnoringASCIICase(token.Value(), ident);
 }
 
-template <typename T>
-bool ConsumeIfIdent(T& range_or_stream, const char* ident) {
-  if (!AtIdent(range_or_stream.Peek(), ident)) {
+inline bool ConsumeIfIdent(CSSParserTokenStream& stream, const char* ident) {
+  if (!AtIdent(stream.Peek(), ident)) {
     return false;
   }
-  range_or_stream.ConsumeIncludingWhitespace();
+  stream.ConsumeIncludingWhitespace();
   return true;
 }
 
@@ -765,12 +761,11 @@
   return token.GetType() == kDelimiterToken && token.Delimiter() == c;
 }
 
-template <typename T>
-bool ConsumeIfDelimiter(T& range_or_stream, UChar c) {
-  if (!AtDelimiter(range_or_stream.Peek(), c)) {
+inline bool ConsumeIfDelimiter(CSSParserTokenStream& stream, UChar c) {
+  if (!AtDelimiter(stream.Peek(), c)) {
     return false;
   }
-  range_or_stream.ConsumeIncludingWhitespace();
+  stream.ConsumeIncludingWhitespace();
   return true;
 }
 
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc
index 2b5b889e..8328020 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc
@@ -91,8 +91,7 @@
 
 TEST(CSSParsingUtilsTest, AtIdent_Stream) {
   String text = "foo,bar,10px";
-  CSSTokenizer tokenizer(text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(text);
   EXPECT_FALSE(AtIdent(stream.Consume(), "bar"));  // foo
   EXPECT_FALSE(AtIdent(stream.Consume(), "bar"));  // ,
   EXPECT_TRUE(AtIdent(stream.Consume(), "bar"));   // bar
@@ -101,21 +100,9 @@
   EXPECT_FALSE(AtIdent(stream.Consume(), "bar"));  // EOF
 }
 
-TEST(CSSParsingUtilsTest, ConsumeIfIdent_Range) {
+TEST(CSSParsingUtilsTest, ConsumeIfIdent) {
   String text = "foo,bar,10px";
-  auto tokens = CSSTokenizer(text).TokenizeToEOF();
-  CSSParserTokenRange range(tokens);
-  EXPECT_TRUE(AtIdent(range.Peek(), "foo"));
-  EXPECT_FALSE(ConsumeIfIdent(range, "bar"));
-  EXPECT_TRUE(AtIdent(range.Peek(), "foo"));
-  EXPECT_TRUE(ConsumeIfIdent(range, "foo"));
-  EXPECT_EQ(kCommaToken, range.Peek().GetType());
-}
-
-TEST(CSSParsingUtilsTest, ConsumeIfIdent_Stream) {
-  String text = "foo,bar,10px";
-  CSSTokenizer tokenizer(text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(text);
   EXPECT_TRUE(AtIdent(stream.Peek(), "foo"));
   EXPECT_FALSE(ConsumeIfIdent(stream, "bar"));
   EXPECT_TRUE(AtIdent(stream.Peek(), "foo"));
@@ -123,22 +110,9 @@
   EXPECT_EQ(kCommaToken, stream.Peek().GetType());
 }
 
-TEST(CSSParsingUtilsTest, AtDelimiter_Range) {
+TEST(CSSParsingUtilsTest, AtDelimiter) {
   String text = "foo,<,10px";
-  auto tokens = CSSTokenizer(text).TokenizeToEOF();
-  CSSParserTokenRange range(tokens);
-  EXPECT_FALSE(AtDelimiter(range.Consume(), '<'));  // foo
-  EXPECT_FALSE(AtDelimiter(range.Consume(), '<'));  // ,
-  EXPECT_TRUE(AtDelimiter(range.Consume(), '<'));   // <
-  EXPECT_FALSE(AtDelimiter(range.Consume(), '<'));  // ,
-  EXPECT_FALSE(AtDelimiter(range.Consume(), '<'));  // 10px
-  EXPECT_FALSE(AtDelimiter(range.Consume(), '<'));  // EOF
-}
-
-TEST(CSSParsingUtilsTest, AtDelimiter_Stream) {
-  String text = "foo,<,10px";
-  CSSTokenizer tokenizer(text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(text);
   EXPECT_FALSE(AtDelimiter(stream.Consume(), '<'));  // foo
   EXPECT_FALSE(AtDelimiter(stream.Consume(), '<'));  // ,
   EXPECT_TRUE(AtDelimiter(stream.Consume(), '<'));   // <
@@ -147,21 +121,9 @@
   EXPECT_FALSE(AtDelimiter(stream.Consume(), '<'));  // EOF
 }
 
-TEST(CSSParsingUtilsTest, ConsumeIfDelimiter_Range) {
+TEST(CSSParsingUtilsTest, ConsumeIfDelimiter) {
   String text = "<,=,10px";
-  auto tokens = CSSTokenizer(text).TokenizeToEOF();
-  CSSParserTokenRange range(tokens);
-  EXPECT_TRUE(AtDelimiter(range.Peek(), '<'));
-  EXPECT_FALSE(ConsumeIfDelimiter(range, '='));
-  EXPECT_TRUE(AtDelimiter(range.Peek(), '<'));
-  EXPECT_TRUE(ConsumeIfDelimiter(range, '<'));
-  EXPECT_EQ(kCommaToken, range.Peek().GetType());
-}
-
-TEST(CSSParsingUtilsTest, ConsumeIfDelimiter_Stream) {
-  String text = "<,=,10px";
-  CSSTokenizer tokenizer(text);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(text);
   EXPECT_TRUE(AtDelimiter(stream.Peek(), '<'));
   EXPECT_FALSE(ConsumeIfDelimiter(stream, '='));
   EXPECT_TRUE(AtDelimiter(stream.Peek(), '<'));
@@ -169,42 +131,6 @@
   EXPECT_EQ(kCommaToken, stream.Peek().GetType());
 }
 
-TEST(CSSParsingUtilsTest, ConsumeAnyValue_Range) {
-  struct {
-    // The input string to parse as <any-value>.
-    const char* input;
-    // The expected result from ConsumeAnyValue.
-    bool expected;
-    // The serialization of the tokens remaining in the range.
-    const char* remainder;
-  } tests[] = {
-      {"1", true, ""},
-      {"1px", true, ""},
-      {"1px ", true, ""},
-      {"ident", true, ""},
-      {"(([ident]))", true, ""},
-      {" ( ( 1 ) ) ", true, ""},
-      {"rgb(1, 2, 3)", true, ""},
-      {"rgb(1, 2, 3", true, ""},
-      {"!!!;;;", true, ""},
-      {"asdf)", false, ")"},
-      {")asdf", false, ")asdf"},
-      {"(ab)cd) e", false, ") e"},
-      {"(as]df) e", false, " e"},
-      {"(a b [ c { d ) e } f ] g h) i", false, " i"},
-      {"a url(() b", false, "url(() b"},
-  };
-
-  for (const auto& test : tests) {
-    String input(test.input);
-    SCOPED_TRACE(input);
-    auto tokens = CSSTokenizer(input).TokenizeToEOF();
-    CSSParserTokenRange range(tokens);
-    EXPECT_EQ(test.expected, css_parsing_utils::ConsumeAnyValue(range));
-    EXPECT_EQ(String(test.remainder), range.Serialize());
-  }
-}
-
 TEST(CSSParsingUtilsTest, ConsumeAnyValue_Stream) {
   struct {
     // The input string to parse as <any-value>.
@@ -232,8 +158,7 @@
   for (const auto& test : tests) {
     String input(test.input);
     SCOPED_TRACE(input);
-    CSSTokenizer tokenizer(input);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(input);
     css_parsing_utils::ConsumeAnyValue(stream);
     EXPECT_EQ(String(test.remainder), stream.RemainingText().ToString());
   }
@@ -258,8 +183,7 @@
 
 TEST(CSSParsingUtilsTest, ConsumeAbsoluteColor) {
   auto ConsumeColorForTest = [](String css_text, auto func) {
-    CSSTokenizer tokenizer(css_text);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(css_text);
     CSSParserContext* context = MakeContext();
     return func(stream, *context);
   };
@@ -298,8 +222,7 @@
 
 TEST(CSSParsingUtilsTest, InternalColorsOnlyAllowedInUaMode) {
   auto ConsumeColorForTest = [](String css_text, CSSParserMode mode) {
-    CSSTokenizer tokenizer(css_text);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(css_text);
     return css_parsing_utils::ConsumeColor(stream, *MakeContext(mode));
   };
 
@@ -351,8 +274,7 @@
   for (const char*& test : tests) {
     String input(test);
     SCOPED_TRACE(input);
-    CSSTokenizer tokenizer(input);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(input);
     EXPECT_EQ(nullptr, css_parsing_utils::ConsumeColor(stream, *MakeContext()));
     EXPECT_EQ(test, stream.RemainingText());
   }
@@ -361,8 +283,7 @@
 TEST(CSSParsingUtilsTest, InternalPositionTryFallbacksInUAMode) {
   auto ConsumePositionTryFallbackForTest = [](String css_text,
                                               CSSParserMode mode) {
-    CSSTokenizer tokenizer(css_text);
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(css_text);
     return css_parsing_utils::ConsumeSinglePositionTryFallback(
         stream, *MakeContext(mode));
   };
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index c70c111..a805c68 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -114,7 +114,8 @@
   // Fractional unit.
   auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value);
   if (primitive_value && primitive_value->IsFlex()) {
-    return Length::Flex(primitive_value->GetFloatValue());
+    return Length::Flex(primitive_value->ComputeValueInCanonicalUnit(
+        state.CssToLengthConversionData()));
   }
 
   auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
@@ -614,23 +615,25 @@
 scoped_refptr<FontPalette> StyleBuilderConverter::ConvertFontPalette(
     StyleResolverState& state,
     const CSSValue& value) {
-  return StyleBuilderConverterBase::ConvertFontPalette(value);
+  return StyleBuilderConverterBase::ConvertFontPalette(
+      state.CssToLengthConversionData(), value);
 }
 
 scoped_refptr<FontPalette> StyleBuilderConverterBase::ConvertPaletteMix(
+    const CSSLengthResolver& length_resolver,
     const CSSValue& value) {
   DCHECK(RuntimeEnabledFeatures::FontPaletteAnimationEnabled());
 
   auto* palette_mix_value = DynamicTo<cssvalue::CSSPaletteMixValue>(value);
   if (palette_mix_value) {
     scoped_refptr<FontPalette> palette1 =
-        ConvertFontPalette(palette_mix_value->Palette1());
+        ConvertFontPalette(length_resolver, palette_mix_value->Palette1());
     if (palette1 == nullptr) {
       // Use normal palette.
       palette1 = FontPalette::Create();
     }
     scoped_refptr<FontPalette> palette2 =
-        ConvertFontPalette(palette_mix_value->Palette2());
+        ConvertFontPalette(length_resolver, palette_mix_value->Palette2());
     if (palette2 == nullptr) {
       palette2 = FontPalette::Create();
     }
@@ -644,18 +647,22 @@
     double normalized_percentage;
     if (cssvalue::CSSColorMixValue::NormalizePercentages(
             palette_mix_value->Percentage1(), palette_mix_value->Percentage2(),
-            normalized_percentage, alpha_multiplier)) {
+            normalized_percentage, alpha_multiplier, length_resolver)) {
       double percentage1 = kMiddleStatePercentage;
       double percentage2 = kMiddleStatePercentage;
       if (palette_mix_value->Percentage1() &&
           palette_mix_value->Percentage2()) {
-        percentage1 = palette_mix_value->Percentage1()->GetDoubleValue();
-        percentage2 = palette_mix_value->Percentage2()->GetDoubleValue();
+        percentage1 = palette_mix_value->Percentage1()->ComputePercentage(
+            length_resolver);
+        percentage2 = palette_mix_value->Percentage2()->ComputePercentage(
+            length_resolver);
       } else if (palette_mix_value->Percentage1()) {
-        percentage1 = palette_mix_value->Percentage1()->GetDoubleValue();
+        percentage1 = palette_mix_value->Percentage1()->ComputePercentage(
+            length_resolver);
         percentage2 = kFinalStatePercentage - percentage1;
       } else if (palette_mix_value->Percentage2()) {
-        percentage2 = palette_mix_value->Percentage2()->GetDoubleValue();
+        percentage2 = palette_mix_value->Percentage2()->ComputePercentage(
+            length_resolver);
         percentage1 = kFinalStatePercentage - percentage2;
       }
       return FontPalette::Mix(palette1, palette2, percentage1, percentage2,
@@ -667,6 +674,7 @@
 }
 
 scoped_refptr<FontPalette> StyleBuilderConverterBase::ConvertFontPalette(
+    const CSSLengthResolver& length_resolver,
     const CSSValue& value) {
   auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
   if (identifier_value &&
@@ -689,7 +697,7 @@
   }
 
   if (RuntimeEnabledFeatures::FontPaletteAnimationEnabled()) {
-    return ConvertPaletteMix(value);
+    return ConvertPaletteMix(length_resolver, value);
   } else {
     return nullptr;
   }
@@ -1776,10 +1784,12 @@
   } else if (const auto* primitive_value =
                  DynamicTo<CSSPrimitiveValue>(value)) {
     if (primitive_value->IsPercentage()) {
-      float percent = primitive_value->GetFloatValue();
+      float percent =
+          primitive_value->ComputePercentage(state.CssToLengthConversionData());
       return percent ? (percent / 100.0f) : 1.0f;
     } else if (primitive_value->IsNumber()) {
-      float number = primitive_value->GetFloatValue();
+      float number =
+          primitive_value->ComputeNumber(state.CssToLengthConversionData());
       return number ? number : 1.0f;
     }
   }
@@ -2253,7 +2263,8 @@
       black_text_link_colors.SetVisitedLinkColor(Color::kBlack);
       black_text_link_colors.SetActiveLinkColor(Color::kBlack);
 
-      color = ResolveColorValue(*shadow.color, black_text_link_colors,
+      color = ResolveColorValue(conversion_data, *shadow.color,
+                                black_text_link_colors,
                                 mojom::blink::ColorScheme::kLight, nullptr,
                                 /*is_in_web_app_scope=*/false,
                                 /*for_visited_link=*/false);
@@ -2387,7 +2398,8 @@
   return MakeGarbageCollected<ScopedCSSNameList>(std::move(names));
 }
 
-StyleColor ResolveColorValue(const CSSValue& value,
+StyleColor ResolveColorValue(const CSSLengthResolver& length_resolver,
+                             const CSSValue& value,
                              const TextLinkColors& text_link_colors,
                              mojom::blink::ColorScheme used_color_scheme,
                              const ui::ColorProvider* color_provider,
@@ -2419,12 +2431,14 @@
   }
 
   if (auto* color_mix_value = DynamicTo<cssvalue::CSSColorMixValue>(value)) {
-    const StyleColor style_color1 = ResolveColorValue(
-        color_mix_value->Color1(), text_link_colors, used_color_scheme,
-        color_provider, is_in_web_app_scope, for_visited_link);
-    const StyleColor style_color2 = ResolveColorValue(
-        color_mix_value->Color2(), text_link_colors, used_color_scheme,
-        color_provider, is_in_web_app_scope, for_visited_link);
+    const StyleColor style_color1 =
+        ResolveColorValue(length_resolver, color_mix_value->Color1(),
+                          text_link_colors, used_color_scheme, color_provider,
+                          is_in_web_app_scope, for_visited_link);
+    const StyleColor style_color2 =
+        ResolveColorValue(length_resolver, color_mix_value->Color2(),
+                          text_link_colors, used_color_scheme, color_provider,
+                          is_in_web_app_scope, for_visited_link);
     // If neither color is "currentcolor" (or a color-mix function containing a
     // currentcolor) then color-mix functions can be resolved right now like
     // other colors. Otherwise we need to store an unresolved value on
@@ -2438,7 +2452,8 @@
     double mix_amount = 0.0;
     // TODO(crbug.com/40238188): Not sure what is appropriate to return when
     // both mix amounts are zero.
-    color_mix_value->NormalizePercentages(mix_amount, alpha_multiplier);
+    color_mix_value->NormalizePercentages(mix_amount, alpha_multiplier,
+                                          length_resolver);
     return StyleColor(MakeGarbageCollected<StyleColor::UnresolvedColorMix>(
         color_mix_value->ColorInterpolationSpace(),
         color_mix_value->HueInterpolationMethod(), style_color1, style_color2,
@@ -2448,10 +2463,10 @@
   if (auto* relative_color_value =
           DynamicTo<cssvalue::CSSRelativeColorValue>(value)) {
     // TODO(crbug.com/325309578): Convert unresolved relative color values.
-    return ResolveColorValue(relative_color_value->OriginColor(),
-                             text_link_colors, used_color_scheme,
-                             color_provider, is_in_web_app_scope,
-                             for_visited_link);
+    return ResolveColorValue(
+        length_resolver, relative_color_value->OriginColor(), text_link_colors,
+        used_color_scheme, color_provider, is_in_web_app_scope,
+        for_visited_link);
   }
 
   auto& light_dark_pair = To<CSSLightDarkValuePair>(value);
@@ -2459,9 +2474,9 @@
       used_color_scheme == mojom::blink::ColorScheme::kLight
           ? light_dark_pair.First()
           : light_dark_pair.Second();
-  return ResolveColorValue(color_value, text_link_colors, used_color_scheme,
-                           color_provider, is_in_web_app_scope,
-                           for_visited_link);
+  return ResolveColorValue(length_resolver, color_value, text_link_colors,
+                           used_color_scheme, color_provider,
+                           is_in_web_app_scope, for_visited_link);
 }
 
 StyleColor StyleBuilderConverter::ConvertStyleColor(StyleResolverState& state,
@@ -2470,7 +2485,8 @@
   mojom::blink::ColorScheme color_scheme =
       state.StyleBuilder().UsedColorScheme();
   auto& document = state.GetDocument();
-  return ResolveColorValue(value, document.GetTextLinkColors(), color_scheme,
+  return ResolveColorValue(state.CssToLengthConversionData(), value,
+                           document.GetTextLinkColors(), color_scheme,
                            document.GetColorProviderForPainting(color_scheme),
                            document.IsInWebAppScope(), for_visited_link);
 }
@@ -2893,14 +2909,15 @@
 }
 
 static const CSSValue& ComputeColorValue(
+    const CSSLengthResolver& length_resolver,
     const CSSValue& color_value,
     const Document& document,
     mojom::blink::ColorScheme color_scheme) {
   const bool kNotForVisitedLink = false;
-  const StyleColor style_color =
-      ResolveColorValue(color_value, document.GetTextLinkColors(), color_scheme,
-                        document.GetColorProviderForPainting(color_scheme),
-                        document.IsInWebAppScope(), kNotForVisitedLink);
+  const StyleColor style_color = ResolveColorValue(
+      length_resolver, color_value, document.GetTextLinkColors(), color_scheme,
+      document.GetColorProviderForPainting(color_scheme),
+      document.IsInWebAppScope(), kNotForVisitedLink);
   return *ComputedStyleUtils::ValueForColor(style_color);
 }
 
@@ -2989,7 +3006,8 @@
       return value;
     }
     if (StyleColor::IsColorKeyword(value_id)) {
-      return ComputeColorValue(*identifier_value, document, color_scheme);
+      return ComputeColorValue(state->CssToLengthConversionData(),
+                               *identifier_value, document, color_scheme);
     }
   }
 
@@ -3011,7 +3029,8 @@
   }
 
   if (auto* color_mix_value = DynamicTo<cssvalue::CSSColorMixValue>(value)) {
-    return ComputeColorValue(*color_mix_value, document, color_scheme);
+    return ComputeColorValue(state->CssToLengthConversionData(),
+                             *color_mix_value, document, color_scheme);
   }
 
   return value;
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
index 6b8a37f..f9c4adb1 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
@@ -107,8 +107,10 @@
   static DynamicRangeLimit ConvertDynamicRangeLimit(const CSSValue&);
   static FontSizeAdjust ConvertFontSizeAdjust(const StyleResolverState&,
                                               const CSSValue&);
-  static scoped_refptr<FontPalette> ConvertFontPalette(const CSSValue&);
-  static scoped_refptr<FontPalette> ConvertPaletteMix(const CSSValue&);
+  static scoped_refptr<FontPalette> ConvertFontPalette(const CSSLengthResolver&,
+                                                       const CSSValue&);
+  static scoped_refptr<FontPalette> ConvertPaletteMix(const CSSLengthResolver&,
+                                                      const CSSValue&);
 };
 
 // Note that we assume the parser only allows valid CSSValue types.
@@ -513,7 +515,8 @@
 // `value` is the result of parsing a <color> value.
 // See: https://drafts.csswg.org/css-color/#resolving-color-values
 CORE_EXPORT StyleColor
-ResolveColorValue(const CSSValue& value,
+ResolveColorValue(const CSSLengthResolver& length_resolver,
+                  const CSSValue& value,
                   const TextLinkColors& text_link_colors,
                   mojom::blink::ColorScheme used_color_scheme,
                   const ui::ColorProvider* color_provider,
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade.cc b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
index 5fb6a50..1e7e1c4 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
@@ -219,8 +219,7 @@
     return CSSParserToken(kStringToken, attribute_value);
   }
 
-  CSSTokenizer tokenizer(attribute_value);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(attribute_value);
 
   std::optional<CSSSyntaxDefinition> syntax_definition =
       attribute_type.ConvertToCSSSyntaxDefinition();
@@ -1133,8 +1132,7 @@
   //
   // https://drafts.csswg.org/css-variables/#substitute-a-var
   {
-    CSSTokenizer tokenizer(data->OriginalText());
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(data->OriginalText());
     stream.ConsumeWhitespace();
     CSSValue* value = css_parsing_utils::ConsumeCSSWideKeyword(stream);
     if (value && stream.AtEnd()) {
@@ -1164,16 +1162,14 @@
 
   TokenSequence sequence;
 
-  CSSTokenizer tokenizer(data->OriginalText());
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(data->OriginalText());
   if (ResolveTokensInto(stream, resolver, *context, FunctionContext{},
                         sequence)) {
     // TODO(sesse): It would be nice if we had some way of combining
     // ResolveTokensInto() and the re-tokenization. This is basically
     // what we pay by using the streaming parser everywhere; we tokenize
     // everything involving variable references twice.
-    CSSTokenizer tokenizer2(sequence.OriginalText());
-    CSSParserTokenStream stream2(tokenizer2);
+    CSSParserTokenStream stream2(sequence.OriginalText());
     if (const auto* parsed = Parse(property, stream2, context)) {
       return parsed;
     }
@@ -1207,8 +1203,7 @@
 
     TokenSequence sequence;
 
-    CSSTokenizer tokenizer(shorthand_data->OriginalText());
-    CSSParserTokenStream stream(tokenizer);
+    CSSParserTokenStream stream(shorthand_data->OriginalText());
     if (!ResolveTokensInto(stream, resolver,
                            *GetParserContext(*shorthand_value),
                            FunctionContext{}, sequence)) {
@@ -1219,8 +1214,7 @@
 
     // NOTE: We don't actually need the original text to be comment-stripped,
     // since we're not storing it in a custom property anywhere.
-    CSSTokenizer tokenizer2(sequence.OriginalText());
-    CSSParserTokenStream stream2(tokenizer2);
+    CSSParserTokenStream stream2(sequence.OriginalText());
     if (!CSSPropertyParser::ParseValue(
             shorthand_property_id, /*allow_important_annotation=*/false,
             stream2, shorthand_value->ParserContext(), parsed_properties,
@@ -1375,8 +1369,7 @@
 
   TokenSequence sequence(data);
 
-  CSSTokenizer tokenizer(data->OriginalText());
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(data->OriginalText());
   if (!ResolveTokensInto(stream, resolver, context, FunctionContext{},
                          sequence)) {
     return nullptr;
@@ -1576,8 +1569,7 @@
     return false;
   }
   // Urggg
-  CSSTokenizer tokenizer(ret_value->CssText());
-  CSSParserTokenStream ret_value_stream(tokenizer);
+  CSSParserTokenStream ret_value_stream(ret_value->CssText());
   return ResolveTokensInto(ret_value_stream, resolver, context,
                            FunctionContext{}, out);
 }
@@ -1607,8 +1599,7 @@
         kCalcStart);
   }
 
-  CSSTokenizer tokenizer(expr);
-  CSSParserTokenStream argument_stream(tokenizer);
+  CSSParserTokenStream argument_stream(expr);
   if (!ResolveTokensInto(argument_stream, resolver, context, function_context,
                          resolved_expr)) {
     return nullptr;
@@ -1680,8 +1671,7 @@
     return false;
   }
 
-  CSSTokenizer tokenizer(it->value->CssText());
-  CSSParserTokenStream arg_value_stream(tokenizer);
+  CSSParserTokenStream arg_value_stream(it->value->CssText());
   return ResolveTokensInto(arg_value_stream, resolver, context,
                            FunctionContext{}, out);
 }
diff --git a/third_party/blink/renderer/core/css/style_rule.cc b/third_party/blink/renderer/core/css/style_rule.cc
index d0ab772d..03154627 100644
--- a/third_party/blink/renderer/core/css/style_rule.cc
+++ b/third_party/blink/renderer/core/css/style_rule.cc
@@ -688,8 +688,7 @@
                                     StyleSheetContents* style_sheet) {
   auto* parser_context =
       MakeGarbageCollected<CSSParserContext>(*execution_context);
-  CSSTokenizer tokenizer(value);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(value);
 
   style_scope_ =
       StyleScope::Parse(stream, parser_context, nesting_type,
@@ -864,8 +863,7 @@
 void StyleRuleSupports::SetConditionText(
     const ExecutionContext* execution_context,
     String value) {
-  CSSTokenizer tokenizer(value);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(value);
   auto* context = MakeGarbageCollected<CSSParserContext>(*execution_context);
   CSSParserImpl parser(context);
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 1688657e..6aef1d5 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -221,7 +221,6 @@
 #include "third_party/blink/renderer/core/frame/viewport_data.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
 #include "third_party/blink/renderer/core/html/anchor_element_metrics_sender.h"
-#include "third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_font_cache.h"
 #include "third_party/blink/renderer/core/html/collection_type.h"
 #include "third_party/blink/renderer/core/html/custom/custom_element.h"
@@ -3953,14 +3952,6 @@
 
   if (SvgExtensions())
     AccessSVGExtensions().StartAnimations();
-
-  if (base::FeatureList::IsEnabled(
-          blink::features::kSpeculativeServiceWorkerWarmUp)) {
-    if (auto* observer =
-            AnchorElementObserverForServiceWorker::From(TopDocument())) {
-      observer->MaybeSendPendingWarmUpRequests();
-    }
-  }
 }
 
 static bool AllDescendantsAreComplete(Document* document) {
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 1caea06..7776e8d 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -9602,6 +9602,12 @@
 void Element::LogAddElementIfIsolatedWorldAndInDocument(
     const char element[],
     const QualifiedName& attr1) {
+  // TODO(crbug.com/361461518): Investigate the root cause of execution context
+  // is unexpectedly null.
+  if (!GetDocument().GetExecutionContext()) {
+    return;
+  }
+
   if (!isConnected() ||
       !V8DOMActivityLogger::HasActivityLoggerInIsolatedWorlds()) {
     return;
diff --git a/third_party/blink/renderer/core/dom/focusgroup_flags.cc b/third_party/blink/renderer/core/dom/focusgroup_flags.cc
index de9b252..711992a 100644
--- a/third_party/blink/renderer/core/dom/focusgroup_flags.cc
+++ b/third_party/blink/renderer/core/dom/focusgroup_flags.cc
@@ -5,7 +5,6 @@
 #include "third_party/blink/renderer/core/dom/focusgroup_flags.h"
 
 #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-blink.h"
-#include "third_party/blink/public/platform/web_string.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/flat_tree_traversal.h"
@@ -82,7 +81,7 @@
 
       // We don't use |lowercase_token| here since that string value will be
       // logged in the console and we want it to match the input.
-      invalid_tokens.Append(WTF::String::FromUTF8(tokens[i].Ascii()));
+      invalid_tokens.Append(tokens[i]);
     }
   }
 
@@ -91,8 +90,8 @@
         MakeGarbageCollected<ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kError,
-            WebString::FromUTF8("Unrecognized focusgroup attribute values: " +
-                                invalid_tokens.ToString().Ascii())));
+            "Unrecognized focusgroup attribute values: " +
+                invalid_tokens.ReleaseString()));
   }
 
   FocusgroupFlags flags = FocusgroupFlags::kNone;
@@ -112,10 +111,8 @@
             MakeGarbageCollected<ConsoleMessage>(
                 mojom::blink::ConsoleMessageSource::kOther,
                 mojom::blink::ConsoleMessageLevel::kError,
-                WebString::FromUTF8(
-                    "Focusgroup attribute value 'extend' present, "
-                    "but grid focusgroups cannot be extended. Ignoring "
-                    "focusgroup.")));
+                "Focusgroup attribute value 'extend' present, but grid "
+                "focusgroups cannot be extended. Ignoring focusgroup."));
         return FocusgroupFlags::kNone;
       }
     } else {
@@ -123,9 +120,8 @@
           MakeGarbageCollected<ConsoleMessage>(
               mojom::blink::ConsoleMessageSource::kOther,
               mojom::blink::ConsoleMessageLevel::kError,
-              WebString::FromUTF8(
-                  "Focusgroup attribute value 'extend' present, "
-                  "but no parent focusgroup found. Ignoring 'extend'.")));
+              "Focusgroup attribute value 'extend' present, but no parent "
+              "focusgroup found. Ignoring 'extend'."));
     }
   }
 
@@ -138,9 +134,8 @@
           MakeGarbageCollected<ConsoleMessage>(
               mojom::blink::ConsoleMessageSource::kOther,
               mojom::blink::ConsoleMessageLevel::kError,
-              WebString::FromUTF8(
-                  "Focusgroup attribute values 'extend' and 'grid' present, "
-                  "but grid focusgroup cannot extend. Ignoring focusgroup.")));
+              "Focusgroup attribute values 'extend' and 'grid' present, but "
+              "grid focusgroup cannot extend. Ignoring focusgroup."));
       return FocusgroupFlags::kNone;
     }
 
@@ -154,18 +149,16 @@
             MakeGarbageCollected<ConsoleMessage>(
                 mojom::blink::ConsoleMessageSource::kOther,
                 mojom::blink::ConsoleMessageLevel::kWarning,
-                WebString::FromUTF8(
-                    "Focusgroup attribute value 'row-wrap' present, but can be "
-                    "omitted because focusgroup already wraps in both axes.")));
+                "Focusgroup attribute value 'row-wrap' present, but can be "
+                "omitted because focusgroup already wraps in both axes."));
       }
       if (has_col_wrap) {
         element->GetDocument().AddConsoleMessage(
             MakeGarbageCollected<ConsoleMessage>(
                 mojom::blink::ConsoleMessageSource::kOther,
                 mojom::blink::ConsoleMessageLevel::kWarning,
-                WebString::FromUTF8(
-                    "Focusgroup attribute value 'col-wrap' present, but can be "
-                    "omitted because focusgroup already wraps in both axes.")));
+                "Focusgroup attribute value 'col-wrap' present, but can be "
+                "omitted because focusgroup already wraps in both axes."));
       }
     } else {
       if (has_row_wrap)
@@ -178,9 +171,8 @@
             MakeGarbageCollected<ConsoleMessage>(
                 mojom::blink::ConsoleMessageSource::kOther,
                 mojom::blink::ConsoleMessageLevel::kWarning,
-                WebString::FromUTF8(
-                    "Focusgroup attribute values 'row-wrap col-wrap' should be "
-                    "replaced by 'wrap'.")));
+                "Focusgroup attribute values 'row-wrap col-wrap' should be "
+                "replaced by 'wrap'."));
       }
     }
 
@@ -191,28 +183,25 @@
                                                  ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kError,
-            WebString::FromUTF8(
-                "Focusgroup attribute value 'flow' present, "
-                "but focusgroup already set to wrap in at least one axis.")));
+            "Focusgroup attribute value 'flow' present, but focusgroup already "
+            "set to wrap in at least one axis."));
       } else {
         flags |= FocusgroupFlags::kRowFlow | FocusgroupFlags::kColFlow;
         if (has_row_flow) {
-          element->GetDocument().AddConsoleMessage(MakeGarbageCollected<
-                                                   ConsoleMessage>(
-              mojom::blink::ConsoleMessageSource::kOther,
-              mojom::blink::ConsoleMessageLevel::kWarning,
-              WebString::FromUTF8(
+          element->GetDocument().AddConsoleMessage(
+              MakeGarbageCollected<ConsoleMessage>(
+                  mojom::blink::ConsoleMessageSource::kOther,
+                  mojom::blink::ConsoleMessageLevel::kWarning,
                   "Focusgroup attribute value 'row-flow' present, but can be "
-                  "omitted because focusgroup already flows in both axes.")));
+                  "omitted because focusgroup already flows in both axes."));
         }
         if (has_col_flow) {
-          element->GetDocument().AddConsoleMessage(MakeGarbageCollected<
-                                                   ConsoleMessage>(
-              mojom::blink::ConsoleMessageSource::kOther,
-              mojom::blink::ConsoleMessageLevel::kWarning,
-              WebString::FromUTF8(
+          element->GetDocument().AddConsoleMessage(
+              MakeGarbageCollected<ConsoleMessage>(
+                  mojom::blink::ConsoleMessageSource::kOther,
+                  mojom::blink::ConsoleMessageLevel::kWarning,
                   "Focusgroup attribute value 'col-flow' present, but can be "
-                  "omitted because focusgroup already flows in both axes.")));
+                  "omitted because focusgroup already flows in both axes."));
         }
       }
     } else {
@@ -222,9 +211,8 @@
               MakeGarbageCollected<ConsoleMessage>(
                   mojom::blink::ConsoleMessageSource::kOther,
                   mojom::blink::ConsoleMessageLevel::kError,
-                  WebString::FromUTF8(
-                      "Focusgroup attribute value 'row-flow' present, "
-                      "but focusgroup already wraps in the row axis.")));
+                  "Focusgroup attribute value 'row-flow' present, but "
+                  "focusgroup already wraps in the row axis."));
         } else {
           flags |= FocusgroupFlags::kRowFlow;
         }
@@ -235,9 +223,8 @@
               MakeGarbageCollected<ConsoleMessage>(
                   mojom::blink::ConsoleMessageSource::kOther,
                   mojom::blink::ConsoleMessageLevel::kError,
-                  WebString::FromUTF8(
-                      "Focusgroup attribute value 'col-flow' present, "
-                      "but focusgroup already wraps in the column axis.")));
+                  "Focusgroup attribute value 'col-flow' present, but "
+                  "focusgroup already wraps in the column axis."));
         } else {
           flags |= FocusgroupFlags::kColFlow;
         }
@@ -248,9 +235,8 @@
             MakeGarbageCollected<ConsoleMessage>(
                 mojom::blink::ConsoleMessageSource::kOther,
                 mojom::blink::ConsoleMessageLevel::kWarning,
-                WebString::FromUTF8(
-                    "Focusgroup attribute values 'row-flow col-flow' should be "
-                    "replaced by 'flow'.")));
+                "Focusgroup attribute values 'row-flow col-flow' should be "
+                "replaced by 'flow'."));
       }
     }
 
@@ -260,18 +246,16 @@
           MakeGarbageCollected<ConsoleMessage>(
               mojom::blink::ConsoleMessageSource::kOther,
               mojom::blink::ConsoleMessageLevel::kError,
-              WebString::FromUTF8(
-                  "Focusgroup attribute value 'inline' present, "
-                  "but no has no effect on grid focusgroups.")));
+              "Focusgroup attribute value 'inline' present, but has no effect "
+              "on grid focusgroups."));
     }
     if (has_block) {
       element->GetDocument().AddConsoleMessage(
           MakeGarbageCollected<ConsoleMessage>(
               mojom::blink::ConsoleMessageSource::kOther,
               mojom::blink::ConsoleMessageLevel::kError,
-              WebString::FromUTF8(
-                  "Focusgroup attribute value 'block' present, "
-                  "but no has no effect on grid focusgroups.")));
+              "Focusgroup attribute value 'block' present, but has no effect "
+              "on grid focusgroups."));
     }
 
     return flags;
@@ -285,45 +269,40 @@
         MakeGarbageCollected<ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kError,
-            WebString::FromUTF8(
-                "Focusgroup attribute value 'row-wrap' present, "
-                "but no has no effect on linear focusgroups.")));
+            "Focusgroup attribute value 'row-wrap' present, but has no effect "
+            "on linear focusgroups."));
   }
   if (has_col_wrap) {
     element->GetDocument().AddConsoleMessage(
         MakeGarbageCollected<ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kError,
-            WebString::FromUTF8(
-                "Focusgroup attribute value 'col-wrap' present, "
-                "but no has no effect on linear focusgroups.")));
+            "Focusgroup attribute value 'col-wrap' present, but has no effect "
+            "on linear focusgroups."));
   }
   if (has_flow) {
     element->GetDocument().AddConsoleMessage(
         MakeGarbageCollected<ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kError,
-            WebString::FromUTF8(
-                "Focusgroup attribute value 'flow' present, "
-                "but no has no effect on linear focusgroups.")));
+            "Focusgroup attribute value 'flow' present, but has no effect on "
+            "linear focusgroups."));
   }
   if (has_row_flow) {
     element->GetDocument().AddConsoleMessage(
         MakeGarbageCollected<ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kError,
-            WebString::FromUTF8(
-                "Focusgroup attribute value 'row-flow' present, "
-                "but no has no effect on linear focusgroups.")));
+            "Focusgroup attribute value 'row-flow' present, but has no effect "
+            "on linear focusgroups."));
   }
   if (has_col_flow) {
     element->GetDocument().AddConsoleMessage(
         MakeGarbageCollected<ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kError,
-            WebString::FromUTF8(
-                "Focusgroup attribute value 'col-flow' present, "
-                "but no has no effect on linear focusgroups.")));
+            "Focusgroup attribute value 'col-flow' present, but has no effect "
+            "on linear focusgroups."));
   }
 
   // 4. Set the axis supported on that focusgroup.
@@ -341,14 +320,12 @@
   }
 
   if (has_inline && has_block) {
-    element->GetDocument().AddConsoleMessage(
-        MakeGarbageCollected<ConsoleMessage>(
-            mojom::blink::ConsoleMessageSource::kOther,
-            mojom::blink::ConsoleMessageLevel::kWarning,
-            WebString::FromUTF8(
-                "'inline' and 'block' focusgroup attribute values used "
-                "together are redundant (this is the default behavior) and can "
-                "be omitted.")));
+    element->GetDocument().AddConsoleMessage(MakeGarbageCollected<
+                                             ConsoleMessage>(
+        mojom::blink::ConsoleMessageSource::kOther,
+        mojom::blink::ConsoleMessageLevel::kWarning,
+        "'inline' and 'block' focusgroup attribute values used together "
+        "are redundant (this is the default behavior) and can be omitted."));
   }
 
   // 6. Determine in what axis a focusgroup should wrap. This needs to be
@@ -371,10 +348,9 @@
                                                  ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kWarning,
-            WebString::FromUTF8(
-                "Focusgroup attribute value 'wrap' present but ignored. 'wrap' "
-                "has no effect when set on a focusgroup that extends another "
-                "one in both axes.")));
+            "Focusgroup attribute value 'wrap' present but ignored. 'wrap' has "
+            "no effect when set on a focusgroup that extends another one in "
+            "both axes."));
       }
     } else {
       if (flags & FocusgroupFlags::kInline) {
@@ -398,9 +374,8 @@
                                                ConsoleMessage>(
           mojom::blink::ConsoleMessageSource::kOther,
           mojom::blink::ConsoleMessageLevel::kWarning,
-          WebString::FromUTF8(
-              "Focusgroup attribute value 'wrap' present but ignored. 'wrap' "
-              "is inherited from the extended parent focusgroup.")));
+          "Focusgroup attribute value 'wrap' present but ignored. 'wrap' is "
+          "inherited from the extended parent focusgroup."));
     }
     if (flags & FocusgroupFlags::kInline) {
       flags |= (ancestor_flags & FocusgroupFlags::kWrapInline);
diff --git a/third_party/blink/renderer/core/dom/scroll_marker_group_pseudo_element.cc b/third_party/blink/renderer/core/dom/scroll_marker_group_pseudo_element.cc
index fb001e4..ac0d4d0 100644
--- a/third_party/blink/renderer/core/dom/scroll_marker_group_pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/scroll_marker_group_pseudo_element.cc
@@ -116,10 +116,6 @@
 }
 
 void ScrollMarkerGroupPseudoElement::ClearFocusGroup() {
-  if (selected_marker_) {
-    selected_marker_->SetSelected(false);
-    selected_marker_ = nullptr;
-  }
   focus_group_.clear();
 }
 
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.cc b/third_party/blink/renderer/core/editing/editing_utilities.cc
index 5bfe08d..c82c752 100644
--- a/third_party/blink/renderer/core/editing/editing_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_utilities.cc
@@ -1668,6 +1668,10 @@
   Element* const target = FindEventTargetFrom(
       frame, frame.Selection().ComputeVisibleSelectionInDOMTree());
 
+  // Copy the original target text into a string, in case the 'beforeinput'
+  // event handler modifies the text.
+  const String before_input_target_string = target->GetInnerTextWithoutUpdate();
+
   DataTransfer* const data_transfer = DataTransfer::Create(
       DataTransfer::DataTransferType::kInsertReplacementText,
       DataTransferAccessPolicy::kReadable,
@@ -1683,6 +1687,12 @@
     return;
   }
 
+  // If the 'beforeinput' event handler has modified the input text, then the
+  // replacement text shouldn't be inserted.
+  if (target->innerText() != before_input_target_string) {
+    return;
+  }
+
   // When allowed, insert the text into the active edit context if it exists.
   if (auto* edit_context =
           frame.GetInputMethodController().GetActiveEditContext()) {
diff --git a/third_party/blink/renderer/core/exported/web_frame_serializer.cc b/third_party/blink/renderer/core/exported/web_frame_serializer.cc
index c44a4201..5c564314 100644
--- a/third_party/blink/renderer/core/exported/web_frame_serializer.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_serializer.cc
@@ -40,7 +40,6 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/frame/frame_serializer.h"
-#include "third_party/blink/renderer/core/frame/frame_serializer_delegate_impl.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/web_frame_serializer_impl.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
@@ -93,10 +92,7 @@
                      "WebFrameSerializer::generateMHTMLParts serializing");
   Deque<SerializedResource> resources;
   {
-    HeapHashSet<WeakMember<const Element>> shadow_template_elements;
-    FrameSerializerDelegateImpl core_delegate(*web_delegate,
-                                              shadow_template_elements);
-    FrameSerializer serializer(resources, core_delegate);
+    FrameSerializer serializer(resources, web_delegate);
     serializer.SerializeFrame(*frame);
   }
 
@@ -114,8 +110,8 @@
     // Frame is the 1st resource (see FrameSerializer::serializeFrame doc
     // comment). Frames get a Content-ID header.
     MHTMLArchive::GenerateMHTMLPart(
-        boundary, FrameSerializerDelegateImpl::GetContentID(frame),
-        encoding_policy, resources.TakeFirst(), *output->MutableData());
+        boundary, FrameSerializer::GetContentID(frame), encoding_policy,
+        resources.TakeFirst(), *output->MutableData());
     while (!resources.empty()) {
       TRACE_EVENT0("page-serialization",
                    "WebFrameSerializer::generateMHTMLParts encoding");
diff --git a/third_party/blink/renderer/core/exported/web_media_player_impl_unittest.cc b/third_party/blink/renderer/core/exported/web_media_player_impl_unittest.cc
index 349c59b1..cca555c 100644
--- a/third_party/blink/renderer/core/exported/web_media_player_impl_unittest.cc
+++ b/third_party/blink/renderer/core/exported/web_media_player_impl_unittest.cc
@@ -165,20 +165,8 @@
   MOCK_METHOD0(DurationChanged, void());
   MOCK_METHOD0(SizeChanged, void());
   MOCK_METHOD1(SetCcLayer, void(cc::Layer*));
-  MOCK_METHOD5(AddAudioTrack,
-               WebMediaPlayer::TrackId(const WebString&,
-                                       WebMediaPlayerClient::AudioTrackKind,
-                                       const WebString&,
-                                       const WebString&,
-                                       bool));
-  MOCK_METHOD1(RemoveAudioTrack, void(WebMediaPlayer::TrackId));
-  MOCK_METHOD5(AddVideoTrack,
-               WebMediaPlayer::TrackId(const WebString&,
-                                       WebMediaPlayerClient::VideoTrackKind,
-                                       const WebString&,
-                                       const WebString&,
-                                       bool));
-  MOCK_METHOD1(RemoveVideoTrack, void(WebMediaPlayer::TrackId));
+  MOCK_METHOD1(AddMediaTrack, void(const media::MediaTrack& track));
+  MOCK_METHOD1(RemoveMediaTrack, void(const media::MediaTrack&));
   MOCK_METHOD1(MediaSourceOpened, void(std::unique_ptr<WebMediaSource>));
   MOCK_METHOD2(RemotePlaybackCompatibilityChanged, void(const WebURL&, bool));
   MOCK_METHOD0(WasAlwaysMuted, bool());
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
index 23e153c..9f6cf0f 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -765,6 +765,28 @@
     return false;
   }
 
+  network::mojom::AttributionSupport support =
+      request.GetAttributionReportingSupport();
+
+  // This could occur for responses loaded from memory cache.
+  if (support == network::mojom::AttributionSupport::kUnset) {
+    // `ResourceFetcher::DidLoadResourceFromMemoryCache()` early returns for
+    // detached frames. We log metrics here to verify that this is never hit in
+    // detached frames.
+    const bool is_detached = !local_frame_->IsAttached();
+    base::UmaHistogramBoolean(
+        "Conversions.NonAttributionSrcRequestUnsetSupport.Detached",
+        is_detached);
+
+    if (is_detached) {
+      // Attribution support is unknown from detached frames, therefore not
+      // registering the response.
+      return false;
+    }
+
+    support = GetSupport();
+  }
+
   auto registration_info = GetRegistrationInfo(
       response.HttpHeaderFields(), local_frame_->DomWindow(), request_id,
       cross_app_web_enabled);
@@ -772,9 +794,6 @@
     return false;
   }
 
-  network::mojom::AttributionSupport support =
-      request.GetAttributionReportingSupport();
-
   if (Document* document = local_frame_->DomWindow()->document();
       document->IsPrerendering()) {
     document->AddPostPrerenderingActivationStep(WTF::BindOnce(
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc b/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc
index 41f61db..4322d75 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc
@@ -1120,5 +1120,28 @@
   }
 }
 
+// Regression test for https://crbug.com/363947060.
+TEST_F(AttributionSrcLoaderTest,
+       UnsetAttributionSupportForNonAttributionSrcRequest_NoCrash) {
+  KURL url = ToKURL(kUrl);
+  ResourceRequest request(url);
+
+  ResourceResponse response(url);
+  response.SetHttpStatusCode(200);
+  response.SetHttpHeaderField(http_names::kAttributionReportingRegisterTrigger,
+                              AtomicString(R"({})"));
+
+  MockAttributionHost host(
+      GetFrame().GetRemoteNavigationAssociatedInterfaces());
+  attribution_src_loader_->MaybeRegisterAttributionHeaders(request, response);
+  host.WaitUntilBoundAndFlush();
+
+  auto* mock_data_host = host.mock_data_host();
+  ASSERT_TRUE(mock_data_host);
+
+  mock_data_host->Flush();
+  EXPECT_EQ(mock_data_host->trigger_data().size(), 1u);
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/build.gni b/third_party/blink/renderer/core/frame/build.gni
index efc5399..935c3a6 100644
--- a/third_party/blink/renderer/core/frame/build.gni
+++ b/third_party/blink/renderer/core/frame/build.gni
@@ -82,8 +82,6 @@
   "frame_owner.h",
   "frame_serializer.cc",
   "frame_serializer.h",
-  "frame_serializer_delegate_impl.cc",
-  "frame_serializer_delegate_impl.h",
   "frame_types.h",
   "frame_view.cc",
   "frame_view.h",
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc
index 5b2357e..842268a7 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -35,6 +35,7 @@
 
 #include "third_party/blink/renderer/core/frame/frame_serializer.h"
 
+#include "third_party/blink/public/web/web_frame_serializer.h"
 #include "third_party/blink/renderer/core/css/css_font_face_rule.h"
 #include "third_party/blink/renderer/core/css/css_font_face_src_value.h"
 #include "third_party/blink/renderer/core/css/css_image_value.h"
@@ -47,14 +48,22 @@
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/style_rule.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
+#include "third_party/blink/renderer/core/dom/attribute.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/element_traversal.h"
+#include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/editing/serializers/markup_accumulator.h"
+#include "third_party/blink/renderer/core/frame/frame.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/html/forms/html_input_element.h"
+#include "third_party/blink/renderer/core/html/html_anchor_element.h"
 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
+#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/html_head_element.h"
+#include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
 #include "third_party/blink/renderer/core/html/html_image_loader.h"
 #include "third_party/blink/renderer/core/html/html_link_element.h"
@@ -64,18 +73,28 @@
 #include "third_party/blink/renderer/core/html/html_plugin_element.h"
 #include "third_party/blink/renderer/core/html/html_script_element.h"
 #include "third_party/blink/renderer/core/html/html_style_element.h"
+#include "third_party/blink/renderer/core/html/html_template_element.h"
 #include "third_party/blink/renderer/core/html/image_document.h"
+#include "third_party/blink/renderer/core/html/link_rel_attribute.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
+#include "third_party/blink/renderer/core/layout/layout_box.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/loader/resource/font_resource.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
+#include "third_party/blink/renderer/core/page/chrome_client.h"
+#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/style/style_fetched_image.h"
 #include "third_party/blink/renderer/core/style/style_image.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
 #include "third_party/blink/renderer/platform/mhtml/serialized_resource.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -83,13 +102,278 @@
 
 namespace blink {
 
+namespace {
+
+const int kPopupOverlayZIndexThreshold = 50;
+// Note that this is *not* the open web's declarative shadow DOM attribute,
+// which is <template shadowrootmode>. This is a special attribute used by
+// MHTML archive files to represent shadow roots.
+const char kShadowModeAttributeName[] = "shadowmode";
+const char kShadowDelegatesFocusAttributeName[] = "shadowdelegatesfocus";
+
 using mojom::blink::FormControlType;
 
+}  // namespace
+
+FrameSerializerDelegateImpl::FrameSerializerDelegateImpl(
+    WebFrameSerializer::MHTMLPartsGenerationDelegate& web_delegate,
+    HeapHashSet<WeakMember<const Element>>& shadow_template_elements)
+    : web_delegate_(web_delegate),
+      shadow_template_elements_(shadow_template_elements),
+      popup_overlays_skipped_(false) {}
+
+bool FrameSerializerDelegateImpl::ShouldIgnoreElement(const Element& element) {
+  if (ShouldIgnoreHiddenElement(element)) {
+    return true;
+  }
+  if (ShouldIgnoreMetaElement(element)) {
+    return true;
+  }
+  if (web_delegate_.RemovePopupOverlay() &&
+      ShouldIgnorePopupOverlayElement(element)) {
+    return true;
+  }
+  // Remove <link> for stylesheets that do not load.
+  auto* html_link_element = DynamicTo<HTMLLinkElement>(element);
+  if (html_link_element && html_link_element->RelAttribute().IsStyleSheet() &&
+      !html_link_element->sheet()) {
+    return true;
+  }
+  return false;
+}
+
+bool FrameSerializerDelegateImpl::ShouldIgnoreHiddenElement(
+    const Element& element) {
+  // If an iframe is in the head, it will be moved to the body when the page is
+  // being loaded. But if an iframe is injected into the head later, it will
+  // stay there and not been displayed. To prevent it from being brought to the
+  // saved page and cause it being displayed, we should not include it.
+  if (IsA<HTMLIFrameElement>(element) &&
+      Traversal<HTMLHeadElement>::FirstAncestor(element)) {
+    return true;
+  }
+
+  // Do not include the element that is marked with hidden attribute.
+  if (element.FastHasAttribute(html_names::kHiddenAttr)) {
+    return true;
+  }
+
+  // Do not include the hidden form element.
+  auto* html_element_element = DynamicTo<HTMLInputElement>(&element);
+  return html_element_element && html_element_element->FormControlType() ==
+                                     FormControlType::kInputHidden;
+}
+
+bool FrameSerializerDelegateImpl::ShouldIgnoreMetaElement(
+    const Element& element) {
+  // Do not include meta elements that declare Content-Security-Policy
+  // directives. They should have already been enforced when the original
+  // document is loaded. Since only the rendered resources are encapsulated in
+  // the saved MHTML page, there is no need to carry the directives. If they
+  // are still kept in the MHTML, child frames that are referred to using cid:
+  // scheme could be prevented from loading.
+  if (!IsA<HTMLMetaElement>(element)) {
+    return false;
+  }
+  if (!element.FastHasAttribute(html_names::kContentAttr)) {
+    return false;
+  }
+  const AtomicString& http_equiv =
+      element.FastGetAttribute(html_names::kHttpEquivAttr);
+  return http_equiv == "Content-Security-Policy";
+}
+
+bool FrameSerializerDelegateImpl::ShouldIgnorePopupOverlayElement(
+    const Element& element) {
+  // The element should be visible.
+  LayoutBox* box = element.GetLayoutBox();
+  if (!box) {
+    return false;
+  }
+
+  // The bounding box of the element should contain center point of the
+  // viewport.
+  LocalDOMWindow* window = element.GetDocument().domWindow();
+  DCHECK(window);
+  int center_x = window->innerWidth() / 2;
+  int center_y = window->innerHeight() / 2;
+  if (Page* page = element.GetDocument().GetPage()) {
+    center_x = page->GetChromeClient().WindowToViewportScalar(
+        window->GetFrame(), center_x);
+    center_y = page->GetChromeClient().WindowToViewportScalar(
+        window->GetFrame(), center_y);
+  }
+  if (!PhysicalRect(box->PhysicalLocation(), box->Size())
+           .Contains(LayoutUnit(center_x), LayoutUnit(center_y))) {
+    return false;
+  }
+
+  // The z-index should be greater than the threshold.
+  if (box->Style()->EffectiveZIndex() < kPopupOverlayZIndexThreshold) {
+    return false;
+  }
+
+  popup_overlays_skipped_ = true;
+
+  return true;
+}
+
+bool FrameSerializerDelegateImpl::ShouldIgnoreAttribute(
+    const Element& element,
+    const Attribute& attribute) {
+  // TODO(fgorski): Presence of srcset attribute causes MHTML to not display
+  // images, as only the value of src is pulled into the archive. Discarding
+  // srcset prevents the problem. Long term we should make sure to MHTML plays
+  // nicely with srcset.
+  if (IsA<HTMLImageElement>(element) &&
+      (attribute.LocalName() == html_names::kSrcsetAttr ||
+       attribute.LocalName() == html_names::kSizesAttr)) {
+    return true;
+  }
+
+  // Do not save ping attribute since anyway the ping will be blocked from
+  // MHTML.
+  if (IsA<HTMLAnchorElement>(element) &&
+      attribute.LocalName() == html_names::kPingAttr) {
+    return true;
+  }
+
+  // The special attribute in a template element to denote the shadow DOM
+  // should only be generated from MHTML serialization. If it is found in the
+  // original page, it should be ignored.
+  if (IsA<HTMLTemplateElement>(element) &&
+      (attribute.LocalName() == kShadowModeAttributeName ||
+       attribute.LocalName() == kShadowDelegatesFocusAttributeName) &&
+      !shadow_template_elements_.Contains(&element)) {
+    return true;
+  }
+
+  // If srcdoc attribute for frame elements will be rewritten as src attribute
+  // containing link instead of html contents, don't ignore the attribute.
+  // Bail out now to avoid the check in Element::isScriptingAttribute.
+  bool is_src_doc_attribute = IsA<HTMLFrameElementBase>(element) &&
+                              attribute.GetName() == html_names::kSrcdocAttr;
+  String new_link_for_the_element;
+  if (is_src_doc_attribute && RewriteLink(element, new_link_for_the_element)) {
+    return false;
+  }
+
+  //  Drop integrity attribute for those links with subresource loaded.
+  auto* html_link_element = DynamicTo<HTMLLinkElement>(element);
+  if (attribute.LocalName() == html_names::kIntegrityAttr &&
+      html_link_element && html_link_element->sheet()) {
+    return true;
+  }
+
+  // Do not include attributes that contain javascript. This is because the
+  // script will not be executed when a MHTML page is being loaded.
+  return element.IsScriptingAttribute(attribute);
+}
+
+bool FrameSerializerDelegateImpl::RewriteLink(const Element& element,
+                                              String& rewritten_link) {
+  auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(element);
+  if (!frame_owner) {
+    return false;
+  }
+
+  Frame* frame = frame_owner->ContentFrame();
+  if (!frame) {
+    return false;
+  }
+
+  WebString content_id = FrameSerializer::GetContentID(frame);
+  KURL cid_uri = MHTMLParser::ConvertContentIDToURI(content_id);
+  DCHECK(cid_uri.IsValid());
+  rewritten_link = cid_uri.GetString();
+  return true;
+}
+
+bool FrameSerializerDelegateImpl::ShouldSkipResourceWithURL(const KURL& url) {
+  return web_delegate_.ShouldSkipResource(url);
+}
+
+Vector<Attribute> FrameSerializerDelegateImpl::GetCustomAttributes(
+    const Element& element) {
+  Vector<Attribute> attributes;
+
+  if (auto* image = DynamicTo<HTMLImageElement>(element)) {
+    GetCustomAttributesForImageElement(*image, &attributes);
+  }
+
+  return attributes;
+}
+
+void FrameSerializerDelegateImpl::GetCustomAttributesForImageElement(
+    const HTMLImageElement& element,
+    Vector<Attribute>* attributes) {
+  // Currently only the value of src is pulled into the archive and the srcset
+  // attribute is ignored (see shouldIgnoreAttribute() above). If the device
+  // has a higher DPR, a different image from srcset could be loaded instead.
+  // When this occurs, we should provide the rendering width and height for
+  // <img> element if not set.
+
+  // The image should be loaded and participate the layout.
+  ImageResourceContent* image = element.CachedImage();
+  if (!image || !image->HasImage() || image->ErrorOccurred() ||
+      !element.GetLayoutObject()) {
+    return;
+  }
+
+  // The width and height attributes should not be set.
+  if (element.FastHasAttribute(html_names::kWidthAttr) ||
+      element.FastHasAttribute(html_names::kHeightAttr)) {
+    return;
+  }
+
+  // Check if different image is loaded. naturalWidth/naturalHeight will return
+  // the image size adjusted with current DPR.
+  if ((static_cast<int>(element.naturalWidth())) ==
+          image->GetImage()->width() &&
+      (static_cast<int>(element.naturalHeight())) ==
+          image->GetImage()->height()) {
+    return;
+  }
+
+  Attribute width_attribute(html_names::kWidthAttr,
+                            AtomicString::Number(element.LayoutBoxWidth()));
+  attributes->push_back(width_attribute);
+  Attribute height_attribute(html_names::kHeightAttr,
+                             AtomicString::Number(element.LayoutBoxHeight()));
+  attributes->push_back(height_attribute);
+}
+
+std::pair<ShadowRoot*, HTMLTemplateElement*>
+FrameSerializerDelegateImpl::GetShadowTree(const Element& element) const {
+  ShadowRoot* shadow_root = element.GetShadowRoot();
+  if (!shadow_root || shadow_root->GetMode() == ShadowRootMode::kUserAgent) {
+    return std::pair<ShadowRoot*, HTMLTemplateElement*>();
+  }
+
+  // Put the shadow DOM content inside a template element. A special attribute
+  // is set to tell the mode of the shadow DOM.
+  HTMLTemplateElement* template_element =
+      MakeGarbageCollected<HTMLTemplateElement>(element.GetDocument());
+  template_element->setAttribute(
+      QualifiedName(AtomicString(kShadowModeAttributeName)),
+      AtomicString(shadow_root->GetMode() == ShadowRootMode::kOpen ? "open"
+                                                                   : "closed"));
+  if (shadow_root->delegatesFocus()) {
+    template_element->setAttribute(
+        QualifiedName(AtomicString(kShadowDelegatesFocusAttributeName)),
+        g_empty_atom);
+  }
+  shadow_template_elements_.insert(template_element);
+
+  return std::pair<ShadowRoot*, HTMLTemplateElement*>(shadow_root,
+                                                      template_element);
+}
+
 class SerializerMarkupAccumulator : public MarkupAccumulator {
   STACK_ALLOCATED();
 
  public:
-  SerializerMarkupAccumulator(FrameSerializer::Delegate&,
+  SerializerMarkupAccumulator(FrameSerializerDelegateImpl&,
                               FrameSerializerResourceDelegate&,
                               Document&);
   ~SerializerMarkupAccumulator() override;
@@ -111,7 +395,7 @@
   void AppendExtraForHeadElement(const Element&);
   void AppendStylesheets(Document* document, bool style_element_only);
 
-  FrameSerializer::Delegate& delegate_;
+  FrameSerializerDelegateImpl& delegate_;
   FrameSerializerResourceDelegate& resource_delegate_;
   Document* document_;
 
@@ -120,7 +404,7 @@
 };
 
 SerializerMarkupAccumulator::SerializerMarkupAccumulator(
-    FrameSerializer::Delegate& delegate,
+    FrameSerializerDelegateImpl& delegate,
     FrameSerializerResourceDelegate& resource_delegate,
     Document& document)
     : MarkupAccumulator(kResolveAllURLs,
@@ -291,9 +575,12 @@
 // "Webpage, Complete" method of saving a page. It will take some work but it
 // needs to be done if we want to continue to support non-MHTML saved pages.
 
-FrameSerializer::FrameSerializer(Deque<SerializedResource>& resources,
-                                 Delegate& delegate)
-    : resources_(&resources), delegate_(delegate) {}
+FrameSerializer::FrameSerializer(
+    Deque<SerializedResource>& resources,
+    WebFrameSerializer::MHTMLPartsGenerationDelegate* web_delegate)
+    : resources_(&resources),
+      web_delegate_(web_delegate),
+      delegate_(*web_delegate, shadow_template_elements_) {}
 
 void FrameSerializer::SerializeFrame(const LocalFrame& frame) {
   TRACE_EVENT0("page-serialization", "FrameSerializer::serializeFrame");
@@ -595,4 +882,11 @@
                         escaped_url.c_str());
 }
 
+// static
+String FrameSerializer::GetContentID(Frame* frame) {
+  DCHECK(frame);
+  String frame_id = String(frame->GetFrameIdForTracing().data());
+  return "<frame-" + frame_id + "@mhtml.blink>";
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.h b/third_party/blink/renderer/core/frame/frame_serializer.h
index 9e4ab6a..253b5f0 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.h
+++ b/third_party/blink/renderer/core/frame/frame_serializer.h
@@ -31,6 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_SERIALIZER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_SERIALIZER_H_
 
+#include "third_party/blink/public/web/web_frame_serializer.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/attribute.h"
 #include "third_party/blink/renderer/core/html/html_template_element.h"
@@ -54,6 +55,7 @@
 class HTMLTemplateElement;
 class ImageResourceContent;
 class LocalFrame;
+class Frame;
 class ShadowRoot;
 
 struct SerializedResource;
@@ -71,6 +73,58 @@
   virtual void SerializeCSSStyleSheet(CSSStyleSheet&, const KURL&) = 0;
 };
 
+// An implementation of FrameSerializer's delegate which is used to serialize a
+// frame to a MHTML file.
+class FrameSerializerDelegateImpl final {
+  STACK_ALLOCATED();
+
+ public:
+  FrameSerializerDelegateImpl(WebFrameSerializer::MHTMLPartsGenerationDelegate&,
+                              HeapHashSet<WeakMember<const Element>>&);
+  FrameSerializerDelegateImpl(const FrameSerializerDelegateImpl&) = delete;
+  FrameSerializerDelegateImpl& operator=(const FrameSerializerDelegateImpl&) =
+      delete;
+  ~FrameSerializerDelegateImpl() = default;
+
+  // Controls whether HTML serialization should skip the given element.
+  bool ShouldIgnoreElement(const Element&);
+  // Controls whether HTML serialization should skip the given attribute.
+  bool ShouldIgnoreAttribute(const Element&, const Attribute&);
+  // Method allowing the Delegate control which URLs are written into the
+  // generated html document.
+  //
+  // When URL of the element needs to be rewritten, this method should
+  // return true and populate |rewrittenLink| with a desired value of the
+  // html attribute value to be used in place of the original link.
+  // (i.e. in place of img.src or iframe.src or object.data).
+  //
+  // If no link rewriting is desired, this method should return false.
+  bool RewriteLink(const Element&, String& rewritten_link);
+
+  // Tells whether to skip serialization of a subresource or CSSStyleSheet
+  // with a given URI. Used to deduplicate resources across multiple frames.
+  bool ShouldSkipResourceWithURL(const KURL&);
+
+  // Returns custom attributes that need to add in order to serialize the
+  // element.
+  Vector<Attribute> GetCustomAttributes(const Element&);
+
+  // Serializes *all* open *and* closed (non-UA) shadow roots it finds.
+  std::pair<ShadowRoot*, HTMLTemplateElement*> GetShadowTree(
+      const Element&) const;
+
+ private:
+  bool ShouldIgnoreHiddenElement(const Element&);
+  bool ShouldIgnoreMetaElement(const Element&);
+  bool ShouldIgnorePopupOverlayElement(const Element&);
+  void GetCustomAttributesForImageElement(const HTMLImageElement&,
+                                          Vector<Attribute>*);
+
+  WebFrameSerializer::MHTMLPartsGenerationDelegate& web_delegate_;
+  HeapHashSet<WeakMember<const Element>>& shadow_template_elements_;
+  bool popup_overlays_skipped_;
+};
+
 // This class is used to serialize frame's contents back to text (typically
 // HTML).  It serializes frame's document and resources such as images and CSS
 // stylesheets.
@@ -78,53 +132,13 @@
   STACK_ALLOCATED();
 
  public:
-  class Delegate {
-   public:
-    virtual ~Delegate() = default;
-
-    // Controls whether HTML serialization should skip the given element.
-    virtual bool ShouldIgnoreElement(const Element&) { return false; }
-
-    // Controls whether HTML serialization should skip the given attribute.
-    virtual bool ShouldIgnoreAttribute(const Element&, const Attribute&) {
-      return false;
-    }
-
-    // Method allowing the Delegate control which URLs are written into the
-    // generated html document.
-    //
-    // When URL of the element needs to be rewritten, this method should
-    // return true and populate |rewrittenLink| with a desired value of the
-    // html attribute value to be used in place of the original link.
-    // (i.e. in place of img.src or iframe.src or object.data).
-    //
-    // If no link rewriting is desired, this method should return false.
-    virtual bool RewriteLink(const Element&, String& rewritten_link) {
-      return false;
-    }
-
-    // Tells whether to skip serialization of a subresource or CSSStyleSheet
-    // with a given URI. Used to deduplicate resources across multiple frames.
-    virtual bool ShouldSkipResourceWithURL(const KURL&) { return false; }
-
-    // Returns custom attributes that need to add in order to serialize the
-    // element.
-    virtual Vector<Attribute> GetCustomAttributes(const Element&) {
-      return Vector<Attribute>();
-    }
-
-    // Returns a shadow tree that needs to be serialized.
-    virtual std::pair<ShadowRoot*, HTMLTemplateElement*> GetShadowTree(
-        const Element&) const {
-      return std::pair<ShadowRoot*, HTMLTemplateElement*>();
-    }
-  };
-
   // Constructs a serializer that will write output to the given deque of
   // SerializedResources and uses the Delegate for controlling some
   // serialization aspects.  Callers need to ensure that both arguments stay
   // alive until the FrameSerializer gets destroyed.
-  FrameSerializer(Deque<SerializedResource>&, Delegate&);
+  FrameSerializer(
+      Deque<SerializedResource>&,
+      WebFrameSerializer::MHTMLPartsGenerationDelegate* web_delegate);
 
   // Initiates the serialization of the frame. All serialized content and
   // retrieved resources are added to the Deque passed to the constructor.
@@ -134,6 +148,12 @@
 
   static String MarkOfTheWebDeclaration(const KURL&);
 
+  // Returns a Content-ID to be used for the given frame.
+  // See rfc2557 - section 8.3 - "Use of the Content-ID header and CID URLs".
+  // Format note - the returned string should be of the form "<foo@bar.com>"
+  // (i.e. the strings should include the angle brackets).
+  static String GetContentID(Frame* frame);
+
  private:
   void AddResourceForElement(Document&, const Element&) override;
   void SerializeCSSStyleSheet(CSSStyleSheet&, const KURL&) override;
@@ -157,7 +177,9 @@
   // This hashset is only used for de-duplicating resources to be serialized.
   HashSet<KURL> resource_urls_;
 
-  Delegate& delegate_;
+  HeapHashSet<WeakMember<const Element>> shadow_template_elements_;
+  WebFrameSerializer::MHTMLPartsGenerationDelegate* web_delegate_;
+  FrameSerializerDelegateImpl delegate_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/frame_serializer_delegate_impl.cc b/third_party/blink/renderer/core/frame/frame_serializer_delegate_impl.cc
deleted file mode 100644
index 0c69e77..0000000
--- a/third_party/blink/renderer/core/frame/frame_serializer_delegate_impl.cc
+++ /dev/null
@@ -1,304 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/frame/frame_serializer_delegate_impl.h"
-
-#include "third_party/blink/public/web/web_frame_serializer.h"
-#include "third_party/blink/renderer/core/dom/attribute.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/element_traversal.h"
-#include "third_party/blink/renderer/core/dom/shadow_root.h"
-#include "third_party/blink/renderer/core/frame/frame.h"
-#include "third_party/blink/renderer/core/frame/local_dom_window.h"
-#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
-#include "third_party/blink/renderer/core/html/html_anchor_element.h"
-#include "third_party/blink/renderer/core/html/html_frame_element_base.h"
-#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
-#include "third_party/blink/renderer/core/html/html_head_element.h"
-#include "third_party/blink/renderer/core/html/html_iframe_element.h"
-#include "third_party/blink/renderer/core/html/html_image_element.h"
-#include "third_party/blink/renderer/core/html/html_link_element.h"
-#include "third_party/blink/renderer/core/html/html_meta_element.h"
-#include "third_party/blink/renderer/core/html/html_template_element.h"
-#include "third_party/blink/renderer/core/html/link_rel_attribute.h"
-#include "third_party/blink/renderer/core/html_names.h"
-#include "third_party/blink/renderer/core/input_type_names.h"
-#include "third_party/blink/renderer/core/layout/layout_box.h"
-#include "third_party/blink/renderer/core/layout/layout_object.h"
-#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
-#include "third_party/blink/renderer/core/page/chrome_client.h"
-#include "third_party/blink/renderer/core/page/page.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
-
-namespace blink {
-
-namespace {
-
-const int kPopupOverlayZIndexThreshold = 50;
-// Note that this is *not* the open web's declarative shadow DOM attribute,
-// which is <template shadowrootmode>. This is a special attribute used by
-// MHTML archive files to represent shadow roots.
-const char kShadowModeAttributeName[] = "shadowmode";
-const char kShadowDelegatesFocusAttributeName[] = "shadowdelegatesfocus";
-
-}  // namespace
-
-using mojom::blink::FormControlType;
-
-// static
-String FrameSerializerDelegateImpl::GetContentID(Frame* frame) {
-  DCHECK(frame);
-  String frame_id = String(frame->GetFrameIdForTracing().data());
-  return "<frame-" + frame_id + "@mhtml.blink>";
-}
-
-FrameSerializerDelegateImpl::FrameSerializerDelegateImpl(
-    WebFrameSerializer::MHTMLPartsGenerationDelegate& web_delegate,
-    HeapHashSet<WeakMember<const Element>>& shadow_template_elements)
-    : web_delegate_(web_delegate),
-      shadow_template_elements_(shadow_template_elements),
-      popup_overlays_skipped_(false) {}
-
-bool FrameSerializerDelegateImpl::ShouldIgnoreElement(const Element& element) {
-  if (ShouldIgnoreHiddenElement(element))
-    return true;
-  if (ShouldIgnoreMetaElement(element))
-    return true;
-  if (web_delegate_.RemovePopupOverlay() &&
-      ShouldIgnorePopupOverlayElement(element)) {
-    return true;
-  }
-  // Remove <link> for stylesheets that do not load.
-  auto* html_link_element = DynamicTo<HTMLLinkElement>(element);
-  if (html_link_element && html_link_element->RelAttribute().IsStyleSheet() &&
-      !html_link_element->sheet()) {
-    return true;
-  }
-  return false;
-}
-
-bool FrameSerializerDelegateImpl::ShouldIgnoreHiddenElement(
-    const Element& element) {
-  // If an iframe is in the head, it will be moved to the body when the page is
-  // being loaded. But if an iframe is injected into the head later, it will
-  // stay there and not been displayed. To prevent it from being brought to the
-  // saved page and cause it being displayed, we should not include it.
-  if (IsA<HTMLIFrameElement>(element) &&
-      Traversal<HTMLHeadElement>::FirstAncestor(element)) {
-    return true;
-  }
-
-  // Do not include the element that is marked with hidden attribute.
-  if (element.FastHasAttribute(html_names::kHiddenAttr))
-    return true;
-
-  // Do not include the hidden form element.
-  auto* html_element_element = DynamicTo<HTMLInputElement>(&element);
-  return html_element_element && html_element_element->FormControlType() ==
-                                     FormControlType::kInputHidden;
-}
-
-bool FrameSerializerDelegateImpl::ShouldIgnoreMetaElement(
-    const Element& element) {
-  // Do not include meta elements that declare Content-Security-Policy
-  // directives. They should have already been enforced when the original
-  // document is loaded. Since only the rendered resources are encapsulated in
-  // the saved MHTML page, there is no need to carry the directives. If they
-  // are still kept in the MHTML, child frames that are referred to using cid:
-  // scheme could be prevented from loading.
-  if (!IsA<HTMLMetaElement>(element))
-    return false;
-  if (!element.FastHasAttribute(html_names::kContentAttr))
-    return false;
-  const AtomicString& http_equiv =
-      element.FastGetAttribute(html_names::kHttpEquivAttr);
-  return http_equiv == "Content-Security-Policy";
-}
-
-bool FrameSerializerDelegateImpl::ShouldIgnorePopupOverlayElement(
-    const Element& element) {
-  // The element should be visible.
-  LayoutBox* box = element.GetLayoutBox();
-  if (!box)
-    return false;
-
-  // The bounding box of the element should contain center point of the
-  // viewport.
-  LocalDOMWindow* window = element.GetDocument().domWindow();
-  DCHECK(window);
-  int center_x = window->innerWidth() / 2;
-  int center_y = window->innerHeight() / 2;
-  if (Page* page = element.GetDocument().GetPage()) {
-    center_x = page->GetChromeClient().WindowToViewportScalar(
-        window->GetFrame(), center_x);
-    center_y = page->GetChromeClient().WindowToViewportScalar(
-        window->GetFrame(), center_y);
-  }
-  if (!PhysicalRect(box->PhysicalLocation(), box->Size())
-           .Contains(LayoutUnit(center_x), LayoutUnit(center_y))) {
-    return false;
-  }
-
-  // The z-index should be greater than the threshold.
-  if (box->Style()->EffectiveZIndex() < kPopupOverlayZIndexThreshold)
-    return false;
-
-  popup_overlays_skipped_ = true;
-
-  return true;
-}
-
-bool FrameSerializerDelegateImpl::ShouldIgnoreAttribute(
-    const Element& element,
-    const Attribute& attribute) {
-  // TODO(fgorski): Presence of srcset attribute causes MHTML to not display
-  // images, as only the value of src is pulled into the archive. Discarding
-  // srcset prevents the problem. Long term we should make sure to MHTML plays
-  // nicely with srcset.
-  if (IsA<HTMLImageElement>(element) &&
-      (attribute.LocalName() == html_names::kSrcsetAttr ||
-       attribute.LocalName() == html_names::kSizesAttr)) {
-    return true;
-  }
-
-  // Do not save ping attribute since anyway the ping will be blocked from
-  // MHTML.
-  if (IsA<HTMLAnchorElement>(element) &&
-      attribute.LocalName() == html_names::kPingAttr) {
-    return true;
-  }
-
-  // The special attribute in a template element to denote the shadow DOM
-  // should only be generated from MHTML serialization. If it is found in the
-  // original page, it should be ignored.
-  if (IsA<HTMLTemplateElement>(element) &&
-      (attribute.LocalName() == kShadowModeAttributeName ||
-       attribute.LocalName() == kShadowDelegatesFocusAttributeName) &&
-      !shadow_template_elements_.Contains(&element)) {
-    return true;
-  }
-
-  // If srcdoc attribute for frame elements will be rewritten as src attribute
-  // containing link instead of html contents, don't ignore the attribute.
-  // Bail out now to avoid the check in Element::isScriptingAttribute.
-  bool is_src_doc_attribute = IsA<HTMLFrameElementBase>(element) &&
-                              attribute.GetName() == html_names::kSrcdocAttr;
-  String new_link_for_the_element;
-  if (is_src_doc_attribute && RewriteLink(element, new_link_for_the_element))
-    return false;
-
-  //  Drop integrity attribute for those links with subresource loaded.
-  auto* html_link_element = DynamicTo<HTMLLinkElement>(element);
-  if (attribute.LocalName() == html_names::kIntegrityAttr &&
-      html_link_element && html_link_element->sheet()) {
-    return true;
-  }
-
-  // Do not include attributes that contain javascript. This is because the
-  // script will not be executed when a MHTML page is being loaded.
-  return element.IsScriptingAttribute(attribute);
-}
-
-bool FrameSerializerDelegateImpl::RewriteLink(const Element& element,
-                                              String& rewritten_link) {
-  auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(element);
-  if (!frame_owner)
-    return false;
-
-  Frame* frame = frame_owner->ContentFrame();
-  if (!frame)
-    return false;
-
-  WebString content_id = GetContentID(frame);
-  KURL cid_uri = MHTMLParser::ConvertContentIDToURI(content_id);
-  DCHECK(cid_uri.IsValid());
-  rewritten_link = cid_uri.GetString();
-  return true;
-}
-
-bool FrameSerializerDelegateImpl::ShouldSkipResourceWithURL(const KURL& url) {
-  return web_delegate_.ShouldSkipResource(url);
-}
-
-Vector<Attribute> FrameSerializerDelegateImpl::GetCustomAttributes(
-    const Element& element) {
-  Vector<Attribute> attributes;
-
-  if (auto* image = DynamicTo<HTMLImageElement>(element)) {
-    GetCustomAttributesForImageElement(*image, &attributes);
-  }
-
-  return attributes;
-}
-
-void FrameSerializerDelegateImpl::GetCustomAttributesForImageElement(
-    const HTMLImageElement& element,
-    Vector<Attribute>* attributes) {
-  // Currently only the value of src is pulled into the archive and the srcset
-  // attribute is ignored (see shouldIgnoreAttribute() above). If the device
-  // has a higher DPR, a different image from srcset could be loaded instead.
-  // When this occurs, we should provide the rendering width and height for
-  // <img> element if not set.
-
-  // The image should be loaded and participate the layout.
-  ImageResourceContent* image = element.CachedImage();
-  if (!image || !image->HasImage() || image->ErrorOccurred() ||
-      !element.GetLayoutObject()) {
-    return;
-  }
-
-  // The width and height attributes should not be set.
-  if (element.FastHasAttribute(html_names::kWidthAttr) ||
-      element.FastHasAttribute(html_names::kHeightAttr)) {
-    return;
-  }
-
-  // Check if different image is loaded. naturalWidth/naturalHeight will return
-  // the image size adjusted with current DPR.
-  if ((static_cast<int>(element.naturalWidth())) ==
-          image->GetImage()->width() &&
-      (static_cast<int>(element.naturalHeight())) ==
-          image->GetImage()->height()) {
-    return;
-  }
-
-  Attribute width_attribute(html_names::kWidthAttr,
-                            AtomicString::Number(element.LayoutBoxWidth()));
-  attributes->push_back(width_attribute);
-  Attribute height_attribute(html_names::kHeightAttr,
-                             AtomicString::Number(element.LayoutBoxHeight()));
-  attributes->push_back(height_attribute);
-}
-
-std::pair<ShadowRoot*, HTMLTemplateElement*>
-FrameSerializerDelegateImpl::GetShadowTree(const Element& element) const {
-  ShadowRoot* shadow_root = element.GetShadowRoot();
-  if (!shadow_root || shadow_root->GetMode() == ShadowRootMode::kUserAgent) {
-    return std::pair<ShadowRoot*, HTMLTemplateElement*>();
-  }
-
-  // Put the shadow DOM content inside a template element. A special attribute
-  // is set to tell the mode of the shadow DOM.
-  HTMLTemplateElement* template_element =
-      MakeGarbageCollected<HTMLTemplateElement>(element.GetDocument());
-  template_element->setAttribute(
-      QualifiedName(AtomicString(kShadowModeAttributeName)),
-      AtomicString(shadow_root->GetMode() == ShadowRootMode::kOpen ? "open"
-                                                                   : "closed"));
-  if (shadow_root->delegatesFocus()) {
-    template_element->setAttribute(
-        QualifiedName(AtomicString(kShadowDelegatesFocusAttributeName)),
-        g_empty_atom);
-  }
-  shadow_template_elements_.insert(template_element);
-
-  return std::pair<ShadowRoot*, HTMLTemplateElement*>(shadow_root,
-                                                      template_element);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/frame_serializer_delegate_impl.h b/third_party/blink/renderer/core/frame/frame_serializer_delegate_impl.h
deleted file mode 100644
index ecc418c..0000000
--- a/third_party/blink/renderer/core/frame/frame_serializer_delegate_impl.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_SERIALIZER_DELEGATE_IMPL_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_SERIALIZER_DELEGATE_IMPL_H_
-
-#include "third_party/blink/renderer/core/dom/node.h"
-#include "third_party/blink/renderer/core/frame/frame_serializer.h"
-
-#include "third_party/blink/public/web/web_frame_serializer.h"
-#include "third_party/blink/renderer/core/dom/element.h"
-#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
-
-namespace blink {
-
-class Frame;
-class KURL;
-class HTMLImageElement;
-
-// An implementation of FrameSerializer's delegate which is used to serialize a
-// frame to a MHTML file.
-class FrameSerializerDelegateImpl final : public FrameSerializer::Delegate {
-  STACK_ALLOCATED();
-
- public:
-  // Returns a Content-ID to be used for the given frame.
-  // See rfc2557 - section 8.3 - "Use of the Content-ID header and CID URLs".
-  // Format note - the returned string should be of the form "<foo@bar.com>"
-  // (i.e. the strings should include the angle brackets).
-  static String GetContentID(Frame* frame);
-
-  FrameSerializerDelegateImpl(WebFrameSerializer::MHTMLPartsGenerationDelegate&,
-                              HeapHashSet<WeakMember<const Element>>&);
-  FrameSerializerDelegateImpl(const FrameSerializerDelegateImpl&) = delete;
-  FrameSerializerDelegateImpl& operator=(const FrameSerializerDelegateImpl&) =
-      delete;
-  ~FrameSerializerDelegateImpl() override = default;
-
-  // FrameSerializer::Delegate implementation.
-  bool ShouldIgnoreElement(const Element&) override;
-  bool ShouldIgnoreAttribute(const Element&, const Attribute&) override;
-  bool RewriteLink(const Element&, String& rewritten_link) override;
-  bool ShouldSkipResourceWithURL(const KURL&) override;
-  Vector<Attribute> GetCustomAttributes(const Element&) override;
-
-  // This overloaded version of GetShadowTree is used by
-  // FrameSerializer::GetShadowTree(), as part of serialization to an MHTML
-  // file. It serializes *all* open *and* closed (non-UA) shadow roots it finds.
-  std::pair<ShadowRoot*, HTMLTemplateElement*> GetShadowTree(
-      const Element&) const override;
-
- private:
-  bool ShouldIgnoreHiddenElement(const Element&);
-  bool ShouldIgnoreMetaElement(const Element&);
-  bool ShouldIgnorePopupOverlayElement(const Element&);
-  void GetCustomAttributesForImageElement(const HTMLImageElement&,
-                                          Vector<Attribute>*);
-
-  WebFrameSerializer::MHTMLPartsGenerationDelegate& web_delegate_;
-  HeapHashSet<WeakMember<const Element>>& shadow_template_elements_;
-  bool popup_overlays_skipped_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_SERIALIZER_DELEGATE_IMPL_H_
diff --git a/third_party/blink/renderer/core/frame/frame_serializer_test.cc b/third_party/blink/renderer/core/frame/frame_serializer_test.cc
index 8f62b054..f524473 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer_test.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer_test.cc
@@ -59,8 +59,9 @@
 
 namespace blink {
 
-class FrameSerializerTest : public testing::Test,
-                            public FrameSerializer::Delegate {
+class FrameSerializerTest
+    : public testing::Test,
+      public WebFrameSerializer::MHTMLPartsGenerationDelegate {
  public:
   FrameSerializerTest()
       : folder_("frameserializer/"),
@@ -84,8 +85,6 @@
 
   void SetBaseFolder(const char* folder) { folder_ = folder; }
 
-  void SetRewriteURLFolder(const char* folder) { rewrite_folder_ = folder; }
-
   void RegisterURL(const KURL& url, const char* file, const char* mime_type) {
     url_test_helpers::RegisterMockedURLLoad(
         url, test::CoreTestDataPath(WebString::FromUTF8(folder_ + file)),
@@ -111,12 +110,8 @@
         KURL(base_url_, file), response, WebURLError(error));
   }
 
-  void RegisterRewriteURL(const char* from_url, const char* to_url) {
-    rewrite_urls_.insert(from_url, to_url);
-  }
-
   void RegisterSkipURL(const char* url) {
-    skip_urls_.push_back(KURL(base_url_, url));
+    skip_urls_.insert(KURL(base_url_, url));
   }
 
   void Serialize(const char* url) {
@@ -127,12 +122,17 @@
     // load.
     frame_test_helpers::PumpPendingRequestsForFrameToLoad(
         helper_.GetWebView()->MainFrameImpl());
-    FrameSerializer serializer(resources_, *this);
+    FrameSerializer serializer(resources_, this);
     Frame* frame = helper_.LocalMainFrame()->GetFrame();
     for (; frame; frame = frame->Tree().TraverseNext()) {
       // This is safe, because tests do not do cross-site navigation
       // (and therefore don't have remote frames).
       serializer.SerializeFrame(*To<LocalFrame>(frame));
+      // Don't serialize the same resource on subsequent frames. This mimics how
+      // FrameSerializer is actually used.
+      for (auto& res : GetResources()) {
+        skip_urls_.insert(res.url);
+      }
     }
   }
 
@@ -175,30 +175,13 @@
     settings->SetJavaScriptEnabled(true);
   }
 
-  // FrameSerializer::Delegate implementation.
-  bool RewriteLink(const Element& element, String& rewritten_link) override {
-    String complete_url;
-    for (const auto& attribute : element.Attributes()) {
-      if (element.HasLegalLinkAttribute(attribute.GetName())) {
-        complete_url = element.GetDocument().CompleteURL(attribute.Value());
-        break;
-      }
-    }
-
-    if (complete_url.IsNull() || !rewrite_urls_.Contains(complete_url))
-      return false;
-
-    StringBuilder uri_builder;
-    uri_builder.Append(rewrite_folder_);
-    uri_builder.Append('/');
-    uri_builder.Append(rewrite_urls_.at(complete_url));
-    rewritten_link = uri_builder.ToString();
-    return true;
+  // WebFrameSerializer::MHTMLPartsGenerationDelegate impl.
+  bool ShouldSkipResource(const WebURL& url) override {
+    return skip_urls_.Contains(url.GetString());
   }
+  bool UseBinaryEncoding() override { return false; }
 
-  bool ShouldSkipResourceWithURL(const KURL& url) override {
-    return skip_urls_.Contains(url);
-  }
+  bool RemovePopupOverlay() override { return false; }
 
   test::TaskEnvironment task_environment_;
   ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
@@ -206,9 +189,7 @@
   std::string folder_;
   KURL base_url_;
   Deque<SerializedResource> resources_;
-  HashMap<String, String> rewrite_urls_;
-  Vector<String> skip_urls_;
-  String rewrite_folder_;
+  HashSet<String> skip_urls_;
 };
 
 TEST_F(FrameSerializerTest, HTMLElements) {
@@ -277,6 +258,12 @@
   EXPECT_TRUE(IsSerialized("frame_2.png", "image/png"));
   EXPECT_TRUE(IsSerialized("frame_3.png", "image/png"));
   EXPECT_TRUE(IsSerialized("frame_4.png", "image/png"));
+
+  // Verify all 3 frame src are rewritten to Content ID URLs.
+  Vector<String> split_string;
+  GetSerializedData("simple_frames.html", "text/html")
+      .Split("<frame src=\"cid:", split_string);
+  EXPECT_EQ(split_string.size(), 4u);
 }
 
 TEST_F(FrameSerializerTest, IFrames) {
@@ -490,27 +477,6 @@
   EXPECT_TRUE(IsSerialized("page_with_morphing_data.html", "text/html"));
 }
 
-TEST_F(FrameSerializerTest, RewriteLinksSimple) {
-  SetBaseFolder("frameserializer/rewritelinks/");
-  SetRewriteURLFolder("folder");
-
-  RegisterURL("rewritelinks_simple.html", "text/html");
-  RegisterURL("absolute.png", "image.png", "image/png");
-  RegisterURL("relative.png", "image.png", "image/png");
-  RegisterRewriteURL("http://www.test.com/absolute.png", "a.png");
-  RegisterRewriteURL("http://www.test.com/relative.png", "b.png");
-
-  Serialize("rewritelinks_simple.html");
-
-  EXPECT_EQ(3U, GetResources().size());
-  EXPECT_NE(GetSerializedData("rewritelinks_simple.html", "text/html")
-                .Find("\"folder/a.png\""),
-            kNotFound);
-  EXPECT_NE(GetSerializedData("rewritelinks_simple.html", "text/html")
-                .Find("\"folder/b.png\""),
-            kNotFound);
-}
-
 // Test that we don't regress https://bugs.webkit.org/show_bug.cgi?id=99105
 TEST_F(FrameSerializerTest, SVGImageDontCrash) {
   SetBaseFolder("frameserializer/svg/");
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index f560c0a..3748fa2 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -134,7 +134,6 @@
 #include "third_party/blink/renderer/core/frame/frame_console.h"
 #include "third_party/blink/renderer/core/frame/frame_overlay.h"
 #include "third_party/blink/renderer/core/frame/frame_serializer.h"
-#include "third_party/blink/renderer/core/frame/frame_serializer_delegate_impl.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/local_frame_mojo_handler.h"
diff --git a/third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.cc b/third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.cc
deleted file mode 100644
index bd28616..0000000
--- a/third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.cc
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.h"
-
-#include <algorithm>
-
-#include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
-#include "third_party/blink/public/common/features.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/events/event_target.h"
-#include "third_party/blink/renderer/core/events/pointer_event.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/core/html/html_anchor_element.h"
-#include "third_party/blink/renderer/core/html/html_area_element.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/wtf/hash_set.h"
-
-namespace blink {
-
-// static
-const char AnchorElementObserverForServiceWorker::kSupplementName[] =
-    "AnchorElementObserverForServiceWorker";
-
-// static
-AnchorElementObserverForServiceWorker*
-AnchorElementObserverForServiceWorker::From(Document& document) {
-  TRACE_EVENT0("ServiceWorker", "AnchorElementObserverForServiceWorker::From");
-  const KURL& url = document.Url();
-  if (!document.IsInOutermostMainFrame() || !url.IsValid() ||
-      !url.ProtocolIsInHTTPFamily()) {
-    return nullptr;
-  }
-
-  AnchorElementObserverForServiceWorker* observer =
-      Supplement<Document>::From<AnchorElementObserverForServiceWorker>(
-          document);
-
-  if (!observer) {
-    observer = MakeGarbageCollected<AnchorElementObserverForServiceWorker>(
-        base::PassKey<AnchorElementObserverForServiceWorker>(), document);
-    ProvideTo(document, observer);
-  }
-
-  return observer;
-}
-
-AnchorElementObserverForServiceWorker::AnchorElementObserverForServiceWorker(
-    base::PassKey<AnchorElementObserverForServiceWorker>,
-    Document& document)
-    : Supplement<Document>(document),
-      batch_timer_(
-          document.GetTaskRunner(TaskType::kInternalDefault),
-          this,
-          &AnchorElementObserverForServiceWorker::SendPendingWarmUpRequests) {
-  CHECK(document.IsInOutermostMainFrame());
-}
-
-void AnchorElementObserverForServiceWorker::MaybeSendNavigationTargetLinks(
-    const Links& candidate_links) {
-  if (candidate_links.empty()) {
-    return;
-  }
-
-  TRACE_EVENT0("ServiceWorker",
-               "AnchorElementObserverForServiceWorker::"
-               "MaybeSendNavigationTargetLinks");
-
-  static const int kWarmUpRequestLimit =
-      features::kSpeculativeServiceWorkerWarmUpRequestLimit.Get();
-
-  for (const auto& link : candidate_links) {
-    // Prevents excessive duplicate warm-up requests.
-    if (already_handled_links_.Contains(link)) {
-      continue;
-    }
-
-    if (total_request_count_ < kWarmUpRequestLimit) {
-      ++total_request_count_;
-      already_handled_links_.insert(link);
-      pending_warm_up_links_.push_back(link);
-    }
-  }
-
-  MaybeSendPendingWarmUpRequests();
-}
-
-void AnchorElementObserverForServiceWorker::MaybeSendPendingWarmUpRequests() {
-  TRACE_EVENT0(
-      "ServiceWorker",
-      "AnchorElementObserverForServiceWorker::MaybeSendPendingWarmUpRequests");
-
-  static const bool kSpeculativeServiceWorkerWarmUpWaitForLoad =
-      features::kSpeculativeServiceWorkerWarmUpWaitForLoad.Get();
-  if (kSpeculativeServiceWorkerWarmUpWaitForLoad &&
-      !GetDocument().LoadEventFinished()) {
-    return;
-  }
-
-  if (!pending_warm_up_links_.empty() && !batch_timer_.IsActive()) {
-    static const base::TimeDelta
-        kSpeculativeServiceWorkerWarmUpFirstBatchTimer =
-            features::kSpeculativeServiceWorkerWarmUpFirstBatchTimer.Get();
-    static const base::TimeDelta kSpeculativeServiceWorkerWarmUpBatchTimer =
-        features::kSpeculativeServiceWorkerWarmUpBatchTimer.Get();
-    batch_timer_.StartOneShot(
-        is_first_batch_ ? kSpeculativeServiceWorkerWarmUpFirstBatchTimer
-                        : kSpeculativeServiceWorkerWarmUpBatchTimer,
-        FROM_HERE);
-    is_first_batch_ = false;
-  }
-}
-
-void AnchorElementObserverForServiceWorker::SendPendingWarmUpRequests(
-    TimerBase*) {
-  LocalFrame* local_frame = GetDocument().GetFrame();
-
-  if (!local_frame) {
-    return;
-  }
-
-  TRACE_EVENT1(
-      "ServiceWorker",
-      "AnchorElementObserverForServiceWorker::SendPendingWarmUpRequests",
-      "pending_link_count", pending_warm_up_links_.size());
-
-  static const uint32_t kMaxBatchSize =
-      features::kSpeculativeServiceWorkerWarmUpBatchSize.Get();
-
-  const KURL& document_url = GetDocument().Url();
-  HashSet<KURL> url_set;
-  Vector<KURL> urls;
-  urls.reserve(std::min(pending_warm_up_links_.size(), kMaxBatchSize));
-  while (!pending_warm_up_links_.empty()) {
-    KURL url = pending_warm_up_links_.back()->Url();
-    pending_warm_up_links_.pop_back();
-
-    if (!url.IsValid() || !url.ProtocolIsInHTTPFamily() ||
-        EqualIgnoringFragmentIdentifier(document_url, url)) {
-      continue;
-    }
-
-    url.RemoveFragmentIdentifier();
-    url.SetUser(String());
-    url.SetPass(String());
-    url.SetQuery(String());
-
-    if (url_set.Contains(url)) {
-      continue;
-    }
-    url_set.insert(url);
-    urls.push_back(std::move(url));
-    if (urls.size() >= kMaxBatchSize) {
-      break;
-    }
-  }
-  urls.Reverse();
-  local_frame->MaybeStartOutermostMainFrameNavigation(std::move(urls));
-
-  // Send remaining requests later.
-  MaybeSendPendingWarmUpRequests();
-}
-
-void AnchorElementObserverForServiceWorker::Trace(Visitor* visitor) const {
-  Supplement<Document>::Trace(visitor);
-  visitor->Trace(already_handled_links_);
-  visitor->Trace(pending_warm_up_links_);
-  visitor->Trace(batch_timer_);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.h b/third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.h
deleted file mode 100644
index bfa3a111..0000000
--- a/third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_ANCHOR_ELEMENT_OBSERVER_FOR_SERVICE_WORKER_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_ANCHOR_ELEMENT_OBSERVER_FOR_SERVICE_WORKER_H_
-
-#include "base/types/pass_key.h"
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/platform/heap/collection_support/heap_deque.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"
-#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/supplementable.h"
-#include "third_party/blink/renderer/platform/timer.h"
-
-namespace blink {
-
-class Document;
-class HTMLAnchorElement;
-
-class CORE_EXPORT AnchorElementObserverForServiceWorker
-    : public GarbageCollected<AnchorElementObserverForServiceWorker>,
-      public Supplement<Document> {
- public:
-  static const char kSupplementName[];
-  static AnchorElementObserverForServiceWorker* From(Document&);
-  explicit AnchorElementObserverForServiceWorker(
-      base::PassKey<AnchorElementObserverForServiceWorker>,
-      Document& document);
-  AnchorElementObserverForServiceWorker(
-      AnchorElementObserverForServiceWorker&&) = delete;
-  AnchorElementObserverForServiceWorker& operator=(
-      AnchorElementObserverForServiceWorker&&) = delete;
-  AnchorElementObserverForServiceWorker(
-      const AnchorElementObserverForServiceWorker&) = delete;
-  AnchorElementObserverForServiceWorker& operator=(
-      const AnchorElementObserverForServiceWorker&) = delete;
-  virtual ~AnchorElementObserverForServiceWorker() = default;
-  using Links = HeapVector<Member<HTMLAnchorElement>>;
-
-  void MaybeSendNavigationTargetLinks(const Links& candidate_links);
-  void MaybeSendPendingWarmUpRequests();
-  void Trace(Visitor* visitor) const override;
-
- private:
-  void SendPendingWarmUpRequests(TimerBase*);
-  Document& GetDocument() { return *GetSupplementable(); }
-
-  // Remember already handled links to prevent excessive duplicate
-  // warm-up requests.
-  HeapHashSet<WeakMember<HTMLAnchorElement>> already_handled_links_;
-
-  // The following `pending_warm_up_links_` keeps the pending warm-up requests
-  // until the document is loaded to prioritize loading the document.
-  Links pending_warm_up_links_;
-
-  // Sent URL count to browser process.
-  int total_request_count_ = 0;
-
-  // This timer is used to avoid frequent warm-up mojo calls. Before actually
-  // sending URL candidates, we wait for a while to accept more requests, and
-  // then send the accumulated URL candidates in one batch.
-  HeapTaskRunnerTimer<AnchorElementObserverForServiceWorker> batch_timer_;
-
-  // `true` indicates the timer hasn't used.
-  bool is_first_batch_ = true;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_ANCHOR_ELEMENT_OBSERVER_FOR_SERVICE_WORKER_H_
diff --git a/third_party/blink/renderer/core/html/build.gni b/third_party/blink/renderer/core/html/build.gni
index 04bcdeb9..b993fbb 100644
--- a/third_party/blink/renderer/core/html/build.gni
+++ b/third_party/blink/renderer/core/html/build.gni
@@ -9,8 +9,6 @@
   "anchor_element_metrics_sender.h",
   "anchor_element_observer.cc",
   "anchor_element_observer.h",
-  "anchor_element_observer_for_service_worker.cc",
-  "anchor_element_observer_for_service_worker.h",
   "blocking_attribute.cc",
   "blocking_attribute.h",
   "canvas/canvas_async_blob_creator.cc",
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
index ed11f39f..26fff855 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
@@ -344,9 +344,8 @@
         GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kError,
-            WebString::FromUTF8(
-                "Error while parsing the 'sandbox' attribute: " +
-                parsed.error_message)));
+            "Error while parsing the 'sandbox' attribute: " +
+                String::FromUTF8(parsed.error_message)));
       }
     }
     SetSandboxFlags(current_flags);
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index a8c6713f..fc9fb4ab 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -49,7 +49,6 @@
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/anchor_element_metrics_sender.h"
-#include "third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/html_names.h"
@@ -247,29 +246,6 @@
   if (IsLink()) {
     EmitDidAnchorElementReceiveMouseEvent(*this, event);
 
-    static const bool kSpeculativeServiceWorkerWarmUpIsEnabled =
-        base::FeatureList::IsEnabled(features::kSpeculativeServiceWorkerWarmUp);
-    static const bool kSpeculativeServiceWorkerWarmUpOnPointerover =
-        features::kSpeculativeServiceWorkerWarmUpOnPointerover.Get();
-    static const bool kSpeculativeServiceWorkerWarmUpOnPointerdown =
-        features::kSpeculativeServiceWorkerWarmUpOnPointerdown.Get();
-    if (isConnected() && kSpeculativeServiceWorkerWarmUpIsEnabled) {
-      Document& top_document = GetDocument().TopDocument();
-      if (auto* observer =
-              AnchorElementObserverForServiceWorker::From(top_document)) {
-        if (kSpeculativeServiceWorkerWarmUpOnPointerover &&
-            (event.type() == event_type_names::kMouseover ||
-             event.type() == event_type_names::kPointerover)) {
-          observer->MaybeSendNavigationTargetLinks({this});
-        } else if (kSpeculativeServiceWorkerWarmUpOnPointerdown &&
-                   (event.type() == event_type_names::kMousedown ||
-                    event.type() == event_type_names::kPointerdown ||
-                    event.type() == event_type_names::kTouchstart)) {
-          observer->MaybeSendNavigationTargetLinks({this});
-        }
-      }
-    }
-
     if (IsFocused() && IsEnterKeyKeydownEvent(event) && IsLiveLink()) {
       event.SetDefaultHandled();
       DispatchSimulatedClick(&event);
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index a6f92ac..48f60018 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -211,9 +211,8 @@
         GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
             mojom::blink::ConsoleMessageSource::kOther,
             mojom::blink::ConsoleMessageLevel::kError,
-            WebString::FromUTF8(
-                "Error while parsing the 'sandbox' attribute: " +
-                parsed.error_message)));
+            "Error while parsing the 'sandbox' attribute: " +
+                String::FromUTF8(parsed.error_message)));
       }
     }
     SetSandboxFlags(current_flags);
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index eac8c2dc..3ee116d 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -410,8 +410,8 @@
       GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
           mojom::blink::ConsoleMessageSource::kOther,
           mojom::blink::ConsoleMessageLevel::kError,
-          WebString::FromUTF8("sharedStorageWritable: sharedStorage operations "
-                              "are only available in secure contexts.")));
+          "sharedStorageWritable: sharedStorage operations are only available "
+          "in secure contexts."));
     } else if (!params.new_value.IsNull()) {
       UseCounter::Count(GetDocument(),
                         WebFeature::kSharedStorageAPI_Image_Attribute);
diff --git a/third_party/blink/renderer/core/html/media/DEPS b/third_party/blink/renderer/core/html/media/DEPS
index 3cc770b..4ec22ab 100644
--- a/third_party/blink/renderer/core/html/media/DEPS
+++ b/third_party/blink/renderer/core/html/media/DEPS
@@ -3,6 +3,7 @@
     "+media/base/media_content_type.h",
     "+media/base/media_switches.h",
     "+media/base/video_frame.h",
+    "+media/base/media_track.h",
     "+media/mojo/mojom/media_player.mojom-blink.h",
     "+services/media_session/public/mojom/media_session.mojom-blink.h",
 ]
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 3b2b6b3..36a7d5d 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -41,6 +41,7 @@
 #include "cc/layers/layer.h"
 #include "media/base/media_content_type.h"
 #include "media/base/media_switches.h"
+#include "media/base/media_track.h"
 #include "services/media_session/public/mojom/media_session.mojom-blink.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
@@ -255,52 +256,6 @@
   AudioSourceProviderClient* client_;
 };
 
-const AtomicString& AudioKindToString(
-    WebMediaPlayerClient::AudioTrackKind kind) {
-  switch (kind) {
-    case WebMediaPlayerClient::kAudioTrackKindNone:
-      return g_empty_atom;
-    case WebMediaPlayerClient::kAudioTrackKindAlternative:
-      return AudioTrack::AlternativeKeyword();
-    case WebMediaPlayerClient::kAudioTrackKindDescriptions:
-      return AudioTrack::DescriptionsKeyword();
-    case WebMediaPlayerClient::kAudioTrackKindMain:
-      return AudioTrack::MainKeyword();
-    case WebMediaPlayerClient::kAudioTrackKindMainDescriptions:
-      return AudioTrack::MainDescriptionsKeyword();
-    case WebMediaPlayerClient::kAudioTrackKindTranslation:
-      return AudioTrack::TranslationKeyword();
-    case WebMediaPlayerClient::kAudioTrackKindCommentary:
-      return AudioTrack::CommentaryKeyword();
-  }
-
-  NOTREACHED_IN_MIGRATION();
-  return g_empty_atom;
-}
-
-const AtomicString& VideoKindToString(
-    WebMediaPlayerClient::VideoTrackKind kind) {
-  switch (kind) {
-    case WebMediaPlayerClient::kVideoTrackKindNone:
-      return g_empty_atom;
-    case WebMediaPlayerClient::kVideoTrackKindAlternative:
-      return VideoTrack::AlternativeKeyword();
-    case WebMediaPlayerClient::kVideoTrackKindCaptions:
-      return VideoTrack::CaptionsKeyword();
-    case WebMediaPlayerClient::kVideoTrackKindMain:
-      return VideoTrack::MainKeyword();
-    case WebMediaPlayerClient::kVideoTrackKindSign:
-      return VideoTrack::SignKeyword();
-    case WebMediaPlayerClient::kVideoTrackKindSubtitles:
-      return VideoTrack::SubtitlesKeyword();
-    case WebMediaPlayerClient::kVideoTrackKindCommentary:
-      return VideoTrack::CommentaryKeyword();
-  }
-
-  NOTREACHED_IN_MIGRATION();
-  return g_empty_atom;
-}
-
 bool CanLoadURL(const KURL& url, const String& content_type_str) {
   DEFINE_STATIC_LOCAL(const String, codecs, ("codecs"));
 
@@ -3276,30 +3231,6 @@
   web_media_player_->EnabledAudioTracksChanged(enabled_track_ids);
 }
 
-WebMediaPlayer::TrackId HTMLMediaElement::AddAudioTrack(
-    const WebString& id,
-    WebMediaPlayerClient::AudioTrackKind kind,
-    const WebString& label,
-    const WebString& language,
-    bool enabled) {
-  AtomicString kind_string = AudioKindToString(kind);
-  DVLOG(3) << "addAudioTrack(" << *this << ", '" << String(id) << "', ' "
-           << kind_string << "', '" << String(label) << "', '"
-           << String(language) << "', " << BoolString(enabled) << ")";
-
-  auto* audio_track = MakeGarbageCollected<AudioTrack>(id, kind_string, label,
-                                                       language, enabled);
-  audioTracks().Add(audio_track);
-
-  return audio_track->id();
-}
-
-void HTMLMediaElement::RemoveAudioTrack(WebMediaPlayer::TrackId track_id) {
-  DVLOG(3) << "removeAudioTrack(" << *this << ")";
-
-  audioTracks().Remove(track_id);
-}
-
 VideoTrackList& HTMLMediaElement::videoTracks() {
   return *video_tracks_;
 }
@@ -3321,32 +3252,39 @@
                                                                  : nullptr);
 }
 
-WebMediaPlayer::TrackId HTMLMediaElement::AddVideoTrack(
-    const WebString& id,
-    WebMediaPlayerClient::VideoTrackKind kind,
-    const WebString& label,
-    const WebString& language,
-    bool selected) {
-  AtomicString kind_string = VideoKindToString(kind);
-  DVLOG(3) << "addVideoTrack(" << *this << ", '" << String(id) << "', '"
-           << kind_string << "', '" << String(label) << "', '"
-           << String(language) << "', " << BoolString(selected) << ")";
-
-  // If another track was selected (potentially by the user), leave it selected.
-  if (selected && videoTracks().selectedIndex() != -1)
-    selected = false;
-
-  auto* video_track = MakeGarbageCollected<VideoTrack>(id, kind_string, label,
-                                                       language, selected);
-  videoTracks().Add(video_track);
-
-  return video_track->id();
+void HTMLMediaElement::AddMediaTrack(const media::MediaTrack& track) {
+  switch (track.type()) {
+    case media::MediaTrack::Type::kVideo: {
+      bool enabled = track.enabled() && videoTracks().selectedIndex() == -1;
+      videoTracks().Add(MakeGarbageCollected<VideoTrack>(
+          String::FromUTF8(track.id().value()),
+          AtomicString::FromUTF8(track.kind().value()),
+          AtomicString::FromUTF8(track.label().value()),
+          AtomicString::FromUTF8(track.language().value()), enabled));
+      break;
+    }
+    case media::MediaTrack::Type::kAudio: {
+      audioTracks().Add(MakeGarbageCollected<AudioTrack>(
+          String::FromUTF8(track.id().value()),
+          AtomicString::FromUTF8(track.kind().value()),
+          AtomicString::FromUTF8(track.label().value()),
+          AtomicString::FromUTF8(track.language().value()), track.enabled()));
+      break;
+    }
+  }
 }
 
-void HTMLMediaElement::RemoveVideoTrack(WebMediaPlayer::TrackId track_id) {
-  DVLOG(3) << "removeVideoTrack(" << *this << ")";
-
-  videoTracks().Remove(track_id);
+void HTMLMediaElement::RemoveMediaTrack(const media::MediaTrack& track) {
+  switch (track.type()) {
+    case media::MediaTrack::Type::kVideo: {
+      videoTracks().Remove(String::FromUTF8(track.id().value()));
+      break;
+    }
+    case media::MediaTrack::Type::kAudio: {
+      audioTracks().Remove(String::FromUTF8(track.id().value()));
+      break;
+    }
+  }
 }
 
 void HTMLMediaElement::ForgetResourceSpecificTracks() {
@@ -4498,15 +4436,15 @@
   // Create a placeholder audio track if the player says it has audio but it
   // didn't explicitly announce the tracks.
   if (HasAudio() && !audioTracks().length()) {
-    AddAudioTrack("audio", WebMediaPlayerClient::kAudioTrackKindMain,
-                  "Audio Track", "", true);
+    AddMediaTrack(media::MediaTrack::CreateAudioTrack(
+        "audio", media::MediaTrack::AudioKind::kMain, "Audio Track", "", true));
   }
 
   // Create a placeholder video track if the player says it has video but it
   // didn't explicitly announce the tracks.
   if (HasVideo() && !videoTracks().length()) {
-    AddVideoTrack("video", WebMediaPlayerClient::kVideoTrackKindMain,
-                  "Video Track", "", true);
+    AddMediaTrack(media::MediaTrack::CreateVideoTrack(
+        "video", media::MediaTrack::VideoKind::kMain, "Video Track", "", true));
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index 27f7efb6..44f50f9 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -547,18 +547,10 @@
   int GetElementId() override { return GetDomNodeId(); }
 
   void SetCcLayer(cc::Layer*) final;
-  WebMediaPlayer::TrackId AddAudioTrack(const WebString&,
-                                        WebMediaPlayerClient::AudioTrackKind,
-                                        const WebString&,
-                                        const WebString&,
-                                        bool) final;
-  void RemoveAudioTrack(WebMediaPlayer::TrackId) final;
-  WebMediaPlayer::TrackId AddVideoTrack(const WebString&,
-                                        WebMediaPlayerClient::VideoTrackKind,
-                                        const WebString&,
-                                        const WebString&,
-                                        bool) final;
-  void RemoveVideoTrack(WebMediaPlayer::TrackId) final;
+
+  void AddMediaTrack(const media::MediaTrack&) final;
+  void RemoveMediaTrack(const media::MediaTrack&) final;
+
   void MediaSourceOpened(std::unique_ptr<WebMediaSource>) final;
   void RemotePlaybackCompatibilityChanged(const WebURL&,
                                           bool is_compatible) final;
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
index 69efe60..2f303141 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
@@ -1479,8 +1479,7 @@
     property_value = property_value.Substring(
         0, property_value.length() - 10 /* length of "!important" */);
   }
-  CSSTokenizer tokenizer(property_value);
-  CSSParserTokenStream stream(tokenizer);
+  CSSParserTokenStream stream(property_value);
   stream.EnsureLookAhead();  // Several parsers expect this.
   CSSPropertyID property_id =
       CssPropertyID(style_->GetExecutionContext(), property_entry.name);
diff --git a/third_party/blink/renderer/core/layout/absolute_utils.cc b/third_party/blink/renderer/core/layout/absolute_utils.cc
index 782b7d9..5435fce 100644
--- a/third_party/blink/renderer/core/layout/absolute_utils.cc
+++ b/third_party/blink/renderer/core/layout/absolute_utils.cc
@@ -135,11 +135,11 @@
     const LayoutUnit static_position_offset,
     InsetBias static_position_inset_bias,
     InsetBias alignment_inset_bias,
-    const std::optional<InsetBias>& safe_alignment_inset_bias,
+    const std::optional<InsetBias>& safe_inset_bias,
     LayoutUnit* imcb_start_out,
     LayoutUnit* imcb_end_out,
     InsetBias* imcb_inset_bias_out,
-    std::optional<InsetBias>* imcb_safe_inset_bias_out) {
+    std::optional<InsetBias>* safe_inset_bias_out) {
   DCHECK_NE(available_size, kIndefiniteSize);
   if (!inset_start && !inset_end) {
     // If both our insets are auto, the available-space is defined by the
@@ -184,7 +184,7 @@
       // Both insets were set - use the alignment bias (defaults to the "start"
       // edge of the containing block if we have normal alignment).
       *imcb_inset_bias_out = alignment_inset_bias;
-      *imcb_safe_inset_bias_out = safe_alignment_inset_bias;
+      *safe_inset_bias_out = safe_inset_bias;
     }
   }
 }
@@ -206,31 +206,29 @@
       IsParallelWritingMode(container_writing_direction.GetWritingMode(),
                             self_writing_direction.GetWritingMode());
 
-  std::optional<InsetBias> safe_inline_alignment_inset_bias;
+  std::optional<InsetBias> inline_safe_inset_bias;
   const auto inline_alignment_inset_bias = GetAlignmentInsetBias(
       alignment.inline_alignment, container_writing_direction,
       self_writing_direction,
-      /* is_justify_axis */ is_parallel, &safe_inline_alignment_inset_bias);
-  std::optional<InsetBias> safe_block_alignment_inset_bias;
+      /* is_justify_axis */ is_parallel, &inline_safe_inset_bias);
+  std::optional<InsetBias> block_safe_inset_bias;
   const auto block_alignment_inset_bias = GetAlignmentInsetBias(
       alignment.block_alignment, container_writing_direction,
       self_writing_direction,
-      /* is_justify_axis */ !is_parallel, &safe_block_alignment_inset_bias);
+      /* is_justify_axis */ !is_parallel, &block_safe_inset_bias);
 
   ComputeUnclampedIMCBInOneAxis(
       available_size.inline_size, insets.inline_start, insets.inline_end,
       static_position.offset.inline_offset,
       GetStaticPositionInsetBias(static_position.inline_edge),
-      inline_alignment_inset_bias, safe_inline_alignment_inset_bias,
-      &imcb.inline_start, &imcb.inline_end, &imcb.inline_inset_bias,
-      &imcb.safe_inline_inset_bias);
+      inline_alignment_inset_bias, inline_safe_inset_bias, &imcb.inline_start,
+      &imcb.inline_end, &imcb.inline_inset_bias, &imcb.inline_safe_inset_bias);
   ComputeUnclampedIMCBInOneAxis(
       available_size.block_size, insets.block_start, insets.block_end,
       static_position.offset.block_offset,
       GetStaticPositionInsetBias(static_position.block_edge),
-      block_alignment_inset_bias, safe_block_alignment_inset_bias,
-      &imcb.block_start, &imcb.block_end, &imcb.block_inset_bias,
-      &imcb.safe_block_inset_bias);
+      block_alignment_inset_bias, block_safe_inset_bias, &imcb.block_start,
+      &imcb.block_end, &imcb.block_inset_bias, &imcb.block_safe_inset_bias);
   return imcb;
 }
 
@@ -300,7 +298,7 @@
                    LayoutUnit imcb_start,
                    LayoutUnit imcb_end,
                    const InsetBias imcb_inset_bias,
-                   const std::optional<InsetBias>& imcb_safe_inset_bias,
+                   const std::optional<InsetBias>& safe_inset_bias,
                    const LayoutUnit margin_start,
                    const LayoutUnit margin_end,
                    const LayoutUnit size,
@@ -310,9 +308,9 @@
   LayoutUnit free_space =
       available_size - imcb_start - imcb_end - margin_start - margin_end - size;
   InsetBias bias = imcb_inset_bias;
-  if (imcb_safe_inset_bias && free_space < LayoutUnit()) {
+  if (safe_inset_bias && free_space < LayoutUnit()) {
     free_space = LayoutUnit();
-    bias = *imcb_safe_inset_bias;
+    bias = *safe_inset_bias;
   }
 
   // Move the weaker inset edge to consume all the free space, so that:
@@ -434,8 +432,10 @@
     const ComputedStyle& style,
     WritingDirectionMode container_writing_direction,
     WritingDirectionMode self_writing_direction) {
-  ItemPosition align_normal_behavior = ItemPosition::kNormal;
-  ItemPosition justify_normal_behavior = ItemPosition::kNormal;
+  StyleSelfAlignmentData align_normal_behavior(ItemPosition::kNormal,
+                                               OverflowAlignment::kDefault);
+  StyleSelfAlignmentData justify_normal_behavior(ItemPosition::kNormal,
+                                                 OverflowAlignment::kDefault);
   const PositionArea position_area = style.GetPositionArea().ToPhysical(
       container_writing_direction, self_writing_direction);
   if (!position_area.IsNone()) {
@@ -517,14 +517,14 @@
                               *anchor_center_position.inline_offset,
                               &imcb.inline_start, &imcb.inline_end);
     imcb.inline_inset_bias = InsetBias::kEqual;
-    imcb.safe_inline_inset_bias = std::nullopt;
+    imcb.inline_safe_inset_bias = std::nullopt;
   }
   if (anchor_center_position.block_offset) {
     ResizeIMCBForCenterOffset(available_size.block_size,
                               *anchor_center_position.block_offset,
                               &imcb.block_start, &imcb.block_end);
     imcb.block_inset_bias = InsetBias::kEqual;
-    imcb.safe_block_inset_bias = std::nullopt;
+    imcb.block_safe_inset_bias = std::nullopt;
   }
   // Clamp any negative size to 0.
   if (imcb.InlineSize() < LayoutUnit()) {
@@ -698,7 +698,7 @@
 
   ComputeInsets(space.AvailableSize().inline_size, imcb.inline_start,
                 imcb.inline_end, imcb.inline_inset_bias,
-                imcb.safe_inline_inset_bias, dimensions->margins.inline_start,
+                imcb.inline_safe_inset_bias, dimensions->margins.inline_start,
                 dimensions->margins.inline_end, inline_size,
                 &dimensions->inset.inline_start, &dimensions->inset.inline_end);
 
@@ -794,7 +794,7 @@
 
   ComputeInsets(space.AvailableSize().block_size, imcb.block_start,
                 imcb.block_end, imcb.block_inset_bias,
-                imcb.safe_block_inset_bias, dimensions->margins.block_start,
+                imcb.block_safe_inset_bias, dimensions->margins.block_start,
                 dimensions->margins.block_end, block_size,
                 &dimensions->inset.block_start, &dimensions->inset.block_end);
 
diff --git a/third_party/blink/renderer/core/layout/absolute_utils.h b/third_party/blink/renderer/core/layout/absolute_utils.h
index 2f97a8a..ce5cfae 100644
--- a/third_party/blink/renderer/core/layout/absolute_utils.h
+++ b/third_party/blink/renderer/core/layout/absolute_utils.h
@@ -106,8 +106,8 @@
   // If safe alignment is specified (e.g. "align-self: safe end") and the
   // object overflows its containing block it'll become start aligned instead.
   // This field indicates the "start" edge of the containing block.
-  std::optional<InsetBias> safe_inline_inset_bias;
-  std::optional<InsetBias> safe_block_inset_bias;
+  std::optional<InsetBias> inline_safe_inset_bias;
+  std::optional<InsetBias> block_safe_inset_bias;
 
   LayoutUnit InlineEndOffset() const {
     return available_size.inline_size - inline_end;
diff --git a/third_party/blink/renderer/core/layout/block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/block_layout_algorithm.cc
index 1022b24..5720376 100644
--- a/third_party/blink/renderer/core/layout/block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/block_layout_algorithm.cc
@@ -250,7 +250,9 @@
   DCHECK(!child_style.MarginInlineEndUsing(style).IsAuto());
 
   ItemPosition justify_self =
-      child_style.ResolvedJustifySelf(ItemPosition::kNormal, &style)
+      child_style
+          .ResolvedJustifySelf(
+              {ItemPosition::kNormal, OverflowAlignment::kDefault}, &style)
           .GetPosition();
   if (!RuntimeEnabledFeatures::LayoutJustifySelfForBlocksEnabled() ||
       justify_self == ItemPosition::kNormal) {
@@ -3048,7 +3050,10 @@
           !has_auto_margins;
 
       const ItemPosition justify_self =
-          child_style.ResolvedJustifySelf(ItemPosition::kNormal, &Style())
+          child_style
+              .ResolvedJustifySelf(
+                  {ItemPosition::kNormal, OverflowAlignment::kDefault},
+                  &Style())
               .GetPosition();
 
       if (justify_self_affects_sizing &&
@@ -3129,7 +3134,9 @@
       !has_auto_margins;
 
   const ItemPosition justify_self =
-      child_style.ResolvedJustifySelf(ItemPosition::kNormal, &Style())
+      child_style
+          .ResolvedJustifySelf(
+              {ItemPosition::kNormal, OverflowAlignment::kDefault}, &Style())
           .GetPosition();
 
   if (justify_self_affects_sizing && justify_self == ItemPosition::kStretch) {
diff --git a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
index c3b5c14..891b6f1 100644
--- a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
@@ -283,12 +283,14 @@
       if (is_column_) {
         const ItemPosition normalized_alignment =
             FlexibleBoxAlgorithm::AlignmentForChild(style, child_style);
-        const ItemPosition default_justify_self_behavior =
-            child.IsReplaced() ? ItemPosition::kStart : ItemPosition::kStretch;
         const ItemPosition normalized_justify =
             FlexibleBoxAlgorithm::TranslateItemPosition(
                 style, child_style,
-                child_style.ResolvedJustifySelf(default_justify_self_behavior)
+                child_style
+                    .ResolvedJustifySelf({child.IsReplaced()
+                                              ? ItemPosition::kStart
+                                              : ItemPosition::kStretch,
+                                          OverflowAlignment::kDefault})
                     .GetPosition());
 
         const bool are_cross_axis_insets_auto =
diff --git a/third_party/blink/renderer/core/layout/flex/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flex/flexible_box_algorithm.cc
index 51c03a1..d3c1d3a 100644
--- a/third_party/blink/renderer/core/layout/flex/flexible_box_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flex/flexible_box_algorithm.cc
@@ -277,9 +277,10 @@
   const bool is_wrap_reverse =
       parent_style->FlexWrap() == EFlexWrap::kWrapReverse;
   const ItemPosition position = Alignment();
-  if (!is_webkit_box &&
-      style_->ResolvedAlignSelf(ItemPosition::kStretch, parent_style)
-              .Overflow() == OverflowAlignment::kSafe) {
+  if (!is_webkit_box && style_->ResolvedAlignSelf({ItemPosition::kStretch,
+                                                   OverflowAlignment::kDefault},
+                                                  parent_style)
+                                .Overflow() == OverflowAlignment::kSafe) {
     available_space = available_space.ClampNegativeToZero();
   }
 
@@ -964,7 +965,9 @@
       flexbox_style.IsDeprecatedWebkitBox()
           ? BoxAlignmentToItemPosition(flexbox_style.BoxAlign())
           : child_style
-                .ResolvedAlignSelf(ItemPosition::kStretch, &flexbox_style)
+                .ResolvedAlignSelf(
+                    {ItemPosition::kStretch, OverflowAlignment::kDefault},
+                    &flexbox_style)
                 .GetPosition();
   return TranslateItemPosition(flexbox_style, child_style, align);
 }
@@ -1000,9 +1003,10 @@
   }
 
   if (align == ItemPosition::kLeft || align == ItemPosition::kRight) {
-    DCHECK_EQ(
-        align,
-        child_style.ResolvedJustifySelf(ItemPosition::kStretch).GetPosition())
+    DCHECK_EQ(align, child_style
+                         .ResolvedJustifySelf({ItemPosition::kStretch,
+                                               OverflowAlignment::kDefault})
+                         .GetPosition())
         << "justify-self is the only way that we can get a left or right "
            "ItemPosition";
     DCHECK(IsColumnFlow(flexbox_style))
diff --git a/third_party/blink/renderer/core/layout/grid/grid_item.cc b/third_party/blink/renderer/core/layout/grid/grid_item.cc
index 6f9bc04..f5c0aa6d 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_item.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_item.cc
@@ -35,15 +35,15 @@
   const bool is_for_columns = track_direction == kForColumns;
   const auto root_grid_writing_direction =
       root_grid_style.GetWritingDirection();
+  const StyleSelfAlignmentData normal_value(ItemPosition::kNormal,
+                                            OverflowAlignment::kDefault);
 
   const auto& alignment =
       (is_for_columns ==
        IsParallelWritingMode(root_grid_writing_direction.GetWritingMode(),
                              parent_grid_style.GetWritingMode()))
-          ? item_style.ResolvedJustifySelf(ItemPosition::kNormal,
-                                           &parent_grid_style)
-          : item_style.ResolvedAlignSelf(ItemPosition::kNormal,
-                                         &parent_grid_style);
+          ? item_style.ResolvedJustifySelf(normal_value, &parent_grid_style)
+          : item_style.ResolvedAlignSelf(normal_value, &parent_grid_style);
 
   *auto_behavior = AutoSizeBehavior::kFitContent;
   *is_overflow_safe = alignment.Overflow() == OverflowAlignment::kSafe;
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index a987b58..adaff0f 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -1793,21 +1793,6 @@
   });
 }
 
-LayoutObject* LayoutObject::ContainerForColumnSpanAll(
-    AncestorSkipInfo* skip_info) const {
-  NOT_DESTROYED();
-  LayoutObject* multicol_container = SpannerPlaceholder()->Container();
-  if (skip_info) {
-    // We jumped directly from the spanner to the multicol container. Need to
-    // check if we skipped |ancestor| or filter/reflection on the way.
-    for (LayoutObject* walker = Parent();
-         walker && walker != multicol_container; walker = walker->Parent()) {
-      skip_info->Update(*walker);
-    }
-  }
-  return multicol_container;
-}
-
 LayoutBlock* LayoutObject::ContainingBlockForAbsolutePosition(
     AncestorSkipInfo* skip_info) const {
   NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index c0b661a..5ad03571 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1850,35 +1850,11 @@
   // walk the containing block chain. See e.g. markContainerChainForLayout.
   // It is also used for correctly sizing absolutely positioned elements
   // (point 3 above).
-  LayoutObject* Container(AncestorSkipInfo* skip_info = nullptr) const {
-    NOT_DESTROYED();
-#if DCHECK_IS_ON()
-    if (skip_info) {
-      skip_info->AssertClean();
-    }
-#endif
-
-    if (IsColumnSpanAll()) [[unlikely]] {
-      return ContainerForColumnSpanAll(skip_info);
-    }
-
-    if (IsOutOfFlowPositioned()) {
-      if (style_->GetPosition() == EPosition::kFixed) {
-        return ContainerForFixedPosition(skip_info);
-      }
-      DCHECK_EQ(style_->GetPosition(), EPosition::kAbsolute);
-      return ContainerForAbsolutePosition(skip_info);
-    }
-
-    return Parent();
-  }
-
+  LayoutObject* Container(AncestorSkipInfo* = nullptr) const;
   // Finds the container as if this object is absolute-position.
   LayoutObject* ContainerForAbsolutePosition(AncestorSkipInfo* = nullptr) const;
   // Finds the container as if this object is fixed-position.
   LayoutObject* ContainerForFixedPosition(AncestorSkipInfo* = nullptr) const;
-  // Finds the container as if this object is a column-spanner.
-  LayoutObject* ContainerForColumnSpanAll(AncestorSkipInfo* = nullptr) const;
 
   bool CanContainOutOfFlowPositionedElement(EPosition position) const {
     NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/layout_object_hot.cc b/third_party/blink/renderer/core/layout/layout_object_hot.cc
index cc17e428..144d602 100644
--- a/third_party/blink/renderer/core/layout/layout_object_hot.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_hot.cc
@@ -26,6 +26,59 @@
   DisplayItemClient::Trace(visitor);
 }
 
+LayoutObject* LayoutObject::Container(AncestorSkipInfo* skip_info) const {
+  NOT_DESTROYED();
+
+#if DCHECK_IS_ON()
+  if (skip_info)
+    skip_info->AssertClean();
+#endif
+
+  if (IsTextOrSVGChild())
+    return Parent();
+
+  EPosition pos = style_->GetPosition();
+  if (pos == EPosition::kFixed)
+    return ContainerForFixedPosition(skip_info);
+
+  if (pos == EPosition::kAbsolute) {
+    return ContainerForAbsolutePosition(skip_info);
+  }
+
+  if (IsColumnSpanAll()) {
+    LayoutObject* multicol_container = SpannerPlaceholder()->Container();
+    if (skip_info) {
+      // We jumped directly from the spanner to the multicol container. Need to
+      // check if we skipped |ancestor| or filter/reflection on the way.
+      for (LayoutObject* walker = Parent();
+           walker && walker != multicol_container; walker = walker->Parent())
+        skip_info->Update(*walker);
+    }
+    return multicol_container;
+  }
+
+  if (IsFloating() && !IsInLayoutNGInlineFormattingContext()) {
+    // TODO(crbug.com/1229581): Remove this when removing support for legacy
+    // layout.
+    //
+    // In the legacy engine, floats inside non-atomic inlines belong to their
+    // nearest containing block, not the parent non-atomic inline (if any). Skip
+    // past all non-atomic inlines. Note that the reason for not simply using
+    // ContainingBlock() here is that we want to stop at any kind of LayoutBox,
+    // such as LayoutVideo. Otherwise we won't mark the container chain
+    // correctly when marking for re-layout.
+    LayoutObject* walker = Parent();
+    while (walker && walker->IsLayoutInline()) {
+      if (skip_info)
+        skip_info->Update(*walker);
+      walker = walker->Parent();
+    }
+    return walker;
+  }
+
+  return Parent();
+}
+
 LayoutBox* LayoutObject::DeprecatedEnclosingScrollableBox() const {
   NOT_DESTROYED();
   DCHECK(!RuntimeEnabledFeatures::IntersectionOptimizationEnabled());
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource.cc b/third_party/blink/renderer/core/loader/resource/image_resource.cc
index b637257..5da149e 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource.cc
@@ -137,10 +137,9 @@
   response.SetHttpStatusText(AtomicString("OK"));
   response.SetCurrentRequestUrl(request.Url());
   response.SetExpectedContentLength(data->size());
-  response.SetTextEncodingName(WebString::FromUTF8(""));
-  response.SetMimeType(WebString::FromUTF8("image/gif"));
-  response.AddHttpHeaderField(WebString::FromUTF8("Content-Type"),
-                              WebString::FromUTF8("image/gif"));
+  response.SetTextEncodingName(g_empty_atom);
+  response.SetMimeType(AtomicString("image/gif"));
+  response.AddHttpHeaderField(http_names::kContentType, response.MimeType());
 
   // The below code is the same as in
   // `ResourceFetcher::CreateResourceForStaticData()`.
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 5bd6035..e615169 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -94,6 +94,7 @@
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/platform/bindings/script_regexp.h"
 #include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_visitor.h"
 
 namespace blink {
 
@@ -731,8 +732,14 @@
         spell_checker.SelectMisspellingAsync();
     const String& misspelled_word = misspelled_word_and_description.first;
     if (misspelled_word.length()) {
-      data.misspelled_word =
-          WebString::FromUTF8(misspelled_word.Utf8()).Utf16();
+      auto to_u16string = [](const String& s) -> std::u16string {
+        return s.empty() ? std::u16string()
+                         : WTF::VisitCharacters(
+                               s, [](const auto* chars, wtf_size_t length) {
+                                 return std::u16string(chars, chars + length);
+                               });
+      };
+      data.misspelled_word = to_u16string(misspelled_word);
       const String& description = misspelled_word_and_description.second;
       if (description.length()) {
         // Suggestions were cached for the misspelled word (won't be true for
@@ -742,9 +749,7 @@
         description.Split('\n', suggestions);
         WebVector<std::u16string> web_suggestions(suggestions.size());
         base::ranges::transform(suggestions, web_suggestions.begin(),
-                                [](const String& s) {
-                                  return WebString::FromUTF8(s.Utf8()).Utf16();
-                                });
+                                to_u16string);
         data.dictionary_suggestions = web_suggestions.ReleaseVector();
       } else if (spell_checker.GetTextCheckerClient()) {
         // No suggestions cached for the misspelled word. Retrieve suggestions
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 58de414..f32db1fb 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -481,12 +481,12 @@
 
 StyleSelfAlignmentData ResolvedSelfAlignment(
     const StyleSelfAlignmentData& value,
-    ItemPosition normal_value_behavior,
+    const StyleSelfAlignmentData& normal_value_behavior,
     bool has_out_of_flow_position) {
   if (value.GetPosition() == ItemPosition::kLegacy ||
       value.GetPosition() == ItemPosition::kNormal ||
       value.GetPosition() == ItemPosition::kAuto) {
-    return {normal_value_behavior, OverflowAlignment::kDefault};
+    return normal_value_behavior;
   }
   if (!has_out_of_flow_position &&
       value.GetPosition() == ItemPosition::kAnchorCenter) {
@@ -496,33 +496,33 @@
 }
 
 StyleSelfAlignmentData ComputedStyle::ResolvedAlignSelf(
-    ItemPosition normal_value_behaviour,
+    const StyleSelfAlignmentData& normal_value_behavior,
     const ComputedStyle* parent_style) const {
   // We will return the behaviour of 'normal' value if needed, which is specific
   // of each layout model.
   if (!parent_style || AlignSelf().GetPosition() != ItemPosition::kAuto) {
-    return ResolvedSelfAlignment(AlignSelf(), normal_value_behaviour,
+    return ResolvedSelfAlignment(AlignSelf(), normal_value_behavior,
                                  HasOutOfFlowPosition());
   }
 
   // The 'auto' keyword computes to the parent's align-items computed value.
   return ResolvedSelfAlignment(parent_style->AlignItems(),
-                               normal_value_behaviour, HasOutOfFlowPosition());
+                               normal_value_behavior, HasOutOfFlowPosition());
 }
 
 StyleSelfAlignmentData ComputedStyle::ResolvedJustifySelf(
-    ItemPosition normal_value_behaviour,
+    const StyleSelfAlignmentData& normal_value_behavior,
     const ComputedStyle* parent_style) const {
   // We will return the behaviour of 'normal' value if needed, which is specific
   // of each layout model.
   if (!parent_style || JustifySelf().GetPosition() != ItemPosition::kAuto) {
-    return ResolvedSelfAlignment(JustifySelf(), normal_value_behaviour,
+    return ResolvedSelfAlignment(JustifySelf(), normal_value_behavior,
                                  HasOutOfFlowPosition());
   }
 
   // The auto keyword computes to the parent's justify-items computed value.
   return ResolvedSelfAlignment(parent_style->JustifyItems(),
-                               normal_value_behaviour, HasOutOfFlowPosition());
+                               normal_value_behavior, HasOutOfFlowPosition());
 }
 
 StyleContentAlignmentData ResolvedContentAlignment(
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 50edcdb..dbd610c 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -403,12 +403,12 @@
   ContentDistributionType ResolvedAlignContentDistribution(
       const StyleContentAlignmentData& normal_value_behavior) const;
   StyleSelfAlignmentData ResolvedAlignSelf(
-      ItemPosition normal_value_behaviour,
+      const StyleSelfAlignmentData& normal_value_behavior,
       const ComputedStyle* parent_style = nullptr) const;
   StyleContentAlignmentData ResolvedAlignContent(
       const StyleContentAlignmentData& normal_behaviour) const;
   StyleSelfAlignmentData ResolvedJustifySelf(
-      ItemPosition normal_value_behaviour,
+      const StyleSelfAlignmentData& normal_value_behavior,
       const ComputedStyle* parent_style = nullptr) const;
   StyleContentAlignmentData ResolvedJustifyContent(
       const StyleContentAlignmentData& normal_behaviour) const;
diff --git a/third_party/blink/renderer/core/style/position_area.cc b/third_party/blink/renderer/core/style/position_area.cc
index b6afd9c..d692b944 100644
--- a/third_party/blink/renderer/core/style/position_area.cc
+++ b/third_party/blink/renderer/core/style/position_area.cc
@@ -289,19 +289,25 @@
   }
 }
 
-std::pair<ItemPosition, ItemPosition> PositionArea::AlignJustifySelfFromPhysical(
+std::pair<StyleSelfAlignmentData, StyleSelfAlignmentData>
+PositionArea::AlignJustifySelfFromPhysical(
     WritingDirectionMode container_writing_direction) const {
-  ItemPosition align = ItemPosition::kStart;
-  ItemPosition align_reverse = ItemPosition::kEnd;
-  ItemPosition justify = ItemPosition::kStart;
-  ItemPosition justify_reverse = ItemPosition::kEnd;
+  StyleSelfAlignmentData align(ItemPosition::kStart,
+                               OverflowAlignment::kDefault);
+  StyleSelfAlignmentData align_reverse(ItemPosition::kEnd,
+                                       OverflowAlignment::kDefault);
+  StyleSelfAlignmentData justify(ItemPosition::kStart,
+                                 OverflowAlignment::kDefault);
+  StyleSelfAlignmentData justify_reverse(ItemPosition::kEnd,
+                                         OverflowAlignment::kDefault);
 
   if ((FirstStart() == PositionAreaRegion::kTop &&
        FirstEnd() == PositionAreaRegion::kBottom) ||
       (FirstStart() == PositionAreaRegion::kCenter &&
        FirstEnd() == PositionAreaRegion::kCenter)) {
     // 'center' or 'all' should align with anchor center.
-    align = align_reverse = ItemPosition::kAnchorCenter;
+    align = align_reverse = {ItemPosition::kAnchorCenter,
+                             OverflowAlignment::kDefault};
   } else {
     // 'top' and 'top center' aligns with end, 'bottom' and 'center bottom' with
     // start.
@@ -314,7 +320,8 @@
       (SecondStart() == PositionAreaRegion::kCenter &&
        SecondEnd() == PositionAreaRegion::kCenter)) {
     // 'center' or 'all' should align with anchor center.
-    justify = justify_reverse = ItemPosition::kAnchorCenter;
+    justify = justify_reverse = {ItemPosition::kAnchorCenter,
+                                 OverflowAlignment::kDefault};
   } else {
     // 'left' and 'left center' aligns with end, 'right' and 'center right' with
     // start.
@@ -325,8 +332,7 @@
 
   PhysicalToLogical converter(container_writing_direction, align,
                               justify_reverse, align_reverse, justify);
-  return std::make_pair<ItemPosition, ItemPosition>(converter.BlockStart(),
-                                                    converter.InlineStart());
+  return {converter.BlockStart(), converter.InlineStart()};
 }
 
 AnchorQuery PositionArea::AnchorTop() {
diff --git a/third_party/blink/renderer/core/style/position_area.h b/third_party/blink/renderer/core/style/position_area.h
index 9577cbc..c3f229a 100644
--- a/third_party/blink/renderer/core/style/position_area.h
+++ b/third_party/blink/renderer/core/style/position_area.h
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/css/anchor_query.h"
 #include "third_party/blink/renderer/core/css/css_anchor_query_enums.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
+#include "third_party/blink/renderer/core/style/style_self_alignment_data.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
@@ -109,7 +110,8 @@
   // different 'normal' behavior for align-self and justify-self. Compute the
   // alignments to be passed into ResolvedAlignSelf()/ResolvedJustifySelf().
   // Return value is an <align-self, justify-self> pair.
-  std::pair<ItemPosition, ItemPosition> AlignJustifySelfFromPhysical(
+  std::pair<StyleSelfAlignmentData, StyleSelfAlignmentData>
+  AlignJustifySelfFromPhysical(
       WritingDirectionMode container_writing_direction) const;
 
   // Made public because they are used in unit test expectations.
diff --git a/third_party/blink/renderer/modules/ai/ai_summarizer_capabilities.cc b/third_party/blink/renderer/modules/ai/ai_summarizer_capabilities.cc
index de59011..84413ab 100644
--- a/third_party/blink/renderer/modules/ai/ai_summarizer_capabilities.cc
+++ b/third_party/blink/renderer/modules/ai/ai_summarizer_capabilities.cc
@@ -14,4 +14,14 @@
   ScriptWrappable::Trace(visitor);
 }
 
+V8AICapabilityAvailability AISummarizerCapabilities::supportsInputLanguage(
+    const WTF::String& language_tag) {
+  constexpr char kLanguageTagEn[] = "en";
+  if (language_tag == kLanguageTagEn) {
+    return V8AICapabilityAvailability(
+        V8AICapabilityAvailability::Enum::kReadily);
+  }
+  return V8AICapabilityAvailability(V8AICapabilityAvailability::Enum::kNo);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/ai/ai_summarizer_capabilities.h b/third_party/blink/renderer/modules/ai/ai_summarizer_capabilities.h
index 84fe99bf..db1f253 100644
--- a/third_party/blink/renderer/modules/ai/ai_summarizer_capabilities.h
+++ b/third_party/blink/renderer/modules/ai/ai_summarizer_capabilities.h
@@ -7,6 +7,9 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ai_capability_availability.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ai_summarizer_format.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ai_summarizer_length.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ai_summarizer_type.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"
 
@@ -23,6 +26,17 @@
 
   // ai_summarizer.idl implementation
   V8AICapabilityAvailability available() { return capability_availability_; }
+  V8AICapabilityAvailability supportsType(V8AISummarizerType type) {
+    return capability_availability_;
+  }
+  V8AICapabilityAvailability supportsFormat(V8AISummarizerFormat format) {
+    return capability_availability_;
+  }
+  V8AICapabilityAvailability supportsLength(V8AISummarizerLength length) {
+    return capability_availability_;
+  }
+  V8AICapabilityAvailability supportsInputLanguage(
+      const WTF::String& language_tag);
 
  private:
   V8AICapabilityAvailability capability_availability_;
diff --git a/third_party/blink/renderer/modules/ai/ai_summarizer_factory.idl b/third_party/blink/renderer/modules/ai/ai_summarizer_factory.idl
index d759efe4..4d47e4f3 100644
--- a/third_party/blink/renderer/modules/ai/ai_summarizer_factory.idl
+++ b/third_party/blink/renderer/modules/ai/ai_summarizer_factory.idl
@@ -10,6 +10,12 @@
 ]
 interface AISummarizerCapabilities {
   readonly attribute AICapabilityAvailability available;
+
+  AICapabilityAvailability supportsType(AISummarizerType type);
+  AICapabilityAvailability supportsFormat(AISummarizerFormat format);
+  AICapabilityAvailability supportsLength(AISummarizerLength length);
+
+  AICapabilityAvailability supportsInputLanguage(DOMString languageTag);
 };
 
 enum AISummarizerType { "tl;dr", "key-points", "teaser", "headline" };
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index 521d274b..c310600 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -972,9 +972,10 @@
     const TextLinkColors& text_link_colors =
         window ? window->document()->GetTextLinkColors()
                : kDefaultTextLinkColors;
-    const StyleColor style_color =
-        ResolveColorValue(*color_mix_value, text_link_colors, color_scheme_,
-                          GetColorProvider(), IsInWebAppScope());
+    // TODO(40946458): Don't use default length resolver here!
+    const StyleColor style_color = ResolveColorValue(
+        CSSToLengthConversionData(), *color_mix_value, text_link_colors,
+        color_scheme_, GetColorProvider(), IsInWebAppScope());
     color = style_color.Resolve(GetCurrentColor(), color_scheme_);
     return ColorParseResult::kColor;
   }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
index df42b6d..6f244a4 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
@@ -75,9 +75,10 @@
               CSSPropertyID::kColor, color_string,
               StrictCSSParserContext(SecureContextMode::kInsecureContext)))) {
     static const TextLinkColors kDefaultTextLinkColors{};
-    const StyleColor style_color =
-        ResolveColorValue(*color_mix_value, kDefaultTextLinkColors,
-                          color_scheme, color_provider, is_in_web_app_scope);
+    // TODO(40946458): Don't use default length resolver here!
+    const StyleColor style_color = ResolveColorValue(
+        CSSToLengthConversionData(), *color_mix_value, kDefaultTextLinkColors,
+        color_scheme, color_provider, is_in_web_app_scope);
     parsed_color = style_color.Resolve(Color::kBlack, color_scheme);
     return ColorParseResult::kColorMix;
   }
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor_unittest.cc b/third_party/blink/renderer/modules/indexeddb/idb_cursor_unittest.cc
index 211aa44e0..cd08dc3 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor_unittest.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor_unittest.cc
@@ -111,7 +111,7 @@
     // Set up `transaction`.
     IDBDatabase* db = MakeGarbageCollected<IDBDatabase>(
         execution_context, mojo::NullAssociatedReceiver(), mojo::NullRemote(),
-        mock_database.BindNewEndpointAndPassDedicatedRemote());
+        mock_database.BindNewEndpointAndPassDedicatedRemote(), /*priority=*/0);
 
     MockIDBTransaction mock_transaction_remote;
     IDBTransaction::TransactionMojoRemote transaction_remote(execution_context);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.cc b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
index fc986af..31a9fb9 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
@@ -101,16 +101,24 @@
     mojo::PendingAssociatedReceiver<mojom::blink::IDBDatabaseCallbacks>
         callbacks_receiver,
     mojo::PendingRemote<mojom::blink::ObservedFeature> connection_lifetime,
-    mojo::PendingAssociatedRemote<mojom::blink::IDBDatabase> pending_database)
+    mojo::PendingAssociatedRemote<mojom::blink::IDBDatabase> pending_database,
+    int connection_priority)
     : ActiveScriptWrappable<IDBDatabase>({}),
       ExecutionContextLifecycleObserver(context),
       database_remote_(context),
       connection_lifetime_(std::move(connection_lifetime)),
+      scheduling_priority_(connection_priority),
       callbacks_receiver_(this, context) {
   database_remote_.Bind(std::move(pending_database),
                         context->GetTaskRunner(TaskType::kDatabaseAccess));
   callbacks_receiver_.Bind(std::move(callbacks_receiver),
                            context->GetTaskRunner(TaskType::kDatabaseAccess));
+
+  // Invokes the callback immediately.
+  scheduler_observer_ = context->GetScheduler()->AddLifecycleObserver(
+      FrameOrWorkerScheduler::ObserverType::kWorkerScheduler,
+      WTF::BindRepeating(&IDBDatabase::OnSchedulerLifecycleStateChanged,
+                         WrapWeakPersistent(this)));
 }
 
 void IDBDatabase::Trace(Visitor* visitor) const {
@@ -726,4 +734,32 @@
   }
 }
 
+void IDBDatabase::OnSchedulerLifecycleStateChanged(
+    scheduler::SchedulingLifecycleState lifecycle_state) {
+  int new_priority = GetSchedulingPriority(lifecycle_state);
+  if (new_priority == scheduling_priority_) {
+    return;
+  }
+  if (database_remote_) {
+    database_remote_->UpdatePriority(scheduling_priority_);
+  }
+}
+
+// static
+int IDBDatabase::GetSchedulingPriority(
+    scheduler::SchedulingLifecycleState lifecycle_state) {
+  switch (lifecycle_state) {
+    case scheduler::SchedulingLifecycleState::kNotThrottled:
+      return 0;
+    case scheduler::SchedulingLifecycleState::kHidden:
+      return 1;
+    case scheduler::SchedulingLifecycleState::kThrottled:
+      return 2;
+    case scheduler::SchedulingLifecycleState::kStopped:
+      return 3;
+  }
+
+  return 0;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.h b/third_party/blink/renderer/modules/indexeddb/idb_database.h
index 86eeaba..a7b6f1d 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.h
@@ -68,8 +68,8 @@
       mojo::PendingAssociatedReceiver<mojom::blink::IDBDatabaseCallbacks>
           callbacks_receiver,
       mojo::PendingRemote<mojom::blink::ObservedFeature> connection_lifetime,
-      mojo::PendingAssociatedRemote<mojom::blink::IDBDatabase>
-          pending_database);
+      mojo::PendingAssociatedRemote<mojom::blink::IDBDatabase> pending_database,
+      int scheduling_priority);
 
   void Trace(Visitor*) const override;
 
@@ -251,6 +251,24 @@
 
   bool IsConnectionOpen() const;
 
+  // Converts a lifecycle state to a priority integer. Lower values represent
+  // higher priority.
+  //
+  // A note on the input type: the scheduling lifecycle state is used as an
+  // imperfect proxy for the general priority of the frame. Its primary
+  // advantage is that it is synchronously accessible during the flow of
+  // creating a transaction. In contrast, the concept of priority in the
+  // browser's `PerformanceManager` would require asynchronous lookup from IDB
+  // backend code (which runs on a threadpool), which would add latency to
+  // transaction processing. The scheduler's lifecycle state may be slightly out
+  // of date if there are in-flight IPC from the browser, but:
+  //
+  // * prioritization is somewhat heuristic anyway
+  // * nothing should break if the priority is occasionally misjudged.
+  // * `scheduler_observer_` should eventually pick up and forward updates.
+  static int GetSchedulingPriority(
+      scheduler::SchedulingLifecycleState lifecycle_state);
+
  protected:
   // EventTarget
   DispatchEventResult DispatchEventInternal(Event&) override;
@@ -262,6 +280,9 @@
                                     ExceptionState&);
   void CloseConnection();
 
+  void OnSchedulerLifecycleStateChanged(
+      scheduler::SchedulingLifecycleState lifecycle_state);
+
   IDBDatabaseMetadata metadata_;
   HeapMojoAssociatedRemote<mojom::blink::IDBDatabase> database_remote_;
   Member<IDBTransaction> version_change_transaction_;
@@ -272,6 +293,11 @@
 
   bool close_pending_ = false;
 
+  // See notes above `GetSchedulingPriority`.
+  int scheduling_priority_;
+  std::unique_ptr<FrameOrWorkerScheduler::LifecycleObserverHandle>
+      scheduler_observer_;
+
   HeapMojoAssociatedReceiver<mojom::blink::IDBDatabaseCallbacks, IDBDatabase>
       callbacks_receiver_;
 };
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
index fa584a833..90f3b30 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
@@ -325,9 +325,24 @@
     return;
   }
 
+  // Getting the scheduling priority as a one-off is somewhat awkward.
+  int scheduling_priority = -1;
+  std::unique_ptr<FrameOrWorkerScheduler::LifecycleObserverHandle> lifecycle =
+      GetExecutionContext()->GetScheduler()->AddLifecycleObserver(
+          FrameOrWorkerScheduler::ObserverType::kWorkerScheduler,
+          WTF::BindRepeating(
+              [](int* priority,
+                 scheduler::SchedulingLifecycleState lifecycle_state) {
+                *priority = IDBDatabase::GetSchedulingPriority(lifecycle_state);
+              },
+              WTF::Unretained(&scheduling_priority)));
+  DCHECK_GE(scheduling_priority, 0);
+  request->set_connection_priority(scheduling_priority);
+
   GetRemote()->Open(CreatePendingRemote(request->CreateFactoryClient()),
                     std::move(callbacks_remote), name, version,
-                    std::move(transaction_receiver), transaction_id);
+                    std::move(transaction_receiver), transaction_id,
+                    scheduling_priority);
 }
 
 IDBOpenDBRequest* IDBFactory::open(ScriptState* script_state,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.cc b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.cc
index fe0eda4c4..fa730e5 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.cc
@@ -125,7 +125,8 @@
 
   auto* idb_database = MakeGarbageCollected<IDBDatabase>(
       GetExecutionContext(), std::move(callbacks_receiver_),
-      std::move(connection_lifetime_), std::move(pending_database));
+      std::move(connection_lifetime_), std::move(pending_database),
+      connection_priority_);
   idb_database->SetMetadata(metadata);
 
   if (old_version == IDBDatabaseMetadata::kNoVersion) {
@@ -173,7 +174,8 @@
 
     idb_database = MakeGarbageCollected<IDBDatabase>(
         GetExecutionContext(), std::move(callbacks_receiver_),
-        std::move(connection_lifetime_), std::move(pending_database));
+        std::move(connection_lifetime_), std::move(pending_database),
+        connection_priority_);
     SetResult(MakeGarbageCollected<IDBAny>(idb_database));
   }
   idb_database->SetMetadata(metadata);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h
index 32d7548..8d802aa5 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h
@@ -85,6 +85,10 @@
   // EventTarget
   const AtomicString& InterfaceName() const override;
 
+  void set_connection_priority(int priority) {
+    connection_priority_ = priority;
+  }
+
   DEFINE_ATTRIBUTE_EVENT_LISTENER(blocked, kBlocked)
   DEFINE_ATTRIBUTE_EVENT_LISTENER(upgradeneeded, kUpgradeneeded)
 
@@ -107,6 +111,11 @@
   base::Time start_time_;
   bool open_time_recorded_ = false;
 
+  // The priority for this connection request which is passed to the backend.
+  // This should be passed along to the database after a successful open
+  // attempt.
+  int connection_priority_ = 0;
+
   // Pointer back to the IDBFactoryClient that holds a persistent reference
   // to this object.
   raw_ptr<IDBFactoryClient> factory_client_ = nullptr;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
index bda38c9..1ebf716 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
@@ -87,77 +87,7 @@
 
   void DatabaseDestroyed() { destroyed_ = true; }
 
-  void RenameObjectStore(int64_t transaction_id,
-                         int64_t object_store_id,
-                         const WTF::String& new_name) override {}
-  void CreateTransaction(mojo::PendingAssociatedReceiver<
-                             mojom::blink::IDBTransaction> transaction_receiver,
-                         int64_t transaction_id,
-                         const WTF::Vector<int64_t>& object_store_ids,
-                         mojom::blink::IDBTransactionMode mode,
-                         mojom::blink::IDBTransactionDurability) override {}
   MOCK_METHOD0(Close, void());
-  void VersionChangeIgnored() override {}
-  void Get(int64_t transaction_id,
-           int64_t object_store_id,
-           int64_t index_id,
-           mojom::blink::IDBKeyRangePtr key_range,
-           bool key_only,
-           mojom::blink::IDBDatabase::GetCallback callback) override {}
-  void GetAll(int64_t transaction_id,
-              int64_t object_store_id,
-              int64_t index_id,
-              mojom::blink::IDBKeyRangePtr key_range,
-              bool key_only,
-              int64_t max_count,
-              mojom::blink::IDBDatabase::GetAllCallback callback) override {}
-  void SetIndexKeys(int64_t transaction_id,
-                    int64_t object_store_id,
-                    std::unique_ptr<::blink::IDBKey> primary_key,
-                    WTF::Vector<::blink::IDBIndexKeys> index_keys) override {}
-  void SetIndexesReady(int64_t transaction_id,
-                       int64_t object_store_id,
-                       const WTF::Vector<int64_t>& index_ids) override {}
-  void OpenCursor(
-      int64_t transaction_id,
-      int64_t object_store_id,
-      int64_t index_id,
-      mojom::blink::IDBKeyRangePtr key_range,
-      mojom::blink::IDBCursorDirection direction,
-      bool key_only,
-      mojom::blink::IDBTaskType task_type,
-      mojom::blink::IDBDatabase::OpenCursorCallback callback) override {}
-  void Count(int64_t transaction_id,
-             int64_t object_store_id,
-             int64_t index_id,
-             mojom::blink::IDBKeyRangePtr key_range,
-             CountCallback callback) override {}
-  void DeleteRange(int64_t transaction_id,
-                   int64_t object_store_id,
-                   mojom::blink::IDBKeyRangePtr key_range,
-                   DeleteRangeCallback callback) override {}
-  void GetKeyGeneratorCurrentNumber(
-      int64_t transaction_id,
-      int64_t object_store_id,
-      GetKeyGeneratorCurrentNumberCallback callback) override {}
-  void Clear(int64_t transaction_id,
-             int64_t object_store_id,
-             ClearCallback callback) override {}
-  void CreateIndex(int64_t transaction_id,
-                   int64_t object_store_id,
-                   int64_t index_id,
-                   const WTF::String& name,
-                   const ::blink::IDBKeyPath& key_path,
-                   bool unique,
-                   bool multi_entry) override {}
-  void DeleteIndex(int64_t transaction_id,
-                   int64_t object_store_id,
-                   int64_t index_id) override {}
-  void RenameIndex(int64_t transaction_id,
-                   int64_t object_store_id,
-                   int64_t index_id,
-                   const WTF::String& new_name) override {}
-  void Abort(int64_t transaction_id) override {}
 
   bool destroyed() { return destroyed_; }
 
@@ -190,7 +120,7 @@
 
     db_ = MakeGarbageCollected<IDBDatabase>(
         execution_context, mojo::NullAssociatedReceiver(), mojo::NullRemote(),
-        mock_database.BindNewEndpointAndPassDedicatedRemote());
+        mock_database.BindNewEndpointAndPassDedicatedRemote(), /*priority=*/0);
 
     IDBTransaction::TransactionMojoRemote transaction_remote(execution_context);
     mojo::PendingAssociatedReceiver<mojom::blink::IDBTransaction> receiver =
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_transaction_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_transaction_test.cc
index 29ba5d3e..5f5f1b55 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_transaction_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_transaction_test.cc
@@ -87,7 +87,7 @@
 
     db_ = MakeGarbageCollected<IDBDatabase>(
         execution_context, mojo::NullAssociatedReceiver(), mojo::NullRemote(),
-        mock_database.BindNewEndpointAndPassDedicatedRemote());
+        mock_database.BindNewEndpointAndPassDedicatedRemote(), /*priority=*/0);
 
     IDBTransaction::TransactionMojoRemote transaction_remote(execution_context);
     mojo::PendingAssociatedReceiver<mojom::blink::IDBTransaction> receiver =
diff --git a/third_party/blink/renderer/modules/indexeddb/mock_idb_database.h b/third_party/blink/renderer/modules/indexeddb/mock_idb_database.h
index 137bc51..e4f239a 100644
--- a/third_party/blink/renderer/modules/indexeddb/mock_idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/mock_idb_database.h
@@ -129,6 +129,7 @@
               (int64_t transaction_id, int64_t object_store_id, ClearCallback),
               (override));
   MOCK_METHOD(void, DidBecomeInactive, (), (override));
+  MOCK_METHOD(void, UpdatePriority, (int new_priority), (override));
 
   // AbstrackMockIDBDatabase::OnDisconnect()
   MOCK_METHOD(void, OnDisconnect, (), (override));
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.cc
index d8429b0..d8a5b10 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.cc
@@ -60,7 +60,7 @@
 
   if (!input_params.IsValid()) {
     DVLOG(1) << "Invalid params: " << input_params.AsHumanReadableString();
-    NotifyError(current_status_);
+    NotifyError(media::EncoderStatus::Codes::kEncoderUnsupportedConfig);
     return;
   }
   input_params_ = input_params;
@@ -191,12 +191,12 @@
                            std::move(codec_desc), encoded_buffer.timestamp);
 }
 
-void AudioTrackMojoEncoder::NotifyError(const media::EncoderStatus& error) {
+void AudioTrackMojoEncoder::NotifyError(media::EncoderStatus error) {
   if (on_encoded_audio_error_cb_.is_null()) {
     return;
   }
 
-  std::move(on_encoded_audio_error_cb_).Run(error);
+  std::move(on_encoded_audio_error_cb_).Run(std::move(error));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.h b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.h
index e91e432a..ba41472 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.h
@@ -76,7 +76,7 @@
   void OnEncodeOutput(
       media::EncodedAudioBuffer encoded_buffer,
       std::optional<media::AudioEncoder::CodecDescription> codec_desc);
-  void NotifyError(const media::EncoderStatus& error);
+  void NotifyError(media::EncoderStatus error);
 
   // The `media::AudioEncoder` interface requires the callback provided to
   // `Initialize` to be run before any further calls are made. So, we store any
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder_unittest.cc
index da897cfe..4b7c5a2 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder_unittest.cc
@@ -286,7 +286,7 @@
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(error_code(),
-            media::EncoderStatus::Codes::kEncoderInitializeNeverCompleted);
+            media::EncoderStatus::Codes::kEncoderUnsupportedConfig);
 }
 
 TEST_F(AudioTrackMojoEncoderTest, EncoderInitializationError) {
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc
index e41fb99..29884178 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc
@@ -70,9 +70,11 @@
 
 AudioTrackOpusEncoder::AudioTrackOpusEncoder(
     OnEncodedAudioCB on_encoded_audio_cb,
+    OnEncodedAudioErrorCB on_encoded_audio_error_cb,
     uint32_t bits_per_second,
     bool vbr_enabled)
-    : AudioTrackEncoder(std::move(on_encoded_audio_cb)),
+    : AudioTrackEncoder(std::move(on_encoded_audio_cb),
+                        std::move(on_encoded_audio_error_cb)),
       bits_per_second_(bits_per_second),
       vbr_enabled_(vbr_enabled),
       opus_encoder_(nullptr) {}
@@ -99,6 +101,7 @@
 
   if (!input_params.IsValid()) {
     DLOG(ERROR) << "Invalid params: " << input_params.AsHumanReadableString();
+    NotifyError(media::EncoderStatus::Codes::kEncoderUnsupportedConfig);
     return;
   }
   input_params_ = input_params;
@@ -137,6 +140,7 @@
     DLOG(ERROR) << "Couldn't init Opus encoder: " << opus_strerror(opus_result)
                 << ", sample rate: " << converted_params_.sample_rate()
                 << ", channels: " << converted_params_.channels();
+    NotifyError(media::EncoderStatus::Codes::kEncoderInitializationError);
     return;
   }
 
@@ -151,6 +155,7 @@
   if (opus_encoder_ctl(opus_encoder_.get(), OPUS_SET_BITRATE(bitrate)) !=
       OPUS_OK) {
     DLOG(ERROR) << "Failed to set Opus bitrate: " << bitrate;
+    NotifyError(media::EncoderStatus::Codes::kEncoderUnsupportedConfig);
     return;
   }
 
@@ -158,6 +163,7 @@
   if (opus_encoder_ctl(opus_encoder_.get(), OPUS_SET_VBR(vbr_enabled)) !=
       OPUS_OK) {
     DLOG(ERROR) << "Failed to set Opus VBR mode: " << vbr_enabled;
+    NotifyError(media::EncoderStatus::Codes::kEncoderUnsupportedConfig);
     return;
   }
 }
@@ -197,6 +203,10 @@
                              input_bus->frames(), input_params_.sample_rate());
       on_encoded_audio_cb_.Run(converted_params_, std::move(encoded_data),
                                std::nullopt, capture_time_of_first_sample);
+    } else {
+      // Opus encoder keeps running even if it fails to encode a frame, which
+      // is different behavior from the AAC encoder.
+      NotifyError(media::EncoderStatus::Codes::kEncoderFailedEncode);
     }
   }
 }
@@ -210,4 +220,11 @@
   }
 }
 
+void AudioTrackOpusEncoder::NotifyError(media::EncoderStatus error) {
+  if (on_encoded_audio_error_cb_.is_null()) {
+    return;
+  }
+
+  std::move(on_encoded_audio_error_cb_).Run(std::move(error));
+}
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h
index 63e5eb04..45767dc 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h
@@ -24,6 +24,7 @@
                               public media::AudioConverter::InputCallback {
  public:
   AudioTrackOpusEncoder(OnEncodedAudioCB on_encoded_audio_cb,
+                        OnEncodedAudioErrorCB on_encoded_audio_error_cb,
                         uint32_t bits_per_second,
                         bool vbr_enabled = true);
   ~AudioTrackOpusEncoder() override;
@@ -45,6 +46,8 @@
                       uint32_t frames_delayed,
                       const media::AudioGlitchInfo& glitch_info) override;
 
+  void NotifyError(media::EncoderStatus error);
+
   // Target bitrate for Opus. If 0, Opus provide automatic bitrate is used.
   const uint32_t bits_per_second_;
 
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc
index d105e12..445c623 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc
@@ -12,8 +12,11 @@
 
 namespace blink {
 
-AudioTrackPcmEncoder::AudioTrackPcmEncoder(OnEncodedAudioCB on_encoded_audio_cb)
-    : AudioTrackEncoder(std::move(on_encoded_audio_cb)) {}
+AudioTrackPcmEncoder::AudioTrackPcmEncoder(
+    OnEncodedAudioCB on_encoded_audio_cb,
+    OnEncodedAudioErrorCB on_encoded_audio_error_cb)
+    : AudioTrackEncoder(std::move(on_encoded_audio_cb),
+                        std::move(on_encoded_audio_error_cb)) {}
 
 void AudioTrackPcmEncoder::OnSetFormat(
     const media::AudioParameters& input_params) {
@@ -22,8 +25,13 @@
 
   if (!input_params.IsValid()) {
     DLOG(ERROR) << "Invalid params: " << input_params.AsHumanReadableString();
+    if (!on_encoded_audio_error_cb_.is_null()) {
+      std::move(on_encoded_audio_error_cb_)
+          .Run(media::EncoderStatus::Codes::kEncoderUnsupportedConfig);
+    }
     return;
   }
+
   input_params_ = input_params;
 }
 
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h
index 2402818..0f25353 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h
@@ -17,7 +17,9 @@
 // back out again.
 class AudioTrackPcmEncoder : public AudioTrackEncoder {
  public:
-  explicit AudioTrackPcmEncoder(OnEncodedAudioCB on_encoded_audio_cb);
+  explicit AudioTrackPcmEncoder(
+      OnEncodedAudioCB on_encoded_audio_cb,
+      OnEncodedAudioErrorCB on_encoded_audio_error_cb);
 
   AudioTrackPcmEncoder(const AudioTrackPcmEncoder&) = delete;
   AudioTrackPcmEncoder& operator=(const AudioTrackPcmEncoder&) = delete;
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
index 81d934d7..49232f5f 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
@@ -115,7 +115,7 @@
   switch (codec) {
     case CodecId::kPcm:
       return std::make_unique<AudioTrackPcmEncoder>(
-          std::move(on_encoded_audio_cb));
+          std::move(on_encoded_audio_cb), std::move(on_encoded_audio_error_cb));
     case CodecId::kAac:
 #if HAS_AAC_ENCODER
       return std::make_unique<AudioTrackMojoEncoder>(
@@ -127,8 +127,8 @@
     case CodecId::kOpus:
     default:
       return std::make_unique<AudioTrackOpusEncoder>(
-          std::move(on_encoded_audio_cb), bits_per_second,
-          bitrate_mode == BitrateMode::kVariable);
+          std::move(on_encoded_audio_cb), std::move(on_encoded_audio_error_cb),
+          bits_per_second, bitrate_mode == BitrateMode::kVariable);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/mediastream/web_media_player_ms.cc b/third_party/blink/renderer/modules/mediastream/web_media_player_ms.cc
index 64aed7e..3549961 100644
--- a/third_party/blink/renderer/modules/mediastream/web_media_player_ms.cc
+++ b/third_party/blink/renderer/modules/mediastream/web_media_player_ms.cc
@@ -25,6 +25,7 @@
 #include "cc/layers/video_layer.h"
 #include "media/base/media_content_type.h"
 #include "media/base/media_log.h"
+#include "media/base/media_track.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_transformation.h"
 #include "media/base/video_types.h"
@@ -547,11 +548,10 @@
       // is enabled by default to match blink logic.
       bool is_first_audio_track = true;
       for (auto component : audio_components) {
-        client_->AddAudioTrack(
-            blink::WebString::FromUTF8(component->Id().Utf8()),
-            blink::WebMediaPlayerClient::kAudioTrackKindMain,
-            blink::WebString::FromUTF8(component->GetSourceName().Utf8()),
-            /*language=*/"", is_first_audio_track);
+        client_->AddMediaTrack(media::MediaTrack::CreateAudioTrack(
+            component->Id().Utf8(), media::MediaTrack::AudioKind::kMain,
+            component->GetSourceName().Utf8(), /*language=*/"",
+            is_first_audio_track));
         is_first_audio_track = false;
       }
     }
@@ -572,11 +572,10 @@
       // is enabled by default to match blink logic.
       bool is_first_video_track = true;
       for (auto component : video_components) {
-        client_->AddVideoTrack(
-            blink::WebString::FromUTF8(component->Id().Utf8()),
-            blink::WebMediaPlayerClient::kVideoTrackKindMain,
-            blink::WebString::FromUTF8(component->GetSourceName().Utf8()),
-            /*language=*/"", is_first_video_track);
+        client_->AddMediaTrack(media::MediaTrack::CreateVideoTrack(
+            component->Id().Utf8(), media::MediaTrack::VideoKind::kMain,
+            component->GetSourceName().Utf8(), /*language=*/"",
+            is_first_video_track));
         is_first_video_track = false;
       }
     }
diff --git a/third_party/blink/renderer/modules/mediastream/web_media_player_ms_test.cc b/third_party/blink/renderer/modules/mediastream/web_media_player_ms_test.cc
index c8548b9..bbe97c0 100644
--- a/third_party/blink/renderer/modules/mediastream/web_media_player_ms_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/web_media_player_ms_test.cc
@@ -561,22 +561,10 @@
   void SizeChanged() override;
   void SetCcLayer(cc::Layer* layer) override;
   void OnFirstFrame(base::TimeTicks, size_t) override {}
-  WebMediaPlayer::TrackId AddAudioTrack(const WebString& id,
-                                        AudioTrackKind,
-                                        const WebString& label,
-                                        const WebString& language,
-                                        bool enabled) override {
-    return WebMediaPlayer::TrackId();
-  }
-  void RemoveAudioTrack(WebMediaPlayer::TrackId) override {}
-  WebMediaPlayer::TrackId AddVideoTrack(const WebString& id,
-                                        VideoTrackKind,
-                                        const WebString& label,
-                                        const WebString& language,
-                                        bool selected) override {
-    return WebMediaPlayer::TrackId();
-  }
-  void RemoveVideoTrack(WebMediaPlayer::TrackId) override {}
+
+  void RemoveMediaTrack(const media::MediaTrack&) override {}
+  void AddMediaTrack(const media::MediaTrack& track) override {}
+
   void MediaSourceOpened(std::unique_ptr<WebMediaSource>) override {}
   void RemotePlaybackCompatibilityChanged(const WebURL& url,
                                           bool is_compatible) override {}
diff --git a/third_party/blink/renderer/modules/ml/ml_context.cc b/third_party/blink/renderer/modules/ml/ml_context.cc
index bc32986..3b55b72 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.cc
+++ b/third_party/blink/renderer/modules/ml/ml_context.cc
@@ -20,15 +20,22 @@
 #include "third_party/blink/public/platform/task_type.h"
 #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/modules/v8/v8_ml_batch_normalization_support_limits.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_binary_support_limits.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_buffer_descriptor.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_concat_support_limits.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_context_lost_info.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_context_options.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ml_conv_2d_support_limits.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_device_type.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_gather_support_limits.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_gemm_support_limits.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ml_gru_cell_support_limits.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ml_gru_support_limits.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_logical_not_support_limits.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ml_lstm_cell_support_limits.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ml_lstm_support_limits.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ml_normalization_support_limits.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_op_support_limits.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_operand_data_type.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_power_preference.h"
@@ -239,6 +246,22 @@
       SupportedDataTypesToSupportLimits(data_type_limits.arg_min_max_output));
   op_support_limits->setArgMax(argmax);
 
+  MLBatchNormalizationSupportLimits* batch_normalization =
+      MLBatchNormalizationSupportLimits::Create();
+  batch_normalization->setInput(SupportedDataTypesToSupportLimits(
+      data_type_limits.batch_normalization_input));
+  batch_normalization->setMean(SupportedDataTypesToSupportLimits(
+      data_type_limits.batch_normalization_input));
+  batch_normalization->setVariance(SupportedDataTypesToSupportLimits(
+      data_type_limits.batch_normalization_input));
+  batch_normalization->setScale(SupportedDataTypesToSupportLimits(
+      data_type_limits.batch_normalization_input));
+  batch_normalization->setBias(SupportedDataTypesToSupportLimits(
+      data_type_limits.batch_normalization_input));
+  batch_normalization->setOutput(SupportedDataTypesToSupportLimits(
+      data_type_limits.batch_normalization_input));
+  op_support_limits->setBatchNormalization(batch_normalization);
+
   MLSingleInputSupportLimits* cast = MLSingleInputSupportLimits::Create();
   cast->setInput(
       SupportedDataTypesToSupportLimits(data_type_limits.cast_input));
@@ -260,6 +283,28 @@
       SupportedDataTypesToSupportLimits(data_type_limits.concat_inputs));
   op_support_limits->setConcat(concat);
 
+  MLConv2dSupportLimits* conv2d = MLConv2dSupportLimits::Create();
+  conv2d->setInput(
+      SupportedDataTypesToSupportLimits(data_type_limits.conv2d_input));
+  conv2d->setFilter(
+      SupportedDataTypesToSupportLimits(data_type_limits.conv2d_input));
+  conv2d->setBias(
+      SupportedDataTypesToSupportLimits(data_type_limits.conv2d_input));
+  conv2d->setOutput(
+      SupportedDataTypesToSupportLimits(data_type_limits.conv2d_input));
+  op_support_limits->setConv2d(conv2d);
+
+  MLConv2dSupportLimits* conv_transpose2d = MLConv2dSupportLimits::Create();
+  conv_transpose2d->setInput(SupportedDataTypesToSupportLimits(
+      data_type_limits.conv_transpose2d_input));
+  conv_transpose2d->setFilter(SupportedDataTypesToSupportLimits(
+      data_type_limits.conv_transpose2d_input));
+  conv_transpose2d->setBias(SupportedDataTypesToSupportLimits(
+      data_type_limits.conv_transpose2d_input));
+  conv_transpose2d->setOutput(SupportedDataTypesToSupportLimits(
+      data_type_limits.conv_transpose2d_input));
+  op_support_limits->setConvTranspose2d(conv_transpose2d);
+
   // Element-wise binary ops.
   MLBinarySupportLimits* add = MLBinarySupportLimits::Create();
   add->setA(SupportedDataTypesToSupportLimits(data_type_limits.add_input));
@@ -458,6 +503,40 @@
       SupportedDataTypesToSupportLimits(data_type_limits.gemm_input));
   op_support_limits->setGemm(gemm);
 
+  MLGruSupportLimits* gru = MLGruSupportLimits::Create();
+  gru->setInput(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_input));
+  gru->setWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_input));
+  gru->setRecurrentWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_input));
+  gru->setBias(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_input));
+  gru->setRecurrentBias(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_input));
+  gru->setInitialHiddenState(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_input));
+  gru->setOutputs(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_input));
+  op_support_limits->setGru(gru);
+
+  MLGruCellSupportLimits* gru_cell = MLGruCellSupportLimits::Create();
+  gru_cell->setInput(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_cell_input));
+  gru_cell->setWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_cell_input));
+  gru_cell->setRecurrentWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_cell_input));
+  gru_cell->setHiddenState(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_cell_input));
+  gru_cell->setBias(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_cell_input));
+  gru_cell->setRecurrentBias(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_cell_input));
+  gru_cell->setOutput(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.gru_cell_input));
+  op_support_limits->setGruCell(gru_cell);
+
   MLSingleInputSupportLimits* hard_sigmoid =
       MLSingleInputSupportLimits::Create();
   hard_sigmoid->setInput(SupportedDataTypesToSupportLimits(
@@ -473,6 +552,30 @@
       properties_.data_type_limits.hard_swish_input));
   op_support_limits->setHardSwish(hard_swish);
 
+  MLNormalizationSupportLimits* instance_normalization =
+      MLNormalizationSupportLimits::Create();
+  instance_normalization->setInput(SupportedDataTypesToSupportLimits(
+      data_type_limits.instance_normalization_input));
+  instance_normalization->setScale(SupportedDataTypesToSupportLimits(
+      data_type_limits.instance_normalization_input));
+  instance_normalization->setBias(SupportedDataTypesToSupportLimits(
+      data_type_limits.instance_normalization_input));
+  instance_normalization->setOutput(SupportedDataTypesToSupportLimits(
+      data_type_limits.instance_normalization_input));
+  op_support_limits->setInstanceNormalization(instance_normalization);
+
+  MLNormalizationSupportLimits* layer_normalization =
+      MLNormalizationSupportLimits::Create();
+  layer_normalization->setInput(SupportedDataTypesToSupportLimits(
+      data_type_limits.layer_normalization_input));
+  layer_normalization->setScale(SupportedDataTypesToSupportLimits(
+      data_type_limits.layer_normalization_input));
+  layer_normalization->setBias(SupportedDataTypesToSupportLimits(
+      data_type_limits.layer_normalization_input));
+  layer_normalization->setOutput(SupportedDataTypesToSupportLimits(
+      data_type_limits.layer_normalization_input));
+  op_support_limits->setLayerNormalization(layer_normalization);
+
   MLSingleInputSupportLimits* leaky_relu = MLSingleInputSupportLimits::Create();
   leaky_relu->setInput(
       SupportedDataTypesToSupportLimits(data_type_limits.leaky_relu_input));
@@ -487,6 +590,48 @@
       SupportedDataTypesToSupportLimits(data_type_limits.linear_input));
   op_support_limits->setLinear(linear);
 
+  MLLstmSupportLimits* lstm = MLLstmSupportLimits::Create();
+  lstm->setInput(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_input));
+  lstm->setWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_input));
+  lstm->setRecurrentWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_input));
+  lstm->setBias(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_input));
+  lstm->setRecurrentBias(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_input));
+  lstm->setPeepholeWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_input));
+  lstm->setInitialHiddenState(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_input));
+  lstm->setInitialCellState(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_input));
+  lstm->setOutputs(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_input));
+  op_support_limits->setLstm(lstm);
+
+  MLLstmCellSupportLimits* lstm_cell = MLLstmCellSupportLimits::Create();
+  lstm_cell->setInput(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_cell_input));
+  lstm_cell->setWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_cell_input));
+  lstm_cell->setRecurrentWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_cell_input));
+  lstm_cell->setHiddenState(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_cell_input));
+  lstm_cell->setCellState(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_cell_input));
+  lstm_cell->setBias(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_cell_input));
+  lstm_cell->setRecurrentBias(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_cell_input));
+  lstm_cell->setPeepholeWeight(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_cell_input));
+  lstm_cell->setOutputs(SupportedDataTypesToSupportLimits(
+      properties_.data_type_limits.lstm_cell_input));
+  op_support_limits->setLstmCell(lstm_cell);
+
   MLBinarySupportLimits* matmul = MLBinarySupportLimits::Create();
   matmul->setA(
       SupportedDataTypesToSupportLimits(data_type_limits.matmul_input));
diff --git a/third_party/blink/renderer/modules/ml/ml_context.idl b/third_party/blink/renderer/modules/ml/ml_context.idl
index 24801d1d4..6468489 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.idl
+++ b/third_party/blink/renderer/modules/ml/ml_context.idl
@@ -33,11 +33,27 @@
   MLSupportLimits output;
 };
 
+dictionary MLBatchNormalizationSupportLimits {
+  MLSupportLimits input;
+  MLSupportLimits mean;
+  MLSupportLimits variance;
+  MLSupportLimits scale;
+  MLSupportLimits bias;
+  MLSupportLimits output;
+};
+
 dictionary MLConcatSupportLimits {
   MLSupportLimits inputs;
   MLSupportLimits output;
 };
 
+dictionary MLConv2dSupportLimits {
+  MLSupportLimits input;
+  MLSupportLimits filter;
+  MLSupportLimits bias;
+  MLSupportLimits output;
+};
+
 dictionary MLGatherSupportLimits {
   MLSupportLimits input;
   MLSupportLimits indices;
@@ -51,6 +67,57 @@
   MLSupportLimits output;
 };
 
+dictionary MLGruSupportLimits {
+  MLSupportLimits input;
+  MLSupportLimits weight;
+  MLSupportLimits recurrentWeight;
+  MLSupportLimits bias;
+  MLSupportLimits recurrentBias;
+  MLSupportLimits initialHiddenState;
+  MLSupportLimits outputs;
+};
+
+dictionary MLGruCellSupportLimits {
+  MLSupportLimits input;
+  MLSupportLimits weight;
+  MLSupportLimits recurrentWeight;
+  MLSupportLimits hiddenState;
+  MLSupportLimits bias;
+  MLSupportLimits recurrentBias;
+  MLSupportLimits output;
+};
+
+dictionary MLLstmSupportLimits {
+  MLSupportLimits input;
+  MLSupportLimits weight;
+  MLSupportLimits recurrentWeight;
+  MLSupportLimits bias;
+  MLSupportLimits recurrentBias;
+  MLSupportLimits peepholeWeight;
+  MLSupportLimits initialHiddenState;
+  MLSupportLimits initialCellState;
+  MLSupportLimits outputs;
+};
+
+dictionary MLLstmCellSupportLimits {
+  MLSupportLimits input;
+  MLSupportLimits weight;
+  MLSupportLimits recurrentWeight;
+  MLSupportLimits hiddenState;
+  MLSupportLimits cellState;
+  MLSupportLimits bias;
+  MLSupportLimits recurrentBias;
+  MLSupportLimits peepholeWeight;
+  MLSupportLimits outputs;
+};
+
+dictionary MLNormalizationSupportLimits {
+  MLSupportLimits input;
+  MLSupportLimits scale;
+  MLSupportLimits bias;
+  MLSupportLimits output;
+};
+
 dictionary MLPreluSupportLimits {
   MLSupportLimits input;
   MLSupportLimits slope;
@@ -82,9 +149,12 @@
 
   MLSingleInputSupportLimits argMin;
   MLSingleInputSupportLimits argMax;
+  MLBatchNormalizationSupportLimits batchNormalization;
   MLSingleInputSupportLimits cast;
   MLSingleInputSupportLimits clamp;
   MLConcatSupportLimits concat;
+  MLConv2dSupportLimits conv2d;
+  MLConv2dSupportLimits convTranspose2d;
 
   // Element-wise binary ops.
   MLBinarySupportLimits add;
@@ -125,10 +195,16 @@
   MLGatherSupportLimits gatherElements;
   MLSingleInputSupportLimits gelu;
   MLGemmSupportLimits gemm;
+  MLGruSupportLimits gru;
+  MLGruCellSupportLimits gruCell;
   MLSingleInputSupportLimits hardSigmoid;
   MLSingleInputSupportLimits hardSwish;
+  MLNormalizationSupportLimits instanceNormalization;
+  MLNormalizationSupportLimits layerNormalization;
   MLSingleInputSupportLimits leakyRelu;
   MLSingleInputSupportLimits linear;
+  MLLstmSupportLimits lstm;
+  MLLstmCellSupportLimits lstmCell;
   MLBinarySupportLimits matmul;
   MLSingleInputSupportLimits pad;
   MLPreluSupportLimits prelu;
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
index 58df546..2393b91 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
@@ -1062,7 +1062,8 @@
   ASSIGN_OR_THROW_AND_RETURN_IF_ERROR(
       webnn::OperandDescriptor output_descriptor,
       webnn::ValidateBatchNormalizationAndInferOutput(
-          input->Descriptor(), mean->Descriptor(), variance->Descriptor(),
+          ml_context_->GetProperties(), input->Descriptor(), mean->Descriptor(),
+          variance->Descriptor(),
           ConvertToBatchNormalizationAttributes(options)));
 
   // Create batchNormalization operator and its output operand. Connect the
@@ -1145,8 +1146,8 @@
   ASSIGN_OR_THROW_AND_RETURN_IF_ERROR(
       webnn::OperandDescriptor output_descriptor,
       webnn::ValidateConv2dAndInferOutput(
-          input->Descriptor(), filter->Descriptor(),
-          std::move(conv2d_attributes.value())));
+          ml_context_->GetProperties(), input->Descriptor(),
+          filter->Descriptor(), std::move(conv2d_attributes.value())));
 
   // Create conv2d operator and its output operand. Connect the conv2d operator
   // to its input and output operands.
@@ -1181,8 +1182,8 @@
   ASSIGN_OR_THROW_AND_RETURN_IF_ERROR(
       webnn::OperandDescriptor output_descriptor,
       webnn::ValidateConvTranspose2dAndInferOutput(
-          input->Descriptor(), filter->Descriptor(),
-          std::move(convTranspose2d_attributes.value())));
+          ml_context_->GetProperties(), input->Descriptor(),
+          filter->Descriptor(), std::move(convTranspose2d_attributes.value())));
 
   // Create convTranspose2d operator and its output operand. Connect the
   // convTranspose2d operator to its input and output operands.
@@ -1531,8 +1532,9 @@
                                  HeapVector<Member<const MLOperand>>());
 
   auto validated_outputs = webnn::ValidateGruAndInferOutput(
-      input->Descriptor(), weight->Descriptor(), recurrent_weight->Descriptor(),
-      steps, hidden_size, ConvertToGruAttributes(this, options));
+      ml_context_->GetProperties(), input->Descriptor(), weight->Descriptor(),
+      recurrent_weight->Descriptor(), steps, hidden_size,
+      ConvertToGruAttributes(this, options));
   if (!validated_outputs.has_value()) {
     exception_state.ThrowTypeError(String::FromUTF8(validated_outputs.error()));
     return {};
@@ -1569,8 +1571,8 @@
   THROW_AND_RETURN_TYPE_IF_ERROR(ValidateInputs(inputs), nullptr);
 
   auto validated_output = webnn::ValidateGruCellAndInferOutput(
-      input->Descriptor(), weight->Descriptor(), recurrent_weight->Descriptor(),
-      hidden_state->Descriptor(), hidden_size,
+      ml_context_->GetProperties(), input->Descriptor(), weight->Descriptor(),
+      recurrent_weight->Descriptor(), hidden_state->Descriptor(), hidden_size,
       ConvertToGruCellAttributes(this, options));
   if (!validated_output.has_value()) {
     exception_state.ThrowTypeError(String::FromUTF8(validated_output.error()));
@@ -1637,7 +1639,7 @@
   ASSIGN_OR_THROW_AND_RETURN_IF_ERROR(
       webnn::OperandDescriptor output_descriptor,
       webnn::ValidateInstanceNormalizationAndInferOutput(
-          input->Descriptor(),
+          ml_context_->GetProperties(), input->Descriptor(),
           ConvertToInstanceNormalizationAttributes(options)));
 
   auto* instance_normalization = MakeGarbageCollected<MLOperator>(
@@ -1677,7 +1679,7 @@
   ASSIGN_OR_THROW_AND_RETURN_IF_ERROR(
       webnn::OperandDescriptor output_descriptor,
       webnn::ValidateLayerNormalizationAndInferOutput(
-          input->Descriptor(), axes,
+          ml_context_->GetProperties(), input->Descriptor(), axes,
           ConvertToLayerNormalizationAttributes(options)));
 
   auto* layer_normalization = MakeGarbageCollected<MLOperator>(
@@ -1767,8 +1769,9 @@
   }
 
   auto validated_outputs = webnn::ValidateLstmAndInferOutput(
-      input->Descriptor(), weight->Descriptor(), recurrent_weight->Descriptor(),
-      steps, hidden_size, ConvertToLstmAttributes(options));
+      ml_context_->GetProperties(), input->Descriptor(), weight->Descriptor(),
+      recurrent_weight->Descriptor(), steps, hidden_size,
+      ConvertToLstmAttributes(options));
   if (!validated_outputs.has_value()) {
     exception_state.ThrowTypeError(String::FromUTF8(validated_outputs.error()));
     return {};
@@ -1824,8 +1827,9 @@
   }
 
   auto validated_outputs = webnn::ValidateLstmCellAndInferOutput(
-      input->Descriptor(), weight->Descriptor(), recurrent_weight->Descriptor(),
-      hidden_state->Descriptor(), cell_state->Descriptor(), hidden_size,
+      ml_context_->GetProperties(), input->Descriptor(), weight->Descriptor(),
+      recurrent_weight->Descriptor(), hidden_state->Descriptor(),
+      cell_state->Descriptor(), hidden_size,
       ConvertToLstmCellAttributes(options));
   if (!validated_outputs.has_value()) {
     exception_state.ThrowTypeError(String::FromUTF8(validated_outputs.error()));
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc
index accd4b0..859730e 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc
@@ -605,10 +605,13 @@
          webnn::SupportedDataTypes::All(),
          /*arg_min_max_output=*/
          webnn::SupportedDataTypes::All(),
+         /*batch_normalization_input=*/webnn::SupportedDataTypes::All(),
          /*cast_input=*/webnn::SupportedDataTypes::All(),
          /*clamp_input=*/webnn::SupportedDataTypes::All(),
          /*concat_inputs=*/
          webnn::SupportedDataTypes::All(),
+         /*conv2d_input=*/webnn::SupportedDataTypes::All(),
+         /*conv_transpose2d_input=*/webnn::SupportedDataTypes::All(),
          /*add_input=*/webnn::SupportedDataTypes::All(),
          /*sub_input=*/webnn::SupportedDataTypes::All(),
          /*mul_input=*/webnn::SupportedDataTypes::All(),
@@ -647,10 +650,16 @@
          webnn::SupportedDataTypes::All(),
          /*gelu_input=*/webnn::SupportedDataTypes::All(),
          /*gemm_input=*/webnn::SupportedDataTypes::All(),
+         /*gru_input=*/webnn::SupportedDataTypes::All(),
+         /*gru_cell_input=*/webnn::SupportedDataTypes::All(),
          /*hard_sigmoid_input=*/webnn::SupportedDataTypes::All(),
          /*hard_swish_input=*/webnn::SupportedDataTypes::All(),
+         /*instance_normalization_input=*/webnn::SupportedDataTypes::All(),
+         /*layer_normalization_input=*/webnn::SupportedDataTypes::All(),
          /*leaky_relu_input=*/webnn::SupportedDataTypes::All(),
          /*linear_input=*/webnn::SupportedDataTypes::All(),
+         /*lstm_input=*/webnn::SupportedDataTypes::All(),
+         /*lstm_cell_input=*/webnn::SupportedDataTypes::All(),
          /*matmul_input=*/webnn::SupportedDataTypes::All(),
          /*pad_input=*/webnn::SupportedDataTypes::All(),
          /*average_pool2d_input=*/webnn::SupportedDataTypes::All(),
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.idl b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.idl
index 87bbbd57..cbf89ceb 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.idl
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.idl
@@ -393,118 +393,53 @@
     // called with only two arguments, it goes to the WebGL1 signatures; if it's
     // called with three or four arguments, it goes to the WebGL2 specific
     // signatures.
-    [NoAllocDirectCall] void uniform1fv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan, BufferSourceTypeNoSizeLimit] Float32Array v,
+    [NoAllocDirectCall] void uniform1fv(WebGLUniformLocation? location, Float32List v,
                     GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform1fv(WebGLUniformLocation? location, sequence<GLfloat> v,
+    [NoAllocDirectCall] void uniform2fv(WebGLUniformLocation? location, Float32List v,
                     GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform2fv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan, BufferSourceTypeNoSizeLimit] Float32Array v,
+    [NoAllocDirectCall] void uniform3fv(WebGLUniformLocation? location, Float32List v,
                     GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform2fv(WebGLUniformLocation? location, sequence<GLfloat> v,
+    [NoAllocDirectCall] void uniform4fv(WebGLUniformLocation? location, Float32List v,
                     GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform3fv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan, BufferSourceTypeNoSizeLimit] Float32Array v,
+    [NoAllocDirectCall] void uniform1iv(WebGLUniformLocation? location, Int32List v,
                     GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform3fv(WebGLUniformLocation? location, sequence<GLfloat> v,
+    [NoAllocDirectCall] void uniform2iv(WebGLUniformLocation? location, Int32List v,
                     GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform4fv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan, BufferSourceTypeNoSizeLimit] Float32Array v,
+    [NoAllocDirectCall] void uniform3iv(WebGLUniformLocation? location, Int32List v,
                     GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform4fv(WebGLUniformLocation? location, sequence<GLfloat> v,
+    [NoAllocDirectCall] void uniform4iv(WebGLUniformLocation? location, Int32List v,
                     GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform1iv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan, BufferSourceTypeNoSizeLimit] Int32Array v,
-                    GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform1iv(WebGLUniformLocation? location, sequence<GLint> v,
-                    GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform2iv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan] Int32Array v,
-                    GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform2iv(WebGLUniformLocation? location, sequence<GLint> v,
-                    GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform3iv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan] Int32Array v,
-                    GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform3iv(WebGLUniformLocation? location, sequence<GLint> v,
-                    GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform4iv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan] Int32Array v,
-                    GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform4iv(WebGLUniformLocation? location, sequence<GLint> v,
-                    GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform1uiv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan] Uint32Array v,
+    [NoAllocDirectCall] void uniform1uiv(WebGLUniformLocation? location, Uint32List v,
                      optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform1uiv(WebGLUniformLocation? location, sequence<GLuint> v,
+    [NoAllocDirectCall] void uniform2uiv(WebGLUniformLocation? location, Uint32List v,
                      optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform2uiv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan] Uint32Array v,
+    [NoAllocDirectCall] void uniform3uiv(WebGLUniformLocation? location, Uint32List v,
                      optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform2uiv(WebGLUniformLocation? location, sequence<GLuint> v,
+    [NoAllocDirectCall] void uniform4uiv(WebGLUniformLocation? location, Uint32List v,
                      optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform3uiv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan] Uint32Array v,
-                     optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform3uiv(WebGLUniformLocation? location, sequence<GLuint> v,
-                     optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform4uiv(WebGLUniformLocation? location,
-                    [AllowShared, PassAsSpan] Uint32Array v,
-                     optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniform4uiv(WebGLUniformLocation? location, sequence<GLuint> v,
-                     optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose,
-                          [AllowShared, PassAsSpan] Float32Array array,
+    [NoAllocDirectCall] void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List array,
                           GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> array,
+    [NoAllocDirectCall] void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List array,
                           GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose,
-                          [AllowShared, PassAsSpan] Float32Array array,
+    [NoAllocDirectCall] void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List array,
                           GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> array,
-                          GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose,
-                          [AllowShared, PassAsSpan] Float32Array array,
-                          GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> array,
-                          GLuint srcOffset, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix2x3fv(WebGLUniformLocation? location, GLboolean transpose,
-                            [AllowShared, PassAsSpan] Float32Array value,
+    [NoAllocDirectCall] void uniformMatrix2x3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value,
                             optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix2x3fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value,
+    [NoAllocDirectCall] void uniformMatrix3x2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value,
                             optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix3x2fv(WebGLUniformLocation? location, GLboolean transpose,
-                            [AllowShared, PassAsSpan] Float32Array value,
+    [NoAllocDirectCall] void uniformMatrix2x4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value,
                             optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix3x2fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value,
+    [NoAllocDirectCall] void uniformMatrix4x2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value,
                             optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix2x4fv(WebGLUniformLocation? location, GLboolean transpose,
-                            [AllowShared, PassAsSpan] Float32Array value,
+    [NoAllocDirectCall] void uniformMatrix3x4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value,
                             optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix2x4fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value,
-                            optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix4x2fv(WebGLUniformLocation? location, GLboolean transpose,
-                            [AllowShared, PassAsSpan] Float32Array value,
-                            optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix4x2fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value,
-                            optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix3x4fv(WebGLUniformLocation? location, GLboolean transpose,
-                            [AllowShared, PassAsSpan] Float32Array value,
-                            optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix3x4fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value,
-                            optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix4x3fv(WebGLUniformLocation? location, GLboolean transpose,
-                            [AllowShared, PassAsSpan] Float32Array value,
-                            optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
-    [NoAllocDirectCall] void uniformMatrix4x3fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value,
+    [NoAllocDirectCall] void uniformMatrix4x3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value,
                             optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
 
     void vertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w);
-    [NoAllocDirectCall] void vertexAttribI4iv(GLuint index, [AllowShared, PassAsSpan] Int32Array v);
-    [NoAllocDirectCall] void vertexAttribI4iv(GLuint index, sequence<GLint> v);
+    [NoAllocDirectCall] void vertexAttribI4iv(GLuint index, Int32List v);
     void vertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
-    [NoAllocDirectCall] void vertexAttribI4uiv(GLuint index, [AllowShared, PassAsSpan] Uint32Array v);
-    [NoAllocDirectCall] void vertexAttribI4uiv(GLuint index, sequence<GLuint> v);
+    [NoAllocDirectCall] void vertexAttribI4uiv(GLuint index, Uint32List v);
     void vertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset);
 
     /* Writing to the drawing buffer */
@@ -515,15 +450,10 @@
 
     /* Multiple Render Targets */
     [NoAllocDirectCall] void drawBuffers(sequence<GLenum> buffers);
-    [NoAllocDirectCall] void clearBufferiv(GLenum buffer, GLint drawbuffer,
-                       [AllowShared, PassAsSpan] Int32Array value, optional GLuint srcOffset = 0);
-    [NoAllocDirectCall] void clearBufferiv(GLenum buffer, GLint drawbuffer, sequence<GLint> value, optional GLuint srcOffset = 0);
+    [NoAllocDirectCall] void clearBufferiv(GLenum buffer, GLint drawbuffer, Int32List value, optional GLuint srcOffset = 0);
     [NoAllocDirectCall] void clearBufferuiv(GLenum buffer, GLint drawbuffer,
-                       [AllowShared, PassAsSpan] Uint32Array value, optional GLuint srcOffset = 0);
-    [NoAllocDirectCall] void clearBufferuiv(GLenum buffer, GLint drawbuffer, sequence<GLuint> value, optional GLuint srcOffset = 0);
-    [NoAllocDirectCall] void clearBufferfv(GLenum buffer, GLint drawbuffer,
-                       [AllowShared, PassAsSpan] Float32Array value, optional GLuint srcOffset = 0);
-    [NoAllocDirectCall] void clearBufferfv(GLenum buffer, GLint drawbuffer, sequence<GLfloat> value, optional GLuint srcOffset = 0);
+                       Uint32List value, optional GLuint srcOffset = 0);
+    [NoAllocDirectCall] void clearBufferfv(GLenum buffer, GLint drawbuffer, Float32List value, optional GLuint srcOffset = 0);
     [NoAllocDirectCall] void clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
 
     /* Query Objects */
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl
index 3dc5d7e7..5257bb33 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl
@@ -39,6 +39,9 @@
 typedef unsigned long  GLuint;
 typedef unrestricted float GLfloat;
 typedef unrestricted float GLclampf;
+typedef [PassAsSpan] ([AllowShared, BufferSourceTypeNoSizeLimit] Float32Array or sequence<GLfloat>) Float32List;
+typedef [PassAsSpan] ([AllowShared, BufferSourceTypeNoSizeLimit] Int32Array or sequence<GLint>) Int32List;
+typedef [PassAsSpan] ([AllowShared, BufferSourceTypeNoSizeLimit] Uint32Array or sequence<GLuint>) Uint32List;
 
 interface mixin WebGLRenderingContextBase {
 
@@ -485,10 +488,10 @@
     void bufferData(GLenum target, GLsizeiptr size, GLenum usage);
     void bufferData(GLenum target, [AllowShared, BufferSourceTypeNoSizeLimit] ArrayBufferView data, GLenum usage);
     void bufferData(GLenum target, [AllowShared, BufferSourceTypeNoSizeLimit] ArrayBuffer? data, GLenum usage);
-    void bufferSubData(GLenum target, GLintptr offset,
-        [AllowShared, PassAsSpan] BufferSource data);
 
     GLenum checkFramebufferStatus(GLenum target);
+    void bufferSubData(GLenum target, GLintptr offset,
+        [AllowShared, PassAsSpan, BufferSourceTypeNoSizeLimit] BufferSource data);
     [NoAllocDirectCall] void clear(GLbitfield mask);
     [NoAllocDirectCall] void clearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
     [NoAllocDirectCall] void clearDepth(GLclampf depth);
@@ -668,63 +671,37 @@
         GLenum format, GLenum type, VideoFrame frame);
 
     [NoAllocDirectCall] void uniform1f(WebGLUniformLocation? location, GLfloat x);
-    [NoAllocDirectCall] void uniform1fv(WebGLUniformLocation? location,
-                       [AllowShared, PassAsSpan, BufferSourceTypeNoSizeLimit] Float32Array v);
-    [NoAllocDirectCall] void uniform1fv(WebGLUniformLocation? location, sequence<GLfloat> v);
+    [NoAllocDirectCall] void uniform1fv(WebGLUniformLocation? location, Float32List v);
     [NoAllocDirectCall] void uniform1i(WebGLUniformLocation? location, GLint x);
-    [NoAllocDirectCall] void uniform1iv(WebGLUniformLocation? location,
-                       [AllowShared, PassAsSpan, BufferSourceTypeNoSizeLimit] Int32Array v);
-    [NoAllocDirectCall] void uniform1iv(WebGLUniformLocation? location, sequence<GLint> v);
+    [NoAllocDirectCall] void uniform1iv(WebGLUniformLocation? location, Int32List v);
     [NoAllocDirectCall] void uniform2f(WebGLUniformLocation? location, GLfloat x, GLfloat y);
-    [NoAllocDirectCall] void uniform2fv(WebGLUniformLocation? location,
-                       [AllowShared, PassAsSpan] Float32Array v);
-    [NoAllocDirectCall] void uniform2fv(WebGLUniformLocation? location, sequence<GLfloat> v);
+    [NoAllocDirectCall] void uniform2fv(WebGLUniformLocation? location, Float32List v);
     [NoAllocDirectCall] void uniform2i(WebGLUniformLocation? location, GLint x, GLint y);
-    [NoAllocDirectCall] void uniform2iv(WebGLUniformLocation? location,
-                       [AllowShared, PassAsSpan] Int32Array v);
-    [NoAllocDirectCall] void uniform2iv(WebGLUniformLocation? location, sequence<GLint> v);
+    [NoAllocDirectCall] void uniform2iv(WebGLUniformLocation? location, Int32List v);
     [NoAllocDirectCall] void uniform3f(WebGLUniformLocation? location, GLfloat x, GLfloat y, GLfloat z);
-    [NoAllocDirectCall] void uniform3fv(WebGLUniformLocation? location,
-                       [AllowShared, PassAsSpan] Float32Array v);
-    [NoAllocDirectCall] void uniform3fv(WebGLUniformLocation? location, sequence<GLfloat> v);
+    [NoAllocDirectCall] void uniform3fv(WebGLUniformLocation? location, Float32List v);
     [NoAllocDirectCall] void uniform3i(WebGLUniformLocation? location, GLint x, GLint y, GLint z);
-    [NoAllocDirectCall] void uniform3iv(WebGLUniformLocation? location,
-                       [AllowShared, PassAsSpan] Int32Array v);
-    [NoAllocDirectCall] void uniform3iv(WebGLUniformLocation? location, sequence<GLint> v);
+    [NoAllocDirectCall] void uniform3iv(WebGLUniformLocation? location, Int32List v);
     [NoAllocDirectCall] void uniform4f(WebGLUniformLocation? location, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
-    [NoAllocDirectCall] void uniform4fv(WebGLUniformLocation? location,
-                       [AllowShared, PassAsSpan] Float32Array v);
-    [NoAllocDirectCall] void uniform4fv(WebGLUniformLocation? location, sequence<GLfloat> v);
+    [NoAllocDirectCall] void uniform4fv(WebGLUniformLocation? location, Float32List v);
     [NoAllocDirectCall] void uniform4i(WebGLUniformLocation? location, GLint x, GLint y, GLint z, GLint w);
-    [NoAllocDirectCall] void uniform4iv(WebGLUniformLocation? location,
-                       [AllowShared, PassAsSpan] Int32Array v);
-    [NoAllocDirectCall] void uniform4iv(WebGLUniformLocation? location, sequence<GLint> v);
+    [NoAllocDirectCall] void uniform4iv(WebGLUniformLocation? location, Int32List v);
 
-    [NoAllocDirectCall] void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose,
-                       [AllowShared, PassAsSpan] Float32Array array);
-    [NoAllocDirectCall] void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> array);
-    [NoAllocDirectCall] void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose,
-                       [AllowShared, PassAsSpan] Float32Array array);
-    [NoAllocDirectCall] void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> array);
-    [NoAllocDirectCall] void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose,
-                       [AllowShared, PassAsSpan] Float32Array array);
-    [NoAllocDirectCall] void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> array);
+    [NoAllocDirectCall] void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List array);
+    [NoAllocDirectCall] void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List array);
+    [NoAllocDirectCall] void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List array);
 
     void useProgram(WebGLProgram? program);
     void validateProgram(WebGLProgram program);
 
     [NoAllocDirectCall] void vertexAttrib1f(GLuint indx, GLfloat x);
-    [NoAllocDirectCall] void vertexAttrib1fv(GLuint indx, [AllowShared, PassAsSpan] Float32Array values);
-    [NoAllocDirectCall] void vertexAttrib1fv(GLuint indx, sequence<GLfloat> values);
+    [NoAllocDirectCall] void vertexAttrib1fv(GLuint indx, Float32List values);
     [NoAllocDirectCall] void vertexAttrib2f(GLuint indx, GLfloat x, GLfloat y);
-    [NoAllocDirectCall] void vertexAttrib2fv(GLuint indx, [AllowShared, PassAsSpan] Float32Array values);
-    [NoAllocDirectCall] void vertexAttrib2fv(GLuint indx, sequence<GLfloat> values);
+    [NoAllocDirectCall] void vertexAttrib2fv(GLuint indx, Float32List values);
     [NoAllocDirectCall] void vertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z);
-    [NoAllocDirectCall] void vertexAttrib3fv(GLuint indx, [AllowShared, PassAsSpan] Float32Array values);
-    [NoAllocDirectCall] void vertexAttrib3fv(GLuint indx, sequence<GLfloat> values);
+    [NoAllocDirectCall] void vertexAttrib3fv(GLuint indx, Float32List values);
     [NoAllocDirectCall] void vertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
-    [NoAllocDirectCall] void vertexAttrib4fv(GLuint indx, [AllowShared, PassAsSpan] Float32Array values);
-    [NoAllocDirectCall] void vertexAttrib4fv(GLuint indx, sequence<GLfloat> values);
+    [NoAllocDirectCall] void vertexAttrib4fv(GLuint indx, Float32List values);
     [NoAllocDirectCall] void vertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized,
                                                     GLsizei stride, GLintptr offset);
 
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index f8d29e63..56f0976 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -95,6 +95,9 @@
         WTF::BindOnce(&XRFrameProvider::OnProviderConnectionError,
                       WrapWeakPersistent(this), WrapWeakPersistent(session)));
 
+    frame_transport_->RegisterFrameRenderedCallback(WTF::BindRepeating(
+        &XRFrameProvider::OnRenderComplete, WrapWeakPersistent(this)));
+
     frame_transport_->BindSubmitFrameClient(
         std::move(session_ptr->submit_frame_sink->client_receiver));
     frame_transport_->SetTransportOptions(
@@ -669,6 +672,9 @@
         webgl_context->SharedImageInterface(), webgl_context,
         std::move(image_ref), frame_id_);
     succeeded ? num_frames_++ : dropped_frames_++;
+    if (succeeded) {
+      submit_frame_time_.StartTimer();
+    }
 
     return;
   }
@@ -693,6 +699,9 @@
       std::move(image_ref), frame_id_);
 
   succeeded ? num_frames_++ : dropped_frames_++;
+  if (succeeded) {
+    submit_frame_time_.StartTimer();
+  }
 
   // Reset our frame id, since anything we'd want to do (resizing/etc) can
   // no-longer happen to this frame.
@@ -769,12 +778,19 @@
   xr_frame_stat->page_animation_frame_time =
       immersive_session()->TakeAnimationFrameTimerAverage();
 
+  xr_frame_stat->submit_frame_time =
+      submit_frame_time_.TakeAverageMicroseconds();
+
   if (xr_->GetWebXrInternalsRendererListener()) {
     xr_->GetWebXrInternalsRendererListener()->OnFrameData(
         std::move(xr_frame_stat));
   }
 }
 
+void XRFrameProvider::OnRenderComplete() {
+  submit_frame_time_.StopTimer();
+}
+
 void XRFrameProvider::Trace(Visitor* visitor) const {
   visitor->Trace(xr_);
   visitor->Trace(frame_transport_);
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.h b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
index 3f30481..69132d3 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
@@ -104,6 +104,7 @@
   // Sends the frame data to the requesting sessions for calculating
   // diagnostics.
   void SendFrameData();
+  void OnRenderComplete();
 
   void OnProviderConnectionError(XRSession* session);
   void ProcessScheduledFrame(device::mojom::blink::XRFrameDataPtr frame_data,
@@ -179,6 +180,7 @@
   int num_frames_ = 0;
   int dropped_frames_ = 0;
   AverageTimer frame_data_time_;
+  AverageTimer submit_frame_time_;
 
   base::TimeTicks last_frame_statistics_sent_time_;
   base::RepeatingTimer repeating_timer_;
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
index 543a5d20..d33c161 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -1008,9 +1008,9 @@
   return result;
 }
 
-void ShapeResult::ApplyLeadingExpansion(float expansion) {
+void ShapeResult::ApplyLeadingExpansion(LayoutUnit expansion) {
   DCHECK(RuntimeEnabledFeatures::RubyLineBreakableEnabled());
-  if (expansion <= 0) {
+  if (expansion <= LayoutUnit()) {
     return;
   }
   for (auto& run : runs_) {
@@ -1027,14 +1027,15 @@
         continue;
       }
 
-      glyph_data.advance += expansion;
-      run->width_ += expansion;
-      width_ += expansion;
+      const float expansion_as_float = expansion.ToFloat();
+      glyph_data.advance += expansion_as_float;
+      run->width_ += expansion_as_float;
+      width_ += expansion_as_float;
 
       if (run->IsHorizontal()) {
-        run->glyph_data_.AddOffsetWidthAt(i, expansion);
+        run->glyph_data_.AddOffsetWidthAt(i, expansion_as_float);
       } else {
-        run->glyph_data_.AddOffsetHeightAt(i, expansion);
+        run->glyph_data_.AddOffsetHeightAt(i, expansion_as_float);
         has_vertical_offsets_ = true;
       }
       return;
@@ -1044,9 +1045,9 @@
   NOTREACHED_IN_MIGRATION();
 }
 
-void ShapeResult::ApplyTrailingExpansion(float expansion) {
+void ShapeResult::ApplyTrailingExpansion(LayoutUnit expansion) {
   DCHECK(RuntimeEnabledFeatures::RubyLineBreakableEnabled());
-  if (expansion <= 0) {
+  if (expansion <= LayoutUnit()) {
     return;
   }
   for (auto& run : base::Reversed(runs_)) {
@@ -1057,9 +1058,10 @@
       continue;
     }
     HarfBuzzRunGlyphData& glyph_data = run->glyph_data_.back();
-    glyph_data.advance += expansion;
-    run->width_ += expansion;
-    width_ += expansion;
+    const float expansion_as_float = expansion.ToFloat();
+    glyph_data.advance += expansion_as_float;
+    run->width_ += expansion_as_float;
+    width_ += expansion_as_float;
     return;
   }
   // No glyphs.
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
index ae430018..c22daf16 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
@@ -297,9 +297,9 @@
   ShapeResult* ApplySpacingToCopy(ShapeResultSpacing<TextRun>&,
                                   const TextRun&) const;
   // Add `expansion` space before the first glyph.
-  void ApplyLeadingExpansion(float expansion);
+  void ApplyLeadingExpansion(LayoutUnit expansion);
   // Add `expansion` space after the last glyph.
-  void ApplyTrailingExpansion(float expansion);
+  void ApplyTrailingExpansion(LayoutUnit expansion);
 
   // Adds spacing between ideograph character and non-ideograph character for
   // the property of text-autospace.
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
index 3f1a580a..de883c3 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
@@ -214,6 +214,11 @@
   last_transfer_succeeded_ = success;
 }
 
+void XRFrameTransport::RegisterFrameRenderedCallback(
+    base::RepeatingClosure callback) {
+  on_submit_frame_rendered_callback_ = std::move(callback);
+}
+
 void XRFrameTransport::WaitForPreviousTransfer() {
   DVLOG(3) << __func__ << " Start";
   TRACE_EVENT0("gpu", "waitForPreviousTransferToFinish");
@@ -229,6 +234,9 @@
 void XRFrameTransport::OnSubmitFrameRendered() {
   DVLOG(3) << __FUNCTION__;
   waiting_for_previous_frame_render_ = false;
+  if (on_submit_frame_rendered_callback_) {
+    on_submit_frame_rendered_callback_.Run();
+  }
 }
 
 base::TimeDelta XRFrameTransport::WaitForPreviousRenderToFinish() {
@@ -249,6 +257,9 @@
   // We just received a GpuFence, unblock WaitForGpuFenceReceived.
   waiting_for_previous_frame_fence_ = false;
   previous_frame_fence_ = std::make_unique<gfx::GpuFence>(std::move(handle));
+  if (on_submit_frame_rendered_callback_) {
+    on_submit_frame_rendered_callback_.Run();
+  }
 }
 
 base::TimeDelta XRFrameTransport::WaitForGpuFenceReceived() {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
index c91cf16..63b901c 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
@@ -66,6 +66,8 @@
                           gpu::gles2::GLES2Interface*,
                           int16_t vr_frame_id);
 
+  void RegisterFrameRenderedCallback(base::RepeatingClosure callback);
+
   virtual void Trace(Visitor*) const;
 
  private:
@@ -97,6 +99,8 @@
 
   device::mojom::blink::XRPresentationTransportOptionsPtr transport_options_;
 
+  base::RepeatingClosure on_submit_frame_rendered_callback_;
+
   std::unique_ptr<ImageToBufferCopier> frame_copier_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 };
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
index a2cbcbc..93665c0 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -1625,20 +1625,18 @@
                                        const std::string& label,
                                        const std::string& language,
                                        bool is_first_track) {
-  client_->AddAudioTrack(WebString::FromUTF8(id),
-                         WebMediaPlayerClient::kAudioTrackKindMain,
-                         WebString::FromUTF8(label),
-                         WebString::FromUTF8(language), is_first_track);
+  client_->AddMediaTrack(media::MediaTrack::CreateAudioTrack(
+      id, media::MediaTrack::AudioKind::kMain, label, language,
+      is_first_track));
 }
 
 void WebMediaPlayerImpl::AddVideoTrack(const std::string& id,
                                        const std::string& label,
                                        const std::string& language,
                                        bool is_first_track) {
-  client_->AddVideoTrack(WebString::FromUTF8(id),
-                         WebMediaPlayerClient::kVideoTrackKindMain,
-                         WebString::FromUTF8(label),
-                         WebString::FromUTF8(language), is_first_track);
+  client_->AddMediaTrack(media::MediaTrack::CreateVideoTrack(
+      id, media::MediaTrack::VideoKind::kMain, label, language,
+      is_first_track));
 }
 #endif  // BUILDFLAG(ENABLE_FFMPEG)
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index cee8ca8..fbeeafa 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1061,7 +1061,7 @@
     },
     {
       name: "CSSNestedDeclarations",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "CSSPaintAPIArguments",
diff --git a/third_party/blink/renderer/platform/wtf/text/atomic_string.cc b/third_party/blink/renderer/platform/wtf/text/atomic_string.cc
index 068affd..2ae6772 100644
--- a/third_party/blink/renderer/platform/wtf/text/atomic_string.cc
+++ b/third_party/blink/renderer/platform/wtf/text/atomic_string.cc
@@ -87,6 +87,10 @@
   return AtomicString(AtomicStringTable::Instance().AddUTF8(chars, nullptr));
 }
 
+AtomicString AtomicString::FromUTF8(std::string_view string) {
+  return AtomicString::FromUTF8(string.data(), string.length());
+}
+
 AtomicString AtomicString::LowerASCII(AtomicString source) {
   if (source.IsLowerASCII()) [[likely]] {
     return source;
diff --git a/third_party/blink/renderer/platform/wtf/text/atomic_string.h b/third_party/blink/renderer/platform/wtf/text/atomic_string.h
index 7205441..b27d86b 100644
--- a/third_party/blink/renderer/platform/wtf/text/atomic_string.h
+++ b/third_party/blink/renderer/platform/wtf/text/atomic_string.h
@@ -200,6 +200,7 @@
   // NOTE: Passing a zero size means use the whole string.
   static AtomicString FromUTF8(const char*, size_t length);
   static AtomicString FromUTF8(const char*);
+  static AtomicString FromUTF8(std::string_view);
 
   std::string Ascii() const { return string_.Ascii(); }
   std::string Latin1() const { return string_.Latin1(); }
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index eca2536..8e9cdcd 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -39,56 +39,70 @@
         'allowed': [
             # absl
             'absl::Cleanup',
-            'absl::MakeInt128',
-            'absl::MakeUint128',
-            'absl::Int128High64',
-            'absl::Int128Low64',
-            'absl::Uint128High64',
-            'absl::Uint128Low64',
             'absl::get',
             'absl::get_if',
             'absl::holds_alternative',
             'absl::in_place',
             'absl::in_place_type',
             'absl::int128',
+            'absl::Int128High64',
+            'absl::Int128Low64',
+            'absl::MakeInt128',
+            'absl::MakeUint128',
             'absl::monostate',
             'absl::uint128',
+            'absl::Uint128High64',
+            'absl::Uint128Low64',
             'absl::variant',
             'absl::visit',
 
             # //base constructs that are allowed everywhere
+            'base::(byte_)?span_from_ref',
             'base::AdoptRef',
             'base::ApplyMetadataToPastSamples',
-            'base::SampleMetadataScope',
+            'base::as_byte_span',
+            'base::as_byte_span',
+            'base::as_bytes',
+            'base::as_bytes',
+            'base::as_chars',
+            'base::as_chars',
+            'base::as_string_view',
+            'base::as_string_view',
+            'base::as_writable_byte_span',
+            'base::as_writable_bytes',
+            'base::as_writable_bytes',
+            'base::as_writable_chars',
             'base::AutoReset',
-            'base::Contains',
+            'base::bit_cast',
             'base::ConditionVariable',
+            'base::Contains',
             'base::CPU',
-            'base::ValuesEquivalent',
             'base::Days',
             'base::DefaultTickClock',
             'base::ElapsedTimer',
             'base::EnumSet',
-            'base::HashInts',
-            'base::JobDelegate',
-            'base::JobHandle',
-            'base::PostJob',
+            'base::expected',
             'base::File',
             'base::FileErrorOr',
             'base::FilePath',
             'base::FunctionRef',
             'base::GetUniqueIdForProcess',
+            'base::HashingLRUCache',
+            'base::HashInts',
             'base::HeapArray',
             'base::HexStringToUInt64',
             'base::Hours',
-            "base::i18n::TextDirection",
-            "base::i18n::ToChar16Ptr",
-            "base::i18n::ToUCharPtr",
+            'base::i18n::TextDirection',
+            'base::i18n::ToChar16Ptr',
+            'base::i18n::ToUCharPtr',
+            'base::JobDelegate',
+            'base::JobHandle',
             'base::Location',
+            'base::make_span',
             'base::MakeRefCounted',
             'base::MappedReadOnlyRegion',
-            'base::MatchPattern',
             'base::MatcherStringPattern',
+            'base::MatchPattern',
             'base::MessagePump',
             'base::MetricsSubSampler',
             'base::Microseconds',
@@ -96,6 +110,7 @@
             'base::Minutes',
             'base::Nanoseconds',
             'base::NotFatalUntil',
+            'base::optional_ref',
             'base::OptionalFromPtr',
             'base::OptionalToPtr',
             'base::Overloaded',
@@ -103,21 +118,28 @@
             'base::PersistentHash',
             'base::PlatformThread',
             'base::PlatformThreadId',
+            'base::PostJob',
+            'base::PowerMonitor',
             'base::Process',
             'base::RadToDeg',
-            'base::RefCountedData',
-            'base::RunLoop',
-            'base::HashingLRUCache',
+            'base::ranges::.+',
             'base::ReadOnlySharedMemoryMapping',
             'base::ReadOnlySharedMemoryRegion',
+            'base::RefCountedData',
             'base::RemoveChars',
             'base::RepeatingTimer',
+            'base::RunLoop',
+            'base::SampleMetadataScope',
+            'base::ScopedAllowBlocking',
+            'base::ScopedClosureRunner',
+            'base::ScopedFD',
             'base::Seconds',
+            'base::sequence_manager::TaskTimeObserver',
             'base::SequencedTaskRunner',
             'base::SingleThreadTaskRunner',
-            'base::ScopedAllowBlocking',
-            'base::ScopedFD',
-            'base::ScopedClosureRunner',
+            'base::span',
+            'base::span(_with_nul)?_from_cstring',
+            'base::Span(OrSize|Reader|Writer)',
             'base::StringPiece',
             'base::SubstringSetMatcher',
             'base::SupportsWeakPtr',
@@ -129,41 +151,20 @@
             'base::Time',
             'base::TimeDelta',
             'base::TimeTicks',
-            'base::trace_event::.*',
+            'base::to_underlying',
             'base::Token',
+            'base::trace_event::.*',
+            'base::unexpected',
             'base::UnguessableToken',
             'base::UnguessableTokenHash',
             'base::UnlocalizedTimeFormatWithPattern',
             'base::UnsafeSharedMemoryRegion',
             'base::Uuid',
+            'base::ValuesEquivalent',
             'base::WeakPtr',
             'base::WeakPtrFactory',
             'base::WrapRefCounted',
             'base::WritableSharedMemoryMapping',
-            'base::as_byte_span',
-            'base::as_bytes',
-            'base::as_chars',
-            'base::as_string_view',
-            'base::as_writable_bytes',
-            'base::bit_cast',
-            'base::expected',
-            'base::make_span',
-            'base::optional_ref',
-            'base::to_underlying',
-            'base::unexpected',
-            'base::ranges::.+',
-            'base::sequence_manager::TaskTimeObserver',
-            'base::span',
-            'base::span(_with_nul)?_from_cstring',
-            'base::Span(OrSize|Reader|Writer)',
-            'base::as_byte_span',
-            'base::as_writable_byte_span',
-            'base::as_bytes',
-            'base::as_writable_bytes',
-            'base::as_chars',
-            'base::as_writable_chars',
-            'base::as_string_view',
-            'base::(byte_)?span_from_ref',
             'logging::GetVlogLevel',
             'logging::SetLogItems',
 
@@ -182,8 +183,8 @@
             'base::bits::.+',
 
             # //base/observer_list.h.
-            'base::ObserverList',
             'base::CheckedObserver',
+            'base::ObserverList',
 
             # //base/functional/callback_helpers.h.
             'base::DoNothing',
@@ -222,60 +223,60 @@
             'base::LinearHistogram',
 
             # //base/metrics/field_trial_params.h.
-            'base::GetFieldTrialParamValueByFeature',
             'base::GetFieldTrialParamByFeatureAsBool',
             'base::GetFieldTrialParamByFeatureAsDouble',
             'base::GetFieldTrialParamByFeatureAsInt',
+            'base::GetFieldTrialParamValueByFeature',
 
             # //base/numerics/safe_conversions.h.
             'base::as_signed',
             'base::as_unsigned',
             'base::checked_cast',
-            'base::saturated_cast',
-            'base::strict_cast',
             'base::ClampCeil',
             'base::ClampFloor',
+            'base::ClampRound',
             'base::IsTypeInRangeForNumericType',
             'base::IsValueInRangeForNumericType',
             'base::IsValueNegative',
             'base::MakeStrictNum',
-            'base::ClampRound',
             'base::SafeUnsignedAbs',
+            'base::saturated_cast',
+            'base::strict_cast',
             'base::StrictNumeric',
 
             # //base/synchronization/lock.h.
             'base::AutoLock',
-            'base::AutoUnlock',
             'base::AutoTryLock',
+            'base::AutoUnlock',
             'base::Lock',
 
             # //base/synchronization/waitable_event.h.
             'base::WaitableEvent',
 
             # //base/numerics/checked_math.h.
+            'base::CheckAdd',
+            'base::CheckAnd',
+            'base::CheckDiv',
             'base::CheckedNumeric',
-            'base::IsValidForType',
-            'base::ValueOrDieForType',
-            'base::ValueOrDefaultForType',
-            'base::MakeCheckedNum',
+            'base::CheckLsh',
             'base::CheckMax',
             'base::CheckMin',
-            'base::CheckAdd',
-            'base::CheckSub',
-            'base::CheckMul',
-            'base::CheckDiv',
             'base::CheckMod',
-            'base::CheckLsh',
-            'base::CheckRsh',
-            'base::CheckAnd',
+            'base::CheckMul',
             'base::CheckOr',
+            'base::CheckRsh',
+            'base::CheckSub',
             'base::CheckXor',
+            'base::IsValidForType',
+            'base::MakeCheckedNum',
+            'base::ValueOrDefaultForType',
+            'base::ValueOrDieForType',
 
             # //base/numerics/clamped_math.h.
             'base::ClampAdd',
             'base::ClampedNumeric',
-            'base::ClampMin',
             'base::ClampMax',
+            'base::ClampMin',
             'base::ClampSub',
             'base::MakeClampedNum',
 
@@ -291,32 +292,32 @@
             'base::AtomicSequenceNumber',
 
             # Task traits
-            'base::TaskTraits',
             'base::MayBlock',
+            'base::SingleThreadTaskRunnerThreadMode',
             'base::TaskPriority',
             'base::TaskShutdownBehavior',
-            'base::WithBaseSyncPrimitives',
+            'base::TaskTraits',
             'base::ThreadPolicy',
             'base::ThreadPool',
-            'base::SingleThreadTaskRunnerThreadMode',
+            'base::WithBaseSyncPrimitives',
 
             # Byte order
-            'base::BigEndian(Reader|Writer)',
             'base::(numerics::)?((I|U)(8|16|32|64)|(Float|Double))(To|From)(Big|Little|Native)Endian',
             'base::(numerics::)?ByteSwap',
+            'base::BigEndian(Reader|Writer)',
 
             # (Cryptographic) random number generation
-            'base::RandUint64',
-            'base::RandInt',
-            'base::RandGenerator',
-            'base::RandDouble',
             'base::RandBytes',
             'base::RandBytesAsString',
+            'base::RandDouble',
+            'base::RandGenerator',
+            'base::RandInt',
+            'base::RandUint64',
 
             # Feature list checking.
+            "base::GetFieldTrial.*",
             'base::Feature.*',
             'base::FEATURE_.+',
-            "base::GetFieldTrial.*",
             'base::features::.+',
             'features::.+',
 
@@ -385,9 +386,9 @@
             'partition_alloc::internal::kAlignment',
 
             # PartitionAlloc
-            'base::PartitionFree',
-            'base::PartitionAllocZeroFill',
             'base::PartitionAllocReturnNull',
+            'base::PartitionAllocZeroFill',
+            'base::PartitionFree',
 
             # For TaskObserver.
             'base::PendingTask',
@@ -397,20 +398,20 @@
             'cc::CategorizedWorkerPool',
             'cc::ColorFilter',
             'cc::DrawLooper',
-            'cc::PathEffect',
             'cc::InspectablePaintRecorder',
             'cc::InspectableRecordPaintCanvas',
+            'cc::NodeId',
+            'cc::NodeInfo',
             'cc::PaintCanvas',
             'cc::PaintFlags',
             'cc::PaintImage',
             'cc::PaintImageBuilder',
             'cc::PaintRecord',
-            'cc::RecordPaintCanvas',
             'cc::PaintShader',
             'cc::PaintWorkletInput',
+            'cc::PathEffect',
+            'cc::RecordPaintCanvas',
             'cc::RefCountedBuffer',
-            'cc::NodeId',
-            'cc::NodeInfo',
             'cc::UsePaintCache',
 
             # Chromium geometry types.
@@ -420,8 +421,8 @@
             'gfx::Outsets',
             'gfx::OutsetsF',
             'gfx::Point',
-            'gfx::PointF',
             'gfx::Point3F',
+            'gfx::PointF',
             'gfx::QuadF',
             'gfx::Quaternion',
             'gfx::Rect',
@@ -443,25 +444,25 @@
             'gfx::DotProduct',
             'gfx::IntersectRects',
             'gfx::MapRect',
+            'gfx::MapRect',
+            'gfx::MaximumCoveredRect',
             'gfx::PointAtOffsetFromOrigin',
             'gfx::PointFToSkPoint',
             'gfx::PointToSkIPoint',
-            'gfx::MapRect',
-            'gfx::MaximumCoveredRect',
             'gfx::RectFToSkRect',
             'gfx::RectToSkIRect',
             'gfx::RectToSkRect',
             'gfx::ScaleInsets',
+            'gfx::ScalePoint',
+            'gfx::ScaleRect',
+            'gfx::ScaleSize',
             'gfx::ScaleToCeiledSize',
             'gfx::ScaleToEnclosedRect',
             'gfx::ScaleToEnclosingRect',
             'gfx::ScaleToFlooredSize',
+            'gfx::ScaleToRoundedPoint',
             'gfx::ScaleToRoundedRect',
             'gfx::ScaleToRoundedSize',
-            'gfx::ScaleRect',
-            'gfx::ScaleSize',
-            'gfx::ScalePoint',
-            'gfx::ScaleToRoundedPoint',
             'gfx::ScaleVector2d',
             'gfx::ScaleVector3d',
             'gfx::SizeFToSkSize',
@@ -534,17 +535,17 @@
             'cc::ViewportLayers',
 
             # cc::Layer helper enums.
-            'cc::HORIZONTAL',
-            'cc::VERTICAL',
-            'cc::THUMB',
-            'cc::TRACK_BUTTONS_TICKMARKS',
             'cc::BrowserControlsState',
             'cc::EventListenerClass',
             'cc::EventListenerProperties',
             'cc::HitTestOpaqueness',
+            'cc::HORIZONTAL',
+            'cc::THUMB',
+            'cc::TRACK_BUTTONS_TICKMARKS',
+            'cc::VERTICAL',
 
             # Animation
-            'cc::AnimationHost',
+            "cc::AnimationHost",
             "cc::AnimationIdProvider",
             "cc::AnimationTimeline",
             "cc::FilterKeyframe",
@@ -568,30 +569,30 @@
             'cc::PaintHoldingReason',
 
             # Scrolling
+            'cc::BrowserControlsOffsetTagsInfo',
+            'cc::kManipulationInfoNone',
             'cc::kManipulationInfoPinchZoom',
             'cc::kManipulationInfoPrecisionTouchPad',
+            'cc::kManipulationInfoScrollbar',
             'cc::kManipulationInfoTouch',
             'cc::kManipulationInfoWheel',
-            'cc::kManipulationInfoScrollbar',
-            'cc::kManipulationInfoNone',
-            'cc::kPixelsPerLineStep',
             'cc::kMinFractionToStepWhenPaging',
             'cc::kPercentDeltaForDirectionalScroll',
-            'cc::BrowserControlsOffsetTagsInfo',
+            'cc::kPixelsPerLineStep',
             'cc::MainThreadScrollingReason',
             'cc::ManipulationInfo',
+            'cc::ScrollOffsetAnimationCurve',
             'cc::ScrollSnapAlign',
             'cc::ScrollSnapType',
-            'cc::ScrollOffsetAnimationCurve',
             'cc::ScrollStateData',
             'cc::ScrollUtils',
             'cc::SnapAlignment',
             'cc::SnapAreaData',
             'cc::SnapAxis',
             'cc::SnapContainerData',
-            'cc::SnappedTargetData',
             'cc::SnapFlingClient',
             'cc::SnapFlingController',
+            'cc::SnappedTargetData',
             'cc::SnapPositionData',
             'cc::SnapSelectionStrategy',
             'cc::SnapStrictness',
@@ -618,6 +619,7 @@
             'url::.+',
 
             # Nested namespaces under the blink namespace
+            '[a-z_]+_names::.+',
             'bindings::.+',
             'canvas_heuristic_parameters::.+',
             'compositor_target_property::.+',
@@ -630,6 +632,7 @@
             'event_handling_util::.+',
             'event_util::.+',
             'file_error::.+',
+            'file_system_access_error::.+',
             'geometry_util::.+',
             'inspector_\\w+_event::.+',
             'inspector_async_task::.+',
@@ -641,7 +644,6 @@
             'layout_text_control::.+',
             'media_constraints_impl::.+',
             'media_element_parser_helpers::.+',
-            'file_system_access_error::.+',
             'network_utils::.+',
             'origin_trials::.+',
             'paint_filter_builder::.+',
@@ -656,22 +658,21 @@
             'touch_action_util::.+',
             'trace_event::.+',
             'unicode::.+',
-            'vector_math::.+',
             'v8_compile_hints::.+',
+            'vector_math::.+',
             'web_core_test_support::.+',
             'worker_pool::.+',
             'xpath::.+',
-            '[a-z_]+_names::.+',
 
             # Third-party libraries that don't depend on non-Blink Chrome code
             # are OK.
             'icu::.+',
+            'inspector_protocol_encoding::.+',
             'perfetto::.+',  # tracing
+            'snappy::.+',
             'testing::.+',  # googlemock / googletest
             'v8::.+',
             'v8_inspector::.+',
-            'inspector_protocol_encoding::.+',
-            'snappy::.+',
 
             # Inspector instrumentation and protocol
             'probe::.+',
@@ -691,14 +692,14 @@
             # CanonicalCookie and related headers
             'net::CanonicalCookie',
             'net::CookieInclusionStatus',
-            'net::CookiePriority',
             'net::CookiePartitionKey',
+            'net::CookiePriority',
             'net::CookieSameSite',
             'net::CookieSourceScheme',
 
             # HTTP status codes
-            'net::HTTP_.+',
             'net::ERR_.*',
+            'net::HTTP_.+',
 
             # For ConnectionInfo enumeration
             'net::HttpConnectionInfo',
@@ -728,9 +729,9 @@
             # Note that the Mojo callback helpers are explicitly forbidden:
             # Blink already has a signal for contexts being destroyed, and
             # other types of failures should be explicitly signalled.
+            '(?:.+::)?mojom::.+',
             'mojo::(?!WrapCallback).+',
             'mojo_base::BigBuffer.*',
-            '(?:.+::)?mojom::.+',
             'service_manager::InterfaceProvider',
 
             # STL containers such as std::string and std::vector are discouraged
@@ -748,10 +749,10 @@
             'ui::Cursor',
 
             # UI Pointer and Hover
-            'ui::PointerType',
-            'ui::POINTER_TYPE_.*',
-            'ui::HoverType',
             'ui::HOVER_TYPE_.*',
+            'ui::HoverType',
+            'ui::POINTER_TYPE_.*',
+            'ui::PointerType',
 
             # UI Keyconverter
             'ui::DomCode',
@@ -760,6 +761,10 @@
 
             # Accessibility base types and the non-Blink enums they
             # depend on.
+            'ax::mojom::BoolAttribute',
+            'ax::mojom::HasPopup',
+            'ax::mojom::Restriction',
+            'ax::mojom::State',
             'ui::AXActionData',
             'ui::AXEvent',
             'ui::AXEventIntent',
@@ -768,21 +773,17 @@
             'ui::AXRelativeBounds',
             'ui::AXTreeChecks',
             'ui::AXTreeData',
+            'ui::AXTreeID',
+            'ui::AXTreeIDUnknown',
             'ui::AXTreeSerializer',
             'ui::AXTreeSource',
             'ui::AXTreeUpdate',
-            'ui::AXTreeID',
-            'ui::AXTreeIDUnknown',
-            'ui::kInvalidAXNodeID',
-            'ui::kFirstGeneratedRendererNodeID',
-            'ui::kLastGeneratedRendererNodeID',
             'ui::kAXModeBasic',
             'ui::kAXModeComplete',
+            'ui::kFirstGeneratedRendererNodeID',
+            'ui::kInvalidAXNodeID',
+            'ui::kLastGeneratedRendererNodeID',
             'ui::ToString',
-            'ax::mojom::BoolAttribute',
-            'ax::mojom::HasPopup',
-            'ax::mojom::State',
-            'ax::mojom::Restriction',
 
             # Accessibility helper functions - mostly used in Blink for
             # serialization. Please keep alphabetized.
@@ -866,9 +867,9 @@
         'allowed': [
             # For the script streaming to be able to block when reading from a
             # mojo datapipe.
+            'base::BlockingType',
             'base::ScopedAllowBaseSyncPrimitives',
             'base::ScopedBlockingCall',
-            'base::BlockingType',
         ],
     },
     {
@@ -946,18 +947,18 @@
         'allowed': [
             'cc::ActiveFrameSequenceTrackers',
             'cc::ApplyViewportChangesArgs',
-            'cc::LayerTreeSettings',
-            'cc::PaintBenchmarkResult',
-            'cc::RenderFrameMetadata',
-            'cc::TaskGraphRunner',
             'cc::ContentLayerClient',
             'cc::DeadlinePolicy',
             'cc::DisplayItemList',
             'cc::DrawColorOp',
             'cc::DrawImageOp',
+            'cc::LayerTreeSettings',
+            'cc::PaintBenchmarkResult',
+            'cc::RenderFrameMetadata',
+            'cc::RestoreOp',
             'cc::SaveOp',
             'cc::ScaleOp',
-            'cc::RestoreOp',
+            'cc::TaskGraphRunner',
             'cc::TranslateOp',
             'gfx::DisplayColorSpaces',
             'gfx::FontRenderParams',
@@ -1371,17 +1372,17 @@
         # display-related types.
         'allowed': [
             'base::flat_map',
+            'display::Display',
             'gl::GpuPreference',
             'gpu::ClientSharedImage',
-            'gpu::SHARED_IMAGE_USAGE_.+',
             'gpu::gles2::GLES2Interface',
-            'gpu::raster::RasterInterface',
             'gpu::Mailbox',
             'gpu::MailboxHolder',
+            'gpu::raster::RasterInterface',
+            'gpu::SHARED_IMAGE_USAGE_.+',
             'gpu::SharedImageInterface',
             'gpu::SyncToken',
             'gpu::webgpu::ReservedTexture',
-            'display::Display',
             'media::IsOpaque',
             'media::kNoTransformation',
             'media::PaintCanvasVideoRenderer',
@@ -1535,17 +1536,17 @@
             'third_party/blink/renderer/modules/mediarecorder/',
         ],
         'allowed': [
-            # TODO(crbug.com/960665): Remove base::queue once it is replaced with a WTF equivalent.
-            'base::queue',
             'base::ClampMul',
             'base::MakeFixedFlatMap',
+            'base::NumberToString',
+            # TODO(crbug.com/960665): Remove base::queue once it is replaced with a WTF equivalent.
+            'base::queue',
             'base::SharedMemory',
             'base::StringPiece',
-            'base::NumberToString',
             'base::ThreadTaskRunnerHandle',
-            'media::.+',
             'libopus::.+',
             'libyuv::.+',
+            'media::.+',
             'video_track_recorder::.+',
         ],
         'inclass_allowed': [
@@ -1621,8 +1622,8 @@
         ],
         'allowed': [
             'base::ClampMul',
-            'base::IsAligned',
             'base::DoNothingWithBoundArgs',
+            'base::IsAligned',
             'base::PlatformThreadRef',
             'base::WrapRefCounted',
             'cc::kNumYUVPlanes',
@@ -1630,17 +1631,17 @@
             'cc::YUVIndex',
             'cc::YUVSubsampling',
             'gpu::kNullSurfaceHandle',
-            'gpu::SHARED_IMAGE_.+',
-            'gpu::raster::RasterInterface',
             'gpu::Mailbox',
             'gpu::MailboxHolder',
+            'gpu::raster::RasterInterface',
+            'gpu::SHARED_IMAGE_.+',
             'gpu::SharedImageInterface',
             'gpu::SyncToken',
-            'viz::RasterContextProvider',
-            'viz::ReleaseCallback',
-            'media::.+',
             'libgav1::.+',
             'libyuv::.+',
+            'media::.+',
+            'viz::RasterContextProvider',
+            'viz::ReleaseCallback',
         ]
     },
     {
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/ios.py b/third_party/blink/tools/blinkpy/web_tests/port/ios.py
index 8ddc56a4..2c76914 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/ios.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/ios.py
@@ -55,19 +55,11 @@
 
         return result
 
-    def cmd_line(self):
-        return [
-            self._path_to_simulator(), '-d',
-            self._device_name(), '-s',
-            self._sdk_version(), '-k', 'never', '-c',
-            '%s -' % self.additional_driver_flags()
-        ]
-
     def reinstall_cmd_line(self):
         return [
-            self._path_to_simulator(), '-d',
-            self._device_name(), '-s',
-            self._sdk_version(), '-k', 'never', '-c', '--prepare-web-tests',
+            self.path_to_simulator(), '-d',
+            self.device_name(), '-s',
+            self.sdk_version(), '-k', 'never', '-c', '--prepare-web-tests',
             self.path_to_driver()
         ]
 
@@ -75,7 +67,7 @@
         return self.build_path(self.driver_name() + '.app', target=target)
 
     def check_simulator_is_booted(self):
-        device = self._get_device(self._device_name())
+        device = self._get_device(self.device_name())
         state = device.get('state')
         if state != BOOT_STATE:
             _log.info('No simulator is booted. Booting a simulator...')
@@ -84,18 +76,18 @@
 
             while True:
                 time.sleep(2)  # Wait for 2 seconds before checking the state.
-                device = self._get_device(self._device_name())
+                device = self._get_device(self.device_name())
                 state = device.get('state')
                 if state == BOOT_STATE:
                     break
 
-    def _path_to_simulator(self, target=None):
+    def path_to_simulator(self, target=None):
         return self.build_path('iossim', target=target)
 
-    def _device_name(self, target=None):
+    def device_name(self, target=None):
         return 'iPhone 13'
 
-    def _sdk_version(self, target=None):
+    def sdk_version(self, target=None):
         if len(self.runtime_version) != 0:
             return self.runtime_version
 
@@ -174,11 +166,11 @@
 
     def additional_driver_flags(self):
         flags = super(IOSPort, self).additional_driver_flags()
-        flags += ['--no-sandbox']
-        stdio_redirect_flag = '--stdio-redirect=127.0.0.1:' + str(
-            self._stdio_redirect_port)
-        flags += [stdio_redirect_flag]
-        return " ".join(flags)
+        flags += [
+            '--no-sandbox',
+            '--stdio-redirect=127.0.0.1:%s' % self._stdio_redirect_port
+        ]
+        return flags
 
     def stdio_redirect_port(self):
         return self._stdio_redirect_port
@@ -218,10 +210,26 @@
         super(ChromiumIOSDriver, self).__init__(port, worker_number,
                                                 no_timeout)
 
+    def _web_tests_driver_flags(self):
+        flags = self._port.additional_driver_flags()
+        flags += ['--run-web-tests']
+        flags += ['--user-data-dir']
+        return " ".join(flags)
+
     def _base_cmd_line(self):
-        return [self._port.path_to_driver()]
+        return [
+            self._port.path_to_simulator(),
+            '-d',
+            self._port.device_name(),
+            '-s',
+            self._port.sdk_version(),
+            '-k',
+            'never',
+            '-c',
+            '%s -' % self._web_tests_driver_flags(),
+            self._port.path_to_driver(),
+        ]
 
     def cmd_line(self, per_test_args):
-        cmd = self._port.cmd_line()
-        cmd += self._base_cmd_line()
+        cmd = self._base_cmd_line()
         return cmd
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 8b389f7..59fccf9 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -8015,3 +8015,9 @@
 
 # TODO(crbug.com/362797911): Re-enable this test
 crbug.com/362797911 [ Linux ] fast/text-autosizing/textarea-fontsize-change.html [ Failure Pass ]
+
+# Gardener 2024-09-04
+crbug.com/364455582 [ Release Mac11-arm64 ] external/wpt/webxr/xr_viewport_scale.https.html [ Failure ]
+crbug.com/364455582 [ Release Mac12-arm64 ] external/wpt/webxr/xr_viewport_scale.https.html [ Failure ]
+crbug.com/364455582 [ Release Mac13-arm64 ] external/wpt/webxr/xr_viewport_scale.https.html [ Failure ]
+crbug.com/364455582 [ Release Mac14-arm64 ] external/wpt/webxr/xr_viewport_scale.https.html [ Failure ]
\ No newline at end of file
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 1303bb2..9783835 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1092,7 +1092,7 @@
       "fast/parser"
     ],
     "args": ["--disable-features=DOMPartsAPI,DOMPartsAPIMinimal"],
-    "expires": "Dec 1, 2024"
+    "expires": "Jun 1, 2025"
   },
   {
     "prefix": "dom-parts-minimal",
@@ -1102,7 +1102,7 @@
       "external/wpt/dom/parts"
     ],
     "args": ["--enable-features=DOMPartsAPIMinimal"],
-    "expires": "Dec 1, 2024"
+    "expires": "Jun 1, 2025"
   },
   {
     "prefix": "details-styling-disabled",
@@ -1354,7 +1354,7 @@
       "shadow-dom/getinnerhtml-use-counter.html"
     ],
     "args": ["--enable-blink-features=ElementGetInnerHTML"],
-    "expires": "Nov 30, 2024"
+    "expires": "Dec 1, 2024"
   },
   "This virtual suite ensures that mutation events continue to function in the",
   "case that MutationEvents are re-enabled. That is important because there is",
@@ -1387,7 +1387,7 @@
       "virtual/text-antialias/split-text-crash.xhtml"
     ],
     "args": ["--enable-blink-features=MutationEvents", "--disable-blink-features=OmitBlurEventOnElementRemoval"],
-    "expires": "Apr 1, 2025"
+    "expires": "Jun 1, 2025"
   },
   {
     "prefix": "popover-hint-disabled",
@@ -1419,7 +1419,7 @@
       "wpt_internal/html/semantics/popovers/popover-hint-crash.tentative.html"
     ],
     "args": ["--disable-blink-features=HTMLPopoverHint"],
-    "expires": "Jul 1, 2025"
+    "expires": "Jun 1, 2025"
   },
   "TODO(crbug.com/352333393): These tests behave strangely when moved outside",
   "of a WPT runner directory, e.g. with screen captures being taken before",
@@ -1445,7 +1445,7 @@
       "external/wpt/html/semantics/popovers"
     ],
     "args": ["--disable-blink-features=HTMLAnchorAttribute"],
-    "expires": "Sep 1, 2024"
+    "expires": "Jun 1, 2025"
   },
   {
     "prefix": "position-try-options-enabled",
@@ -2060,7 +2060,7 @@
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["external/wpt/custom-elements/scoped-registry"],
     "args": ["--disable-blink-features=ScopedCustomElementRegistry"],
-    "expires": "Jul 1, 2025"
+    "expires": "Jun 1, 2025"
   },
 
   "This individual test must be run separately from others, because it makes a",
@@ -3459,7 +3459,7 @@
       "external/wpt/html/semantics/popovers/popover-target-action-hover.tentative.html"
     ],
     "args": ["--disable-blink-features=HTMLPopoverActionHover"],
-    "expires": "Feb 1, 2025"
+    "expires": "Jun 1, 2025"
   },
   {
     "prefix": "disable-css-line-clamp",
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index d30a9f22a..d709403 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -282763,19 +282763,6 @@
         ]
        ],
        "stylable-select": {
-        "appearance-base-select-other-elements.tentative.html": [
-         "b0b98b74f941ac9956e73c3e7c58a0bb5de7b21f",
-         [
-          null,
-          [
-           [
-            "/html/semantics/forms/the-select-element/stylable-select/appearance-base-select-other-elements-ref.html",
-            "=="
-           ]
-          ],
-          {}
-         ]
-        ],
         "border-rendering.tentative.html": [
          "3265143624b3feb89d723acd4819917c63518219",
          [
@@ -282802,8 +282789,73 @@
           {}
          ]
         ],
+        "select-child-button-and-datalist-invalidation.tentative.html": [
+         "4a71c187c6e858de69b23e3e9170e519488bac38",
+         [
+          null,
+          [
+           [
+            "/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "select-child-button-and-datalist.tentative.html": [
+         "1650f37d5c1612e74120eb2723639a0030b3b566",
+         [
+          null,
+          [
+           [
+            "/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "select-icon-color.tentative.html": [
+         "833e55164b2bf55151b728f1a50596ce822535db",
+         [
+          null,
+          [
+           [
+            "/html/semantics/forms/the-select-element/stylable-select/select-icon-color-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "select-open-invalidation.tentative.html": [
+         "d973d42b5bf7698652446f3393f4231398c36387",
+         [
+          null,
+          [
+           [
+            "/html/semantics/forms/the-select-element/stylable-select/select-open-invalidation-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "select-option-images.tentative.html": [
+         "9768810f6f5e8540ff93d77a17b457e278b431f6",
+         [
+          null,
+          [
+           [
+            "/html/semantics/forms/the-select-element/stylable-select/select-option-images-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
         "select-size-multiple-new-content.tentative.html": [
-         "ba45cc1a148c8ea4cf9f56e737e344a382da286a",
+         "169f1096255ce35a64eafa15e45cb9b555071710",
          [
           null,
           [
@@ -382092,10 +382144,6 @@
         []
        ],
        "stylable-select": {
-        "appearance-base-select-other-elements-ref.html": [
-         "72abb8843f6dab611347c60a5c3ab06860f3a33b",
-         []
-        ],
         "border-rendering-ref.html": [
          "61c68160585f80cc04569334421ee5f70ad5e7b8",
          []
@@ -382104,6 +382152,28 @@
          "d1a1f1bb61ff377679eac4c19efe0f8c4071a4ba",
          []
         ],
+        "resources": {
+         "stylable-select-styles.css": [
+          "ee94ae35c1e3095128b74967c8ac6e325f1d87a5",
+          []
+         ]
+        },
+        "select-child-button-and-datalist-ref.html": [
+         "a27e662b1c4caba8633c807cb6051dd978415e02",
+         []
+        ],
+        "select-icon-color-ref.html": [
+         "224bdd7eb5d746b3ffccec4f0973fb179c645d37",
+         []
+        ],
+        "select-open-invalidation-ref.html": [
+         "fb609a7247e6413967c64c4017ade1970da184da",
+         []
+        ],
+        "select-option-images-ref.html": [
+         "814e01f8d8f8373ca776e679d37ba4e77619f984",
+         []
+        ],
         "select-size-multiple-new-content-ref.html": [
          "dccff7311d1c8c425ca4a199c5fb1e4ed6181a6e",
          []
@@ -487258,7 +487328,7 @@
        ]
       ],
       "pageswap-ctor.html": [
-       "898c212892fdc3ba5650375a91e5cb8df6997d3c",
+       "3e4e9b6466156f5ffc6497e6b3145df5dd361c1f",
        [
         null,
         {}
@@ -548305,7 +548375,7 @@
       ]
      ],
      "element-request-fullscreen-timing.html": [
-      "9ed6dc149365ac869d108d71006ab2997d83c1d7",
+      "b7986575ffef5468a1c6564b99288c0d3d277e8d",
       [
        null,
        {
@@ -596440,12 +596510,21 @@
          ]
         ],
         "select-datalist-options-idl.tentative.html": [
-         "e00fb1951d499a276f15f74c6bd288d79333fa7a",
+         "1a6274662a2c24150dcec48fc8ada566bf29f304",
          [
           null,
           {}
          ]
         ],
+        "select-datalist-popover-behavior.tentative.html": [
+         "2853a09bad4f9220807ff334e35de33af8a9f267",
+         [
+          null,
+          {
+           "testdriver": true
+          }
+         ]
+        ],
         "select-disabled.tentative.html": [
          "60530b417d59bd0891f820e76b31d1992cbecc3d",
          [
@@ -596465,7 +596544,7 @@
          ]
         ],
         "select-keyboard-behavior.tentative.html": [
-         "1e0ffb28a48a2ef64172987bfc15b3295794bc79",
+         "54dba61af4c1a8cc46d971d0c4adac21a758966c",
          [
           null,
           {
@@ -596475,7 +596554,7 @@
          ]
         ],
         "select-mouse-behavior.tentative.html": [
-         "f4333c410465fc24da5c47e07b3eab76ea8ff4bd",
+         "53c10ae2c4f29770b7456bda2d97b730769dd00d",
          [
           null,
           {
@@ -596500,7 +596579,7 @@
          ]
         ],
         "selectedoption.tentative.html": [
-         "b315eb621559a156c508fa8ab627bc9732ea82ae",
+         "ad006a8b38e038fedb3ee612f9944365cdbf17e3",
          [
           null,
           {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/container-queries/at-container-style-serialization-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-conditional/container-queries/at-container-style-serialization-expected.txt
new file mode 100644
index 0000000..1c3d29d6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-conditional/container-queries/at-container-style-serialization-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Unknown CSS property after 'or'
+  assert_equals: expected "style((--FOO: BAR) or ( prop: val ))" but got "style((--FOO: BAR) or ( prop: val  ))"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/palette-mix-computed.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/palette-mix-computed.html
index bade681..c82678e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/palette-mix-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/palette-mix-computed.html
@@ -10,6 +10,11 @@
 <script src="/css/support/computed-testcommon.js"></script>
 </head>
 <body>
+<style>
+:root {
+  font-size: 16px;
+}
+</style>
 <div id="target"></div>
 <script>
     test_computed_value(`font-palette`, `palette-mix(in oklab, light 30%, dark)`);
@@ -29,6 +34,10 @@
     test_computed_value(`font-palette`, `palette-mix(in oklab, light 60%, dark)`, `palette-mix(in oklab, light 60%, dark)`);
     test_computed_value(`font-palette`, `palette-mix(in oklab, light 10%, dark 40%)`, `palette-mix(in oklab, light 10%, dark 40%)`);
 
+    // With sign() function
+    test_computed_value(`font-palette`, `palette-mix(in oklab, light calc(sign(1rem - 1px) * 10%), dark calc(sign(1rem - 1px) * 40%))`, `palette-mix(in oklab, light 10%, dark 40%)`);
+
+
     // Color spaces parsing
     for (const colorSpace of [ "hsl", "hwb", "lch", "oklch", "lab", "oklab", "srgb", "srgb-linear", "xyz", "xyz-d50", "xyz-d65" ]) {
         const resultColorSpace = colorSpace == "xyz" ? "xyz-d65" : colorSpace;
@@ -40,4 +49,4 @@
     test_computed_value(`font-palette`, `palette-mix(in oklab, palette-mix(in srgb, light 30%, normal) 30%, palette-mix(in srgb, --custom-palette 30%, dark))`);
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-size-adjust-computed.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-size-adjust-computed.html
index 4910ddd2..9dbfb09 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-size-adjust-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-size-adjust-computed.html
@@ -24,6 +24,22 @@
 <body>
 <div id="target"></div>
 <script>
+function compareFontSizeAdjustFromFont(actual, expected) {
+  // Depending on font configurations, the retrieved aspect_value may slightly
+  // differ, causing a few pixels' variation from the expected result.
+  const actual_computed_style = actual.split(" ");
+  const expected_computed_style = expected.split(" ");
+  assert_true(actual_computed_style.length === 2 && expected_computed_style.length === 2);
+
+  const actual_font_metric = actual_computed_style[0];
+  const expected_font_metric = expected_computed_style[0];
+  assert_equals(actual_font_metric, expected_font_metric);
+
+  const actual_aspect_value = parseFloat(actual_computed_style[1]);
+  const expected_aspect_value = parseFloat(expected_computed_style[1]);
+  assert_approx_equals(actual_aspect_value, expected_aspect_value, 0.00001);
+}
+
 promise_test(async (t) => {
   await document.fonts.load("1000px ahem-ex-500");
 
@@ -41,8 +57,8 @@
   test_computed_value('font-size-adjust', 'from-font', '0.5');
   test_computed_value('font-size-adjust', 'ex-height from-font', '0.5');  // default basis 'ex-height' omitted from serialization
   test_computed_value('font-size-adjust', 'cap-height from-font', 'cap-height 0.5');
-  test_computed_value('font-size-adjust', 'ch-width from-font', 'ch-width 1');
-  test_computed_value('font-size-adjust', 'ic-width from-font', 'ic-width 1');
+  test_computed_value('font-size-adjust', 'ch-width from-font', 'ch-width 1', undefined, {comparisonFunction: compareFontSizeAdjustFromFont});
+  test_computed_value('font-size-adjust', 'ic-width from-font', 'ic-width 1', undefined, {comparisonFunction: compareFontSizeAdjustFromFont});
   test_computed_value('font-size-adjust', 'ic-height from-font', 'ic-height 1');
 })
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/navigation/pageswap-ctor.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/navigation/pageswap-ctor.html
index 898c212..3e4e9b64 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/navigation/pageswap-ctor.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/navigation/pageswap-ctor.html
@@ -42,7 +42,6 @@
     assert_true(e instanceof PageSwapEvent);
     assert_equals(e.type, "pageswap");
     assert_equals(e.viewTransition, viewTransition);
-    viewTransition.skipTransition();
 }, "Constructing pageswap event with a viewTransition");
 test(function() {
     const viewTransition = document.startViewTransition();
@@ -51,6 +50,5 @@
     assert_equals(e.type, "pageswap");
     assert_equals(e.viewTransition, viewTransition);
     assert_equals(e.activation, navigation.activation);
-    viewTransition.skipTransition();
 }, "Constructing pageswap event with a viewTransition and activation");
-</script>
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/zoom-with-sign-function.html b/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/zoom-with-sign-function.html
new file mode 100644
index 0000000..d3df1861
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/zoom-with-sign-function.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#sign-funcs">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../support/numeric-testcommon.js"></script>
+<style>
+#target {
+  font-size: 10px;
+}
+</style>
+<div id="target"></div>
+<script>
+  test_math_used('calc(sign(1em - 1px) * 2)', '2', {prop:'zoom'});
+  test_math_used('calc(sign(1em - 1px) * 2%)', '2%', {prop:'zoom'});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fullscreen/api/element-request-fullscreen-timing.html b/third_party/blink/web_tests/external/wpt/fullscreen/api/element-request-fullscreen-timing.html
index 9ed6dc14..b798657 100644
--- a/third_party/blink/web_tests/external/wpt/fullscreen/api/element-request-fullscreen-timing.html
+++ b/third_party/blink/web_tests/external/wpt/fullscreen/api/element-request-fullscreen-timing.html
@@ -14,25 +14,19 @@
   // callbacks should be run after it is fired, before the timer callback.
   // The resize event should fire before the fullscreenchange event.
   const events = [];
-  const p = new Promise(resolve => {
-    const callback = t.step_func(event => {
-      // fullscreenElement should have changed before either event is fired.
-      assert_equals(document.fullscreenElement, div, `fullscreenElement in {event.type} event`);
-      events.push(event.type);
-      if (event.type == 'fullscreenchange') {
-        step_timeout(t.unreached_func('timer callback'));
-        requestAnimationFrame(t.step_func_done(() => {
-          assert_array_equals(events, ['resize', 'fullscreenchange'], 'event order');
-          resolve();
-        }));
-      }
-    });
-    document.onfullscreenchange = window.onresize = callback;
+  const callback = t.step_func(event => {
+    // fullscreenElement should have changed before either event is fired.
+    assert_equals(document.fullscreenElement, div, `fullscreenElement in {event.type} event`);
+    events.push(event.type);
+    if (event.type == 'fullscreenchange') {
+      step_timeout(t.unreached_func('timer callback'));
+      requestAnimationFrame(t.step_func_done(() => {
+        assert_array_equals(events, ['resize', 'fullscreenchange'], 'event order');
+      }));
+    }
   });
+  document.onfullscreenchange = window.onresize = callback;
   await trusted_request(div);
-  await p;
-  // so the user doesn't have to exit for themselves
-  document.exitFullscreen();
 }, 'Timing of fullscreenchange and resize events');
 
 async_test(t => {
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
index 553de7af..84f6a597 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
@@ -553,7 +553,9 @@
         });
       }
 
-      if (test.output) {
+      if (test.output &&
+          context.opSupportLimits().conv2d.input.dataTypes.includes(
+              test.input.dataType)) {
         const output = builder.conv2d(input, filter, test.options);
         assert_equals(output.dataType(), test.output.dataType);
         assert_array_equals(output.shape(), test.output.dimensions);
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/convTranspose2d.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/convTranspose2d.https.any.js
index b9704c2..2c4e9dc 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/convTranspose2d.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/convTranspose2d.https.any.js
@@ -568,7 +568,9 @@
         });
       }
 
-      if (test.output) {
+      if (test.output &&
+          context.opSupportLimits().convTranspose2d.input.dataTypes.includes(
+              test.input.dataType)) {
         const output = builder.convTranspose2d(input, filter, test.options);
         assert_equals(output.dataType(), test.output.dataType);
         assert_array_equals(output.shape(), test.output.dimensions);
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/gru.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/gru.https.any.js
index 00a39e4..2b16897 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/gru.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/gru.https.any.js
@@ -330,7 +330,9 @@
         }
       }
 
-      if (test.outputs) {
+      if (test.outputs &&
+          context.opSupportLimits().gru.input.dataTypes.includes(
+              test.input.dataType)) {
         const outputs = builder.gru(
             input, weight, recurrentWeight, test.steps, test.hiddenSize,
             options);
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/gruCell.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/gruCell.https.any.js
index 02408da5..9c0c6d1 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/gruCell.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/gruCell.https.any.js
@@ -347,7 +347,9 @@
             }
           }
 
-          if (test.output) {
+          if (test.output &&
+              context.opSupportLimits().gruCell.input.dataTypes.includes(
+                  test.input.dataType)) {
             const output = builder.gruCell(
                 input, weight, recurrentWeight, hiddenState, test.hiddenSize,
                 options);
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/instanceNormalization.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/instanceNormalization.https.any.js
index 0777d98..36652dd 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/instanceNormalization.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/instanceNormalization.https.any.js
@@ -84,7 +84,8 @@
   {
     name: '[instanceNormalization] Test when the input data type is float16.',
     input: {dataType: 'float16', dimensions: [1, 2, 3, 4]},
-    output: {dataType: 'float16', dimensions: [1, 2, 3, 4]}
+    output: {dataType: 'float16', dimensions: [1, 2, 3, 4]},
+    options: {label}
   },
   {
     name: '[instanceNormalization] Throw if the input is not a 4-D tensor.',
@@ -195,7 +196,10 @@
         });
       }
 
-      if (test.output) {
+      if (test.output &&
+          context.opSupportLimits()
+              .instanceNormalization.input.dataTypes.includes(
+                  test.input.dataType)) {
         const output = builder.instanceNormalization(input, test.options);
         assert_equals(output.dataType(), test.output.dataType);
         assert_array_equals(output.shape(), test.output.dimensions);
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/layerNormalization.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/layerNormalization.https.any.js
index 50e48cc..2eff1688 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/layerNormalization.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/layerNormalization.https.any.js
@@ -43,6 +43,7 @@
     name: '[layerNormalization] Test when the input data type is float16.',
     input: {dataType: 'float16', dimensions: []},
     output: {dataType: 'float16', dimensions: []},
+    options: {label}
   },
   {
     name: '[layerNormalization] Test with given axes.',
@@ -213,7 +214,9 @@
         });
       }
 
-      if (test.output) {
+      if (test.output &&
+          context.opSupportLimits().layerNormalization.input.dataTypes.includes(
+              test.input.dataType)) {
         const output = builder.layerNormalization(input, test.options);
         assert_equals(output.dataType(), test.output.dataType);
         assert_array_equals(output.shape(), test.output.dimensions);
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/lstm.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/lstm.https.any.js
index c0d1c51..5d4f7ee6 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/lstm.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/lstm.https.any.js
@@ -325,7 +325,9 @@
         }
       }
 
-      if (test.outputs) {
+      if (test.outputs &&
+          context.opSupportLimits().lstm.input.dataTypes.includes(
+              test.input.dataType)) {
         const outputs = builder.lstm(
             input, weight, recurrentWeight, test.steps, test.hiddenSize,
             options);
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/lstmCell.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/lstmCell.https.any.js
index eb00ace..59f81fa 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/lstmCell.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/lstmCell.https.any.js
@@ -564,7 +564,9 @@
         }
       }
 
-      if (test.outputs) {
+      if (test.outputs &&
+          context.opSupportLimits().lstmCell.input.dataTypes.includes(
+              test.input.dataType)) {
         const outputs = builder.lstmCell(
             input, weight, recurrentWeight, hiddenState, cellState,
             test.hiddenSize, options);
diff --git a/third_party/blink/web_tests/fast/events/inputevents/inputevent-spellcheck.html b/third_party/blink/web_tests/fast/events/inputevents/inputevent-spellcheck.html
index 145d7ad..17f4fb6 100644
--- a/third_party/blink/web_tests/fast/events/inputevents/inputevent-spellcheck.html
+++ b/third_party/blink/web_tests/fast/events/inputevents/inputevent-spellcheck.html
@@ -163,4 +163,43 @@
     },
     '<textarea id="ta">apple| </textarea>');
 }, 'spellcheck-replace-in-textarea');
+
+test(() => {
+  assert_not_equals(window.internals,
+                    undefined,
+                    'This test requires internals.');
+
+  assert_selection(
+    '<div contenteditable id="editable">|appla^ </div>',
+    selection => {
+      const document = selection.document;
+      internals.setMarker(document, selection.getRangeAt(0), 'Spelling');
+      const editable = document.getElementById('editable');
+
+      let beforeinputDispatched = false;
+      editable.addEventListener('beforeinput', (event) => {
+        beforeinputDispatched = true;
+        editable.removeChild(editable.firstChild);
+        let newContent = document.createTextNode('Some random text');
+        editable.appendChild(newContent);
+        let range = document.createRange();
+        range.setStart(newContent, 16);
+        range.setEnd(newContent, 16);
+        let selection = window.getSelection();
+        selection.removeAllRanges();
+        selection.addRange(range);
+      });
+
+      let inputDispatched = false;
+      editable.addEventListener('input', (event) => {
+        inputDispatched = true;
+      });
+
+      internals.replaceMisspelled(document, 'apple');
+
+      assert_true(beforeinputDispatched);
+      assert_false(inputDispatched);
+    },
+    '<div contenteditable id="editable">|Some random text</div>');
+}, 'spellcheck-replace-with-beforeinput-change-value');
 </script>
diff --git a/third_party/blink/web_tests/fast/scrolling/scroll-animation-on-by-default.html b/third_party/blink/web_tests/fast/scrolling/scroll-animation-on-by-default.html
index ab72f74d..12be87d 100644
--- a/third_party/blink/web_tests/fast/scrolling/scroll-animation-on-by-default.html
+++ b/third_party/blink/web_tests/fast/scrolling/scroll-animation-on-by-default.html
@@ -48,9 +48,16 @@
     const y = window.innerHeight / 2;
     const deltaX = 0;
     const deltaY = 1000;
+    const gestureScroll =
+        smoothScrollWithXY(
+            deltaX, deltaY, x, y,
+            GestureSourceType.MOUSE_INPUT,
+            SPEED_INSTANT, false /* precise_scrolling_deltas */,
+            false /* scroll_by_page */, true /* cursor_visible */,
+            false /* scroll_by_percentage */);
     return Promise.all([
       animatedScrollPromise(document),
-      wheelScroll(x, y, deltaX, deltaY)
+      gestureScroll
     ]);
   }, "Test mouse wheel tick is animated.");
 }
diff --git a/third_party/blink/web_tests/fast/webgl/vertex-attrib-unrestricted-sequence-expected.txt b/third_party/blink/web_tests/fast/webgl/vertex-attrib-unrestricted-sequence-expected.txt
new file mode 100644
index 0000000..72d6eaa
--- /dev/null
+++ b/third_party/blink/web_tests/fast/webgl/vertex-attrib-unrestricted-sequence-expected.txt
@@ -0,0 +1,9 @@
+PASS 
+        const canvas = document.getElementById("canvas");
+        const gl = canvas.getContext("webgl");
+        gl.vertexAttrib1fv(0, [NaN])
+     did not throw exception.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/blink/web_tests/fast/webgl/vertex-attrib-unrestricted-sequence.html b/third_party/blink/web_tests/fast/webgl/vertex-attrib-unrestricted-sequence.html
new file mode 100644
index 0000000..b5ae571
--- /dev/null
+++ b/third_party/blink/web_tests/fast/webgl/vertex-attrib-unrestricted-sequence.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<body>
+<canvas id="canvas" width="10" height="10"></canvas>
+<script src="../../resources/js-test.js"></script>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+}
+
+function test() {
+    shouldNotThrow(`
+        const canvas = document.getElementById("canvas");
+        const gl = canvas.getContext("webgl");
+        gl.vertexAttrib1fv(0, [NaN])
+    `);
+}
+
+test();
+
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 3007c04..a3caf93 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -57,6 +57,10 @@
     attribute @@toStringTag
     getter available
     method constructor
+    method supportsFormat
+    method supportsInputLanguage
+    method supportsLength
+    method supportsType
 interface AISummarizerFactory
     attribute @@toStringTag
     method capabilities
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt
deleted file mode 100644
index 2ea65e6..0000000
--- a/third_party/blink/web_tests/platform/linux/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Timing of fullscreenchange and resize events
-  assert_array_equals: event order lengths differ, expected array ["resize", "fullscreenchange"] length 2, got ["fullscreenchange"] length 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/bufferSubData-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/bufferSubData-expected.txt
deleted file mode 100644
index 908c399..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/bufferSubData-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] bufferSubData
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/compressedTexImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/compressedTexImage2D-expected.txt
deleted file mode 100644
index 239a2cb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/compressedTexImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] compressedTexImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/compressedTexSubImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/compressedTexSubImage2D-expected.txt
deleted file mode 100644
index 704e64a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/compressedTexSubImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] compressedTexSubImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/texImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/texImage2D-expected.txt
deleted file mode 100644
index 3630e6c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/texImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/texSubImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/texSubImage2D-expected.txt
deleted file mode 100644
index 6b27ed439..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/texSubImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texSubImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/uniformMatrixNfv-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/uniformMatrixNfv-expected.txt
deleted file mode 100644
index 14cbc0a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webgl/uniformMatrixNfv-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Should not throw for 2
-  assert_true: Should be able to get a context. expected true got false
-[FAIL] Should not throw for 3
-  assert_true: Should be able to get a context. expected true got false
-[FAIL] Should not throw for 4
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/webgl/texImage-imageBitmap-from-imageData-resize-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/webgl/texImage-imageBitmap-from-imageData-resize-expected.png
deleted file mode 100644
index 99586a4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/webgl/texImage-imageBitmap-from-imageData-resize-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/printing/offscreencanvas-webgl-printing-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/printing/offscreencanvas-webgl-printing-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/printing/offscreencanvas-webgl-printing-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
deleted file mode 100644
index 6c933a31..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
deleted file mode 100644
index 4421a43f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
deleted file mode 100644
index 9856dea..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures lighting estimation feature works when enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures lighting estimation feature works when enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
deleted file mode 100644
index faa17a0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
deleted file mode 100644
index a58b2d4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
deleted file mode 100644
index 4e63def..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Immersive session ends if data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Immersive session ends if data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
deleted file mode 100644
index 863716d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
deleted file mode 100644
index 1fdff744..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
deleted file mode 100644
index c5266d7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
deleted file mode 100644
index d525db8..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Blink appropriately reports when frames are throttled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Blink appropriately reports when frames are throttled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
deleted file mode 100644
index 46f6739..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
deleted file mode 100644
index eadb6da..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that xrview.projection being detached doesn't cause a crash.
-  promise_test: Unhandled rejection with value: object "TypeError: Failed to construct 'XRWebGLLayer': The provided value is not of type '(WebGL2RenderingContext or WebGLRenderingContext)'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/printing/offscreencanvas-webgl-printing-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/printing/offscreencanvas-webgl-printing-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/printing/offscreencanvas-webgl-printing-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
deleted file mode 100644
index 6c933a31..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
deleted file mode 100644
index 4421a43f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
deleted file mode 100644
index 9856dea..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures lighting estimation feature works when enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures lighting estimation feature works when enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
deleted file mode 100644
index faa17a0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
deleted file mode 100644
index a58b2d4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
deleted file mode 100644
index 4e63def..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Immersive session ends if data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Immersive session ends if data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
deleted file mode 100644
index 863716d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
deleted file mode 100644
index 1fdff744..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
deleted file mode 100644
index c5266d7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
deleted file mode 100644
index d525db8..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Blink appropriately reports when frames are throttled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Blink appropriately reports when frames are throttled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
deleted file mode 100644
index 46f6739..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
deleted file mode 100644
index eadb6da..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that xrview.projection being detached doesn't cause a crash.
-  promise_test: Unhandled rejection with value: object "TypeError: Failed to construct 'XRWebGLLayer': The provided value is not of type '(WebGL2RenderingContext or WebGLRenderingContext)'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.png
deleted file mode 100644
index b895392c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.txt
deleted file mode 100644
index 11dc479..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/draws-content/webgl-background-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/draws-content/webgl-background-layer-expected.txt
deleted file mode 100644
index f0943201..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/draws-content/webgl-background-layer-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/draws-content/webgl-simple-background-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/draws-content/webgl-simple-background-expected.txt
deleted file mode 100644
index 3f00f61..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/draws-content/webgl-simple-background-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'enable')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.png
deleted file mode 100644
index 8f0ade3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-background-color-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-background-color-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-background-color-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-copy-image-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-copy-image-expected.txt
deleted file mode 100644
index 88105c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-copy-image-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Test requires WebGL
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-no-alpha-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-no-alpha-expected.png
deleted file mode 100644
index b768272..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-no-alpha-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-no-alpha-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-no-alpha-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-no-alpha-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.png
deleted file mode 100644
index b5daa85..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-reflection-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-reflection-expected.png
deleted file mode 100644
index 7b3c43e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-reflection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-reflection-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-reflection-expected.txt
deleted file mode 100644
index f49f29d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-reflection-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createShader')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-repaint-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-repaint-expected.png
deleted file mode 100644
index b5daa85..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-repaint-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-repaint-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-repaint-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/compositing/webgl/webgl-repaint-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/OffscreenCanvas-2d-drawImage-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/OffscreenCanvas-2d-drawImage-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/OffscreenCanvas-2d-drawImage-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/bug1283434-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/bug1283434-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/bug1283434-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/canvas-createImageBitmap-webgl-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/canvas-createImageBitmap-webgl-expected.txt
deleted file mode 100644
index e9436f2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/canvas-createImageBitmap-webgl-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-Check the imageBitmap of webgl.
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/webgl/create-query-crash-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/webgl/create-query-crash-expected.txt
deleted file mode 100644
index e20643b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/webgl/create-query-crash-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createQuery')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/webgl/webgl-with-neg-outline-offset-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/webgl/webgl-with-neg-outline-offset-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/canvas/webgl/webgl-with-neg-outline-offset-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/canvas-getContext-crash-fragment-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/canvas-getContext-crash-fragment-expected.txt
deleted file mode 100644
index f505335..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/canvas-getContext-crash-fragment-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Tests that canvas can create a webgl context in a template
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Canvas failed in creating a webgl context in a template.
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/canvas-resize-crash-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/canvas-resize-crash-expected.txt
deleted file mode 100644
index def5a02..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/canvas-resize-crash-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Tests that canvas does not crash on resize.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS Canvas did not crash on resize.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Unable to fetch WebGL rendering context for Canvas
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/canvas-toDataURL-premul-overflow-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/canvas-toDataURL-premul-overflow-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/canvas-toDataURL-premul-overflow-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/compressed-tex-image-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/compressed-tex-image-expected.txt
deleted file mode 100644
index 443f0712..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/compressed-tex-image-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This test ensures WebGL implementations correctly implement compressedTexImage2D and compressedTexSubImage2D.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-
-FAIL Unable to fetch WebGL rendering context for Canvas
-FAIL context does not exist
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/context-gc-custom-properties-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/context-gc-custom-properties-expected.txt
deleted file mode 100644
index 8c05ddd..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/context-gc-custom-properties-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Verify that the custom properties on a WebGL rendering context object are retained across GCs.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL Unexpected error: Uncaught Unable to fetch WebGL rendering context for Canvas
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/offscreenCanvas-transferToImageBitmap-texImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/offscreenCanvas-transferToImageBitmap-texImage2D-expected.txt
deleted file mode 100644
index c9b17c9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/offscreenCanvas-transferToImageBitmap-texImage2D-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/pixelated-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/pixelated-expected.png
deleted file mode 100644
index b5daa85..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/pixelated-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/pixelated-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/pixelated-expected.txt
deleted file mode 100644
index f49f29d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/pixelated-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createShader')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/renderer-and-vendor-strings-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/renderer-and-vendor-strings-expected.txt
deleted file mode 100644
index 0caea48..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/renderer-and-vendor-strings-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Verifies the contents of the RENDERER and VENDOR strings to avoid regressions.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/shader-deleted-by-accessor-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/shader-deleted-by-accessor-expected.txt
deleted file mode 100644
index 23729c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/shader-deleted-by-accessor-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Verifies that WebGLRenderingContext::getAttachedShaders doesn't crash when an accessor property is defined on Array.prototype.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/tex-sub-image-cube-maps-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/tex-sub-image-cube-maps-expected.txt
deleted file mode 100644
index 571dc0d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/tex-sub-image-cube-maps-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createBuffer')
-Checks texSubImage2D with cube map textures
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-
-FAIL Unable to fetch WebGL rendering context for Canvas
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-blob-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-blob-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-blob-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-canvas-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-canvas-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-blob-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-blob-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-blob-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-canvas-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-canvas-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-image-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-image-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-image-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-imageData-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-imageData-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-imageData-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt
deleted file mode 100644
index 8dd3516b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageData-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageData-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-imageData-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-video-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-video-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-from-video-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-structured-clone-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-structured-clone-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-structured-clone-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-transferable-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-transferable-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/texImage-imageBitmap-transferable-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-composite-modes-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-composite-modes-expected.png
deleted file mode 100644
index b6031fa..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-composite-modes-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-composite-modes-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-composite-modes-expected.txt
deleted file mode 100644
index b70ff1bf..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-composite-modes-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'VERTEX_SHADER')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-context-attributes-default-value-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-context-attributes-default-value-expected.txt
deleted file mode 100644
index 998c659..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-context-attributes-default-value-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Testing default value:
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-exceptions-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-exceptions-expected.txt
deleted file mode 100644
index 63c66eb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-exceptions-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-layer-update-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-layer-update-expected.png
deleted file mode 100644
index f710c81..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-layer-update-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-layer-update-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-layer-update-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-layer-update-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-sharedarraybuffer-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-sharedarraybuffer-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/webgl/webgl-sharedarraybuffer-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/http/tests/security/webgl-cross-origin-ImageBitmap-blocked-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/http/tests/security/webgl-cross-origin-ImageBitmap-blocked-expected.txt
deleted file mode 100644
index 890bf130..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/http/tests/security/webgl-cross-origin-ImageBitmap-blocked-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createTexture')
-WebGL's tex(Sub)Image2D should throw a SecurityError exception when the ImageBitmap is not origin clean.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'createTexture')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/printing/offscreencanvas-webgl-printing-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/printing/offscreencanvas-webgl-printing-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/printing/offscreencanvas-webgl-printing-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/printing/webgl-repeated-printing-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/printing/webgl-repeated-printing-expected.txt
deleted file mode 100644
index 88105c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/printing/webgl-repeated-printing-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Test requires WebGL
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/printing/webgl-repeated-printing-preservedrawingbuffer-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/printing/webgl-repeated-printing-preservedrawingbuffer-expected.txt
deleted file mode 100644
index 88105c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/printing/webgl-repeated-printing-preservedrawingbuffer-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Test requires WebGL
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
deleted file mode 100644
index 6c933a31..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
deleted file mode 100644
index 4421a43f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
deleted file mode 100644
index 9856dea..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures lighting estimation feature works when enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures lighting estimation feature works when enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
deleted file mode 100644
index faa17a0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
deleted file mode 100644
index a58b2d4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
deleted file mode 100644
index 4e63def..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Immersive session ends if data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Immersive session ends if data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
deleted file mode 100644
index 863716d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
deleted file mode 100644
index 1fdff744..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
deleted file mode 100644
index c5266d7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
deleted file mode 100644
index d525db8..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Blink appropriately reports when frames are throttled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Blink appropriately reports when frames are throttled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
deleted file mode 100644
index 46f6739..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
deleted file mode 100644
index eadb6da..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that xrview.projection being detached doesn't cause a crash.
-  promise_test: Unhandled rejection with value: object "TypeError: Failed to construct 'XRWebGLLayer': The provided value is not of type '(WebGL2RenderingContext or WebGLRenderingContext)'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.png
deleted file mode 100644
index 74d464c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.txt
deleted file mode 100644
index 11dc479..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/draws-content/webgl-background-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/draws-content/webgl-background-layer-expected.txt
deleted file mode 100644
index f0943201..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/draws-content/webgl-background-layer-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/draws-content/webgl-simple-background-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/draws-content/webgl-simple-background-expected.txt
deleted file mode 100644
index 3f00f61..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/draws-content/webgl-simple-background-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'enable')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.png
deleted file mode 100644
index 89efc62..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-background-color-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-background-color-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-background-color-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-copy-image-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-copy-image-expected.txt
deleted file mode 100644
index 88105c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-copy-image-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Test requires WebGL
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-no-alpha-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-no-alpha-expected.png
deleted file mode 100644
index b768272..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-no-alpha-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-no-alpha-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-no-alpha-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-no-alpha-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.png
deleted file mode 100644
index b5daa85..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-reflection-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-reflection-expected.png
deleted file mode 100644
index 74caef3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-reflection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-reflection-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-reflection-expected.txt
deleted file mode 100644
index f49f29d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-reflection-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createShader')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-repaint-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-repaint-expected.png
deleted file mode 100644
index b5daa85..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-repaint-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-repaint-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-repaint-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/compositing/webgl/webgl-repaint-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext-expected.txt
deleted file mode 100644
index 09f603d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that getContext with supported string returns correct results
-  assert_true: expected true got false
-[FAIL] Test that getContext twice with different context type returns null the second time
-  assert_equals: expected null but got object "[object OffscreenCanvasRenderingContext2D]"
-[FAIL] Test that webglcontext.canvas should return the original OffscreenCanvas
-  Cannot read properties of null (reading 'canvas')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker-expected.txt
deleted file mode 100644
index 09f603d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that getContext with supported string returns correct results
-  assert_true: expected true got false
-[FAIL] Test that getContext twice with different context type returns null the second time
-  assert_equals: expected null but got object "[object OffscreenCanvasRenderingContext2D]"
-[FAIL] Test that webglcontext.canvas should return the original OffscreenCanvas
-  Cannot read properties of null (reading 'canvas')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt
deleted file mode 100644
index a699d01..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that transferToImageBitmap returns an ImageBitmap with correct width and height
-  Failed to execute 'transferToImageBitmap' on 'OffscreenCanvas': Cannot transfer an ImageBitmap from an OffscreenCanvas with no context
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt
deleted file mode 100644
index dd10ee11..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that transfer an OffscreenCanvas that has a webgl context throws exception in a worker.
-  assert_true: expected true got object "[object OffscreenCanvas]"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/paint-timing/with-first-paint/first-contentful-canvas-webgl2-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/paint-timing/with-first-paint/first-contentful-canvas-webgl2-expected.txt
deleted file mode 100644
index 786f52d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/paint-timing/with-first-paint/first-contentful-canvas-webgl2-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[PRECONDITION_FAILED] First contentful paint fires due to webgl2 canvas render.
-  WebGL 2 Canvas isn't supported.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/video-rvfc/request-video-frame-callback-before-xr-session.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/video-rvfc/request-video-frame-callback-before-xr-session.https-expected.txt
deleted file mode 100644
index 9033112..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/video-rvfc/request-video-frame-callback-before-xr-session.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Make sure video.rVFC works during a non-immersive session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Make sure video.rVFC works during a non-immersive session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Make sure video.rVFC works during an immersive session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Make sure video.rVFC works during an immersive session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/video-rvfc/request-video-frame-callback-during-xr-session.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/video-rvfc/request-video-frame-callback-during-xr-session.https-expected.txt
deleted file mode 100644
index 1f555b73..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/video-rvfc/request-video-frame-callback-during-xr-session.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Make sure video.rVFC callbacks started during an immersive session continue after it ends - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Make sure video.rVFC callbacks started during an immersive session continue after it ends - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webcodecs/videoFrame-texImage.any-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webcodecs/videoFrame-texImage.any-expected.txt
deleted file mode 100644
index 9f2bac9f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webcodecs/videoFrame-texImage.any-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texImage2D with 48x36 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texSubImage2D with 48x36 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texImage2D with 480x360 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texSubImage2D with 480x360 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texImage2D with a closed VideoFrame.
-  Cannot read properties of null (reading 'texImage2D')
-[FAIL] texSubImage2D with a closed VideoFrame.
-  Cannot read properties of null (reading 'texSubImage2D')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webcodecs/videoFrame-texImage.any.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webcodecs/videoFrame-texImage.any.worker-expected.txt
deleted file mode 100644
index 9f2bac9f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webcodecs/videoFrame-texImage.any.worker-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texImage2D with 48x36 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texSubImage2D with 48x36 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texImage2D with 480x360 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texSubImage2D with 480x360 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texImage2D with a closed VideoFrame.
-  Cannot read properties of null (reading 'texImage2D')
-[FAIL] texSubImage2D with a closed VideoFrame.
-  Cannot read properties of null (reading 'texSubImage2D')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/bufferSubData-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/bufferSubData-expected.txt
deleted file mode 100644
index 908c399..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/bufferSubData-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] bufferSubData
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/compressedTexImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/compressedTexImage2D-expected.txt
deleted file mode 100644
index 239a2cb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/compressedTexImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] compressedTexImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/compressedTexSubImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/compressedTexSubImage2D-expected.txt
deleted file mode 100644
index 704e64a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/compressedTexSubImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] compressedTexSubImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/texImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/texImage2D-expected.txt
deleted file mode 100644
index 3630e6c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/texImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/texSubImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/texSubImage2D-expected.txt
deleted file mode 100644
index 6b27ed439..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/texSubImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texSubImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/uniformMatrixNfv-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/uniformMatrixNfv-expected.txt
deleted file mode 100644
index 14cbc0a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webgl/uniformMatrixNfv-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Should not throw for 2
-  assert_true: Should be able to get a context. expected true got false
-[FAIL] Should not throw for 3
-  assert_true: Should be able to get a context. expected true got false
-[FAIL] Should not throw for 4
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_create_move.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_create_move.https-expected.txt
deleted file mode 100644
index 77aeb33..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_create_move.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures free-floating anchor move gets propagated to anchor poses - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor move gets propagated to anchor poses - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_delay_creation.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_delay_creation.https-expected.txt
deleted file mode 100644
index 2f825e6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_delay_creation.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures free-floating anchor creation with delayed success is handled correctly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor creation with delayed success is handled correctly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor creation with delayed failure is handled correctly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor creation with delayed failure is handled correctly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_failure.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_failure.https-expected.txt
deleted file mode 100644
index 170dc358..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_failure.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures free-floating anchor creation failure is handled correctly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor creation failure is handled correctly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_pause_resume_stop.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_pause_resume_stop.https-expected.txt
deleted file mode 100644
index e7b94f1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_pause_resume_stop.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures free-floating anchor state changes get propagated - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor state changes get propagated - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_getAnchors.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_getAnchors.https-expected.txt
deleted file mode 100644
index eccfc6f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_getAnchors.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame's trackedAnchors is empty when the feature was not requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was not requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_states.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_states.https-expected.txt
deleted file mode 100644
index 5a8b6ba6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/anchors/ar_anchor_states.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Anchor creation succeeds if the feature was requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation succeeds if the feature was requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation fails if the feature was not requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation fails if the feature was not requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation fails if the feature was requested but the session already ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation fails if the feature was requested but the session already ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/ar-module/xrDevice_requestSession_immersive-ar.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/ar-module/xrDevice_requestSession_immersive-ar.https-expected.txt
deleted file mode 100644
index e6e7bd2f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/ar-module/xrDevice_requestSession_immersive-ar.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests requestSession accepts immersive-ar mode - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts immersive-ar mode - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/ar-module/xrSession_environmentBlendMode.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/ar-module/xrSession_environmentBlendMode.https-expected.txt
deleted file mode 100644
index 33b3a708..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/ar-module/xrSession_environmentBlendMode.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests environmentBlendMode for an AR device - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests environmentBlendMode for an AR device - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests environmentBlendMode for a VR device - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests environmentBlendMode for a VR device - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/ar-module/xrSession_interactionMode.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/ar-module/xrSession_interactionMode.https-expected.txt
deleted file mode 100644
index 828c93a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/ar-module/xrSession_interactionMode.https-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests interactionMode for an VR_HMD_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an VR_HMD_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an VR_SCREEN_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an VR_SCREEN_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an AR_HMD_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an AR_HMD_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an AR_SCREEN_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an AR_SCREEN_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for a INLINE_SCREEN_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for a INLINE_SCREEN_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/camera-access/xrCamera_resolution.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/camera-access/xrCamera_resolution.https-expected.txt
deleted file mode 100644
index 8201f37..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/camera-access/xrCamera_resolution.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRCamera object is present and carries expected dimensions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRCamera object is present and carries expected dimensions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_dataUnavailable.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_dataUnavailable.https-expected.txt
deleted file mode 100644
index 881161c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_dataUnavailable.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures depth data is not available when cleared in the controller, `cpu-optimized` - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures depth data is not available when cleared in the controller, `cpu-optimized` - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_inactiveFrame.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_inactiveFrame.https-expected.txt
deleted file mode 100644
index a0f3b74..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_inactiveFrame.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures getDepthInformation() throws when not run in an active frame, `cpu-optimized` - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures getDepthInformation() throws when not run in an active frame, `cpu-optimized` - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_incorrectUsage.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_incorrectUsage.https-expected.txt
deleted file mode 100644
index 68d1931..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_incorrectUsage.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures XRWebGLDepthInformation is not obtainable in `cpu-optimized` usage mode - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures XRWebGLDepthInformation is not obtainable in `cpu-optimized` usage mode - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_luminance_alpha_dataValid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_luminance_alpha_dataValid.https-expected.txt
deleted file mode 100644
index cc04207..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_luminance_alpha_dataValid.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures depth data is returned and values match expectation, cpu-optimized, luminance-alpha. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures depth data is returned and values match expectation, cpu-optimized, luminance-alpha. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_staleView.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_staleView.https-expected.txt
deleted file mode 100644
index d3bfaa68..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_staleView.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures getDepthInformation() throws when run with stale XRView, `cpu-optimized` - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures getDepthInformation() throws when run with stale XRView, `cpu-optimized` - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/depth_sensing_notEnabled.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/depth_sensing_notEnabled.https-expected.txt
deleted file mode 100644
index 2f85c8c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/depth-sensing/depth_sensing_notEnabled.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.getDepthInformation() rejects if depth sensing is not enabled on a session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getDepthInformation() rejects if depth sensing is not enabled on a session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLBinding.getDepthInformation() rejects if depth sensing is not enabled on a session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLBinding.getDepthInformation() rejects if depth sensing is not enabled on a session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay.https-expected.txt
deleted file mode 100644
index 374b44b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay.https-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures DOM Overlay feature works for immersive-ar, body element - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay feature works for immersive-ar, body element - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay feature works for immersive-ar, div element - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay feature works for immersive-ar, div element - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay input deduplication works - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay input deduplication works - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay Fullscreen API doesn't change DOM overlay - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay Fullscreen API doesn't change DOM overlay - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay interactions on cross origin iframe are ignored - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay interactions on cross origin iframe are ignored - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay_hit_test.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay_hit_test.https-expected.txt
deleted file mode 100644
index 9b2b87de..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay_hit_test.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures DOM Overlay interactions on cross origin iframe do not cause hit test results to come up - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay interactions on cross origin iframe do not cause hit test results to come up - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_input_source_recreation.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_input_source_recreation.https-expected.txt
deleted file mode 100644
index 10eaec6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_input_source_recreation.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Input sources are re-created when handedness or target ray mode changes - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Input sources are re-created when handedness or target ray mode changes - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_input_sources_change.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_input_sources_change.https-expected.txt
deleted file mode 100644
index f19b283..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_input_sources_change.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Transient input sources fire events in the right order - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient input sources fire events in the right order - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_referenceSpace_reset_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_referenceSpace_reset_immersive.https-expected.txt
deleted file mode 100644
index b47301e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_referenceSpace_reset_immersive.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession resetpose from a device properly fires off the right events for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession resetpose from a device properly fires off the right events for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_session_select.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_session_select.https-expected.txt
deleted file mode 100644
index 2e23352..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_session_select.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources primary input presses properly fires off the right events - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources primary input presses properly fires off the right events - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_session_select_subframe.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_session_select_subframe.https-expected.txt
deleted file mode 100644
index 24d4ea7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_session_select_subframe.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures that an XRInputSources primary input being pressed and released in the space of a single frame properly fires off the right events - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures that an XRInputSources primary input being pressed and released in the space of a single frame properly fires off the right events - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_session_squeeze.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_session_squeeze.https-expected.txt
deleted file mode 100644
index 2e23352..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/events_session_squeeze.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources primary input presses properly fires off the right events - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources primary input presses properly fires off the right events - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/exclusive_requestFrame_nolayer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/exclusive_requestFrame_nolayer.https-expected.txt
deleted file mode 100644
index 82ad74f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/exclusive_requestFrame_nolayer.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession requestAnimationFrame must fail if the session has no baseLayer for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame must fail if the session has no baseLayer for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame must fail if the session has no baseLayer for non immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame must fail if the session has no baseLayer for non immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_disconnect.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_disconnect.https-expected.txt
deleted file mode 100644
index f01425c0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_disconnect.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] WebXR InputSource's gamepad gets disconnected when the input source is removed - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] WebXR InputSource's gamepad gets disconnected when the input source is removed - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_input_registered.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_input_registered.https-expected.txt
deleted file mode 100644
index f744bb1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_input_registered.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] WebXR InputSource's gamepad properly registers input - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] WebXR InputSource's gamepad properly registers input - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/getInputPose_handedness.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/getInputPose_handedness.https-expected.txt
deleted file mode 100644
index 752041a2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/getInputPose_handedness.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources properly communicate their handedness - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources properly communicate their handedness - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/getInputPose_pointer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/getInputPose_pointer.https-expected.txt
deleted file mode 100644
index 50313c86..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/getInputPose_pointer.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources with a target ray mode of 'tracked-pointer' properly communicate their poses - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources with a target ray mode of 'tracked-pointer' properly communicate their poses - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/getViewerPose_emulatedPosition.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/getViewerPose_emulatedPosition.https-expected.txt
deleted file mode 100644
index 3957dd38..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/getViewerPose_emulatedPosition.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame getViewerPose has emulatedPosition set properly. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose has emulatedPosition set properly. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_source_cancel.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_source_cancel.https-expected.txt
deleted file mode 100644
index 97b8bb74..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_source_cancel.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures hit test source cancellation works when the session has not ended. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation works when the session has not ended. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation works when the session has not ended. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation works when the session has not ended. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation works when the session has ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation works when the session has ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation works when the session has ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation works when the session has ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_inputSources.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_inputSources.https-expected.txt
deleted file mode 100644
index b790f768..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_inputSources.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - no move - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - no move - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - after move - no results - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - after move - no results - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - after move - 1 result - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - after move - 1 result - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_refSpaces.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_refSpaces.https-expected.txt
deleted file mode 100644
index dfa99f7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_refSpaces.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures subscription to hit test works with viewer space - straight ahead - plane - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with viewer space - straight ahead - plane - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with viewer space - straight up - no results - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with viewer space - straight up - no results - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with local space - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with local space - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with local-floor space - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with local-floor space - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_regular.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_regular.https-expected.txt
deleted file mode 100644
index c7b7cb6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_regular.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Hit test subscription succeeds if the feature was requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription succeeds if the feature was requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription fails if the feature was not requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription fails if the feature was not requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription fails if the feature was requested but the session already ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription fails if the feature was requested but the session already ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_transient.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_transient.https-expected.txt
deleted file mode 100644
index 597ed5b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_transient.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Transient hit test subscription succeeds if the feature was requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient hit test subscription succeeds if the feature was requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient hit test subscription fails if the feature was not requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient hit test subscription fails if the feature was not requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient test subscription fails if the feature was requested but the session already ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient test subscription fails if the feature was requested but the session already ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_transientInputSources.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_transientInputSources.https-expected.txt
deleted file mode 100644
index 8428388..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_transientInputSources.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - no move - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - no move - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - after move - no results - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - after move - no results - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - after move - 1 result - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - after move - 1 result - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_unlocalizable.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_unlocalizable.https-expected.txt
deleted file mode 100644
index 7f33807..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_unlocalizable.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures hit test result returns null pose w/unlocalizable space - viewer space - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test result returns null pose w/unlocalizable space - viewer space - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/layers/xrSession_updateRenderState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/layers/xrSession_updateRenderState.https-expected.txt
deleted file mode 100644
index b832bb2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/layers/xrSession_updateRenderState.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure XRSession throws appropriate errors when updating render state without layers feature enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure XRSession throws appropriate errors when updating render state without layers feature enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure XRSession throws appropriate errors when updating render state with layers feature enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure XRSession throws appropriate errors when updating render state with layers feature enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/layers/xrWebGLBinding_constructor.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/layers/xrWebGLBinding_constructor.https-expected.txt
deleted file mode 100644
index a13a630..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/layers/xrWebGLBinding_constructor.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure that XRWebGLBinding's constructor throws appropriate errors using webgl
-  assert_equals: Should get InvalidStateError for creating with inline session. expected "InvalidStateError" but got "TypeError"
-[FAIL] Ensure that XRWebGLBinding's constructor throws appropriate errors using webgl2
-  assert_equals: Should get InvalidStateError for creating with inline session. expected "InvalidStateError" but got "TypeError"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_oldSession.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_oldSession.https-expected.txt
deleted file mode 100644
index 9625c30..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_oldSession.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] getLightEstimate rejects if probe is from wrong session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] getLightEstimate rejects if probe is from wrong session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_staleFrame.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_staleFrame.https-expected.txt
deleted file mode 100644
index 54c171e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_staleFrame.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Cannot get XrLightEstimate from stale frame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Cannot get XrLightEstimate from stale frame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_valid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_valid.https-expected.txt
deleted file mode 100644
index 2130398..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_valid.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Can get XRLightEstimates during frame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Can get XRLightEstimates during frame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_ended.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_ended.https-expected.txt
deleted file mode 100644
index 2a64557..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_ended.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] getLightProbe rejects on an ended session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] getLightProbe rejects on an ended session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_notEnabled.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_notEnabled.https-expected.txt
deleted file mode 100644
index 4335f4cb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_notEnabled.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] getLightProbe rejects if not enabled on session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] getLightProbe rejects if not enabled on session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_valid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_valid.https-expected.txt
deleted file mode 100644
index 538b2742..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_valid.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Can create valid XRLightProbe objects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Can create valid XRLightProbe objects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https-expected.txt
deleted file mode 100644
index 3565636..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that getReflectionCubeMap returns or throws appropriately without a reflection map. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Test that getReflectionCubeMap returns or throws appropriately without a reflection map. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/navigator_xr_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/navigator_xr_sameObject.https-expected.txt
deleted file mode 100644
index 9d8a5844..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/navigator_xr_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Navigator.xr meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Navigator.xr meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/render_state_update.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/render_state_update.https-expected.txt
deleted file mode 100644
index 1d9c4f0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/render_state_update.https-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] updateRenderState handles appropriately ended sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately ended sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately baseLayers created with different sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately baseLayers created with different sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately immersive sessions with specified inlineVerticalFieldOfView - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately immersive sessions with specified inlineVerticalFieldOfView - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately XRRenderStateInit with no params - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately XRRenderStateInit with no params - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately XRRenderStateInit params - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately XRRenderStateInit params - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState clamps appropriately near/far clipping planes - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState clamps appropriately near/far clipping planes - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/render_state_vertical_fov_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/render_state_vertical_fov_immersive.https-expected.txt
deleted file mode 100644
index 7175625..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/render_state_vertical_fov_immersive.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] inlineVerticalFieldOfView is set appropriately on immersively sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] inlineVerticalFieldOfView is set appropriately on immersively sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/render_state_vertical_fov_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/render_state_vertical_fov_inline.https-expected.txt
deleted file mode 100644
index faa17a0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/render_state_vertical_fov_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webGLCanvasContext_create_xrcompatible.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webGLCanvasContext_create_xrcompatible.https-expected.txt
deleted file mode 100644
index 6ffe66bd..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webGLCanvasContext_create_xrcompatible.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Creating a webgl context with no device
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] Creating a webgl2 context with no device
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] An XR-compatible webgl context can be created
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] An XR-compatible webgl2 context can be created
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_contextlost.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_contextlost.https-expected.txt
deleted file mode 100644
index 5a4dd22aa..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_contextlost.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] A lost webgl context should not be able to set xr compatibility
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] A lost webgl2 context should not be able to set xr compatibility
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_reentrant.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_reentrant.https-expected.txt
deleted file mode 100644
index 347a47af..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_reentrant.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Verify promise from a non-reentrant call to makeXRCompatible() is resolved for webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] Verify promise from a non-reentrant call to makeXRCompatible() is resolved for webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] Verify promises from reentrant calls to makeXRCompatible() are resolved for webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] Verify promises from reentrant calls to makeXRCompatible() are resolved for webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webxr_permissions_policy.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webxr_permissions_policy.https-expected.txt
deleted file mode 100644
index 4b92d2c22..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/webxr_permissions_policy.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Validate xr compatibility requests without xr-spatial-tracking policy
-  Cannot read properties of null (reading 'getContextAttributes')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrBoundedReferenceSpace_updates.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrBoundedReferenceSpace_updates.https-expected.txt
deleted file mode 100644
index 3200193..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrBoundedReferenceSpace_updates.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] 'XRBoundedReferenceSpace updates properly when the changes are applied - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] 'XRBoundedReferenceSpace updates properly when the changes are applied - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrDevice_disconnect_ends.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrDevice_disconnect_ends.https-expected.txt
deleted file mode 100644
index af204eb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrDevice_disconnect_ends.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Immersive session ends when device is disconnected - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Immersive session ends when device is disconnected - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrDevice_requestSession_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrDevice_requestSession_immersive.https-expected.txt
deleted file mode 100644
index 0a6951a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrDevice_requestSession_immersive.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests requestSession resolves when supported - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession resolves when supported - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown optionalFeatures - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown optionalFeatures - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrDevice_requestSession_optionalFeatures.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrDevice_requestSession_optionalFeatures.https-expected.txt
deleted file mode 100644
index 51dd97c14f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrDevice_requestSession_optionalFeatures.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests requestSession accepts XRSessionInit dictionary - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary with empty feature lists - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary with empty feature lists - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown strings in optionalFeatures - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown strings in optionalFeatures - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown objects in optionalFeatures - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown objects in optionalFeatures - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_getPose.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_getPose.https-expected.txt
deleted file mode 100644
index f6f532b4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_getPose.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.getPose works for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works for non-immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works for non-immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose.https-expected.txt
deleted file mode 100644
index 8211819a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame getViewerPose(refSpace) matches getPose(viewer, refSpace). - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose(refSpace) matches getPose(viewer, refSpace). - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose_identities.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose_identities.https-expected.txt
deleted file mode 100644
index 2c1c94d8..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose_identities.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame getViewerPose(viewerSpace) & getPose(space, space) return identity even during tracking loss - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose(viewerSpace) & getPose(space, space) return identity even during tracking loss - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_lifetime.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_lifetime.https-expected.txt
deleted file mode 100644
index 16c434f2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_lifetime.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame methods throw exceptions outside of the requestAnimationFrame callback for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame methods throw exceptions outside of the requestAnimationFrame callback for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame methods throw exceptions outside of the requestAnimationFrame callback for non-immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame methods throw exceptions outside of the requestAnimationFrame callback for non-immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_session_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_session_sameObject.https-expected.txt
deleted file mode 100644
index 1c4e5fd9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrFrame_session_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.session meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.session meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_add_remove.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_add_remove.https-expected.txt
deleted file mode 100644
index 4fcb43a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_add_remove.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources can be properly added and removed from the session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources can be properly added and removed from the session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_emulatedPosition.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_emulatedPosition.https-expected.txt
deleted file mode 100644
index 0c5474f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_emulatedPosition.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Poses from XRInputSource.gripSpace have emulatedPosition set properly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Poses from XRInputSource.gripSpace have emulatedPosition set properly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_getPose_targetRay_grip.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_getPose_targetRay_grip.https-expected.txt
deleted file mode 100644
index b2508e7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_getPose_targetRay_grip.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Poses between targetRaySpace and gripSpace can be obtained and behave correctly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Poses between targetRaySpace and gripSpace can be obtained and behave correctly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_profiles.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_profiles.https-expected.txt
deleted file mode 100644
index fd761739e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_profiles.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] WebXR InputSource's profiles list can be set - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] WebXR InputSource's profiles list can be set - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_sameObject.https-expected.txt
deleted file mode 100644
index c239a8f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrInputSource_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSource attributes meet [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSource attributes meet [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrPose_transform_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrPose_transform_sameObject.https-expected.txt
deleted file mode 100644
index f47f8d0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrPose_transform_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRPose.transform meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRPose.transform meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_originOffset.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_originOffset.https-expected.txt
deleted file mode 100644
index 19f0627..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_originOffset.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Updating XRReferenceSpace origin offset updates view and input matrices. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Updating XRReferenceSpace origin offset updates view and input matrices. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_originOffsetBounded.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_originOffsetBounded.https-expected.txt
deleted file mode 100644
index 31306182..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_originOffsetBounded.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Updating XRBoundedReferenceSpace origin offset updates view, input matrices, and bounds geometry. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Updating XRBoundedReferenceSpace origin offset updates view, input matrices, and bounds geometry. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_originOffset_viewer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_originOffset_viewer.https-expected.txt
deleted file mode 100644
index 7511ac1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_originOffset_viewer.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Creating XRReferenceSpace origin offset off of `viewer` space works. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Creating XRReferenceSpace origin offset off of `viewer` space works. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_relationships.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_relationships.https-expected.txt
deleted file mode 100644
index f9ed1442..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrReferenceSpace_relationships.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Bounded space, viewer space, local and local-floor space have correct poses w.r.t. each other - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Bounded space, viewer space, local and local-floor space have correct poses w.r.t. each other - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrRigidTransform_constructor.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrRigidTransform_constructor.https-expected.txt
deleted file mode 100644
index d55c86a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrRigidTransform_constructor.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRRigidTransform constructor works - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRRigidTransform constructor works - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrRigidTransform_inverse.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrRigidTransform_inverse.https-expected.txt
deleted file mode 100644
index ff813ff..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrRigidTransform_inverse.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRRigidTransform inverse works - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRRigidTransform inverse works - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrRigidTransform_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrRigidTransform_sameObject.https-expected.txt
deleted file mode 100644
index bfdcba3f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrRigidTransform_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRRigidTransform position and orientation meet [SameObject] requirements - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRRigidTransform position and orientation meet [SameObject] requirements - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame.https-expected.txt
deleted file mode 100644
index a4ca8d2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for non-immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for non-immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame_invalidhandle.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame_invalidhandle.https-expected.txt
deleted file mode 100644
index daa1261..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame_invalidhandle.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on immersive testSession - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on immersive testSession - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on non-immersive testSession - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on non-immersive testSession - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_enabledFeatures.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_enabledFeatures.https-expected.txt
deleted file mode 100644
index 2b42167..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_enabledFeatures.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Validate enabledFeatures on XRSession - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Validate enabledFeatures on XRSession - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_end.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_end.https-expected.txt
deleted file mode 100644
index 759615a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_end.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] end event fires when immersive session ends - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] end event fires when immersive session ends - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] end event fires when non-immersive session ends - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] end event fires when non-immersive session ends - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_input_events_end.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_input_events_end.https-expected.txt
deleted file mode 100644
index 166244a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_input_events_end.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Calling end during an input callback stops processing at the right time - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Calling end during an input callback stops processing at the right time - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https-expected.txt
deleted file mode 100644
index e821a16..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession requestAnimationFrame calls the provided callback for an immersive session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame calls the provided callback for an immersive session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame calls the provided callback a non-immersive session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame calls the provided callback a non-immersive session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_data_valid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_data_valid.https-expected.txt
deleted file mode 100644
index 823eac7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_data_valid.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] RequestAnimationFrame resolves with good data - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] RequestAnimationFrame resolves with good data - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_timestamp.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_timestamp.https-expected.txt
deleted file mode 100644
index 3570005..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_timestamp.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame getViewerPose updates on the next frame for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose updates on the next frame for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose updates on the next frame for non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose updates on the next frame for non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestReferenceSpace.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestReferenceSpace.https-expected.txt
deleted file mode 100644
index 8efb091..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestReferenceSpace.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Immersive XRSession requestReferenceSpace returns expected objects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Immersive XRSession requestReferenceSpace returns expected objects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Non-immersive XRSession requestReferenceSpace returns expected objects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Non-immersive XRSession requestReferenceSpace returns expected objects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestSessionDuringEnd.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestSessionDuringEnd.https-expected.txt
deleted file mode 100644
index 5311707..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_requestSessionDuringEnd.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create new session in OnSessionEnded event - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Create new session in OnSessionEnded event - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Create mew session in end promise - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Create mew session in end promise - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_sameObject.https-expected.txt
deleted file mode 100644
index 02865c1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession attributes meet [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession attributes meet [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_viewer_referenceSpace.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_viewer_referenceSpace.https-expected.txt
deleted file mode 100644
index 5a3dc53..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_viewer_referenceSpace.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Identity reference space provides correct poses for inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Identity reference space provides correct poses for inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Identity reference space provides correct poses for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Identity reference space provides correct poses for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_visibilityState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_visibilityState.https-expected.txt
deleted file mode 100644
index 89b09ca..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrSession_visibilityState.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures that the XRSession's visibilityState is correctly reported and that the associated visibilitychange event fires. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures that the XRSession's visibilityState is correctly reported and that the associated visibilitychange event fires. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_eyes.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_eyes.https-expected.txt
deleted file mode 100644
index 3984e7a1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_eyes.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRView.eye is correct for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView.eye is correct for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView.eye is correct for non-immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView.eye is correct for non-immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_match.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_match.https-expected.txt
deleted file mode 100644
index e91a9ad..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_match.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame contains the expected views - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame contains the expected views - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_oneframeupdate.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_oneframeupdate.https-expected.txt
deleted file mode 100644
index 8d5ce4c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_oneframeupdate.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRView projection matrices update near and far depths on the next frame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView projection matrices update near and far depths on the next frame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_sameObject.https-expected.txt
deleted file mode 100644
index e2e5bec..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrView_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRView attributes meet [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView attributes meet [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrViewerPose_secondaryViews.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrViewerPose_secondaryViews.https-expected.txt
deleted file mode 100644
index f3bc0cd0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrViewerPose_secondaryViews.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Only primary views are returned if secondary views are not requested for non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Only primary views are returned if secondary views are not requested for non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Only primary views are returned if secondary views are not requested for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Only primary views are returned if secondary views are not requested for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Requesting secondary views only returns primary views for non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Requesting secondary views only returns primary views for non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Requesting secondary views returns both primary and secondary views for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Requesting secondary views returns both primary and secondary views for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrViewerPose_views_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrViewerPose_views_sameObject.https-expected.txt
deleted file mode 100644
index 93c77f0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrViewerPose_views_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRViewerPose.views meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRViewerPose.views meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrViewport_valid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrViewport_valid.https-expected.txt
deleted file mode 100644
index 39ed84b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrViewport_valid.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRViewport attributes are valid - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRViewport attributes are valid - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRViewport attributes are valid with secondary views requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRViewport attributes are valid with secondary views requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_constructor.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_constructor.https-expected.txt
deleted file mode 100644
index 7e4a6ec..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_constructor.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure that XRWebGLLayer's constructor throws appropriate errors using webgl
-  assert_unreached: Inline XRWebGLLayers should not fail when created with a context that is not XRCompatible Reached unreachable code
-[FAIL] Ensure that XRWebGLLayer's constructor throws appropriate errors using webgl2
-  assert_unreached: Inline XRWebGLLayers should not fail when created with a context that is not XRCompatible Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt
deleted file mode 100644
index a55fe90e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure a WebGL layer's framebuffer can only be drawn to inside a XR frame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure a WebGL layer's framebuffer can only be drawn to inside a XR frame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_sameObject.https-expected.txt
deleted file mode 100644
index 2cdeea9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRWebGLLayer.framebuffer meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer.framebuffer meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_scale.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_scale.https-expected.txt
deleted file mode 100644
index 0a88c47..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_scale.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure framebuffer scaling works as expected. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure framebuffer scaling works as expected. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt
deleted file mode 100644
index a45a1fb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure that the framebuffer given by the WebGL layer is opaque for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure that the framebuffer given by the WebGL layer is opaque for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure that the framebuffer given by the WebGL layer is opaque for non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure that the framebuffer given by the WebGL layer is opaque for non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https-expected.txt
deleted file mode 100644
index 49210ce..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure that the framebuffer given by the WebGL layer works with stencil for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure that the framebuffer given by the WebGL layer works with stencil for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_viewports.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_viewports.https-expected.txt
deleted file mode 100644
index 8684a5b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xrWebGLLayer_viewports.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRWebGLLayer reports a valid viewports for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for immersive sessions with secondary views requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for immersive sessions with secondary views requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for inline sessions with secondary views requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for inline sessions with secondary views requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xr_viewport_scale.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xr_viewport_scale.https-expected.txt
deleted file mode 100644
index ae458e8..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/external/wpt/webxr/xr_viewport_scale.https-expected.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] requestViewportScale valid viewport for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ null scale for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ null scale for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ undefined scale for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ undefined scale for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ very small scale for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ very small scale for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale applied next frame for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale applied next frame for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale same frame for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale same frame for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] recommendedViewportScale for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] recommendedViewportScale for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ null scale for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ null scale for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ undefined scale for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ undefined scale for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ very small scale for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ very small scale for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale applied next frame for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale applied next frame for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale same frame for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale same frame for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] recommendedViewportScale for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] recommendedViewportScale for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/arraybuffer/webgl2_size_check-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/arraybuffer/webgl2_size_check-expected.txt
deleted file mode 100644
index 1bdc8c1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/arraybuffer/webgl2_size_check-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that webgl accepts a small view of ArrayBuffer bigger than 2GB.
-  Cannot read properties of null (reading 'createShader')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas-api/OffscreenCanvas-getContext-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas-api/OffscreenCanvas-getContext-expected.txt
deleted file mode 100644
index 54e8ba0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas-api/OffscreenCanvas-getContext-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests basic functionalities of OffscreenCanvas.getContext on the main thread.
-  assert_true: expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas-api/OffscreenCanvas-getContext-in-worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas-api/OffscreenCanvas-getContext-in-worker-expected.txt
deleted file mode 100644
index 0e6da74..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas-api/OffscreenCanvas-getContext-in-worker-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] OffscreenCanvas-getContext-in-worker
-  assert_true: expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas-api/offscreencanvas.transferrable-webgl-exception-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas-api/offscreencanvas.transferrable-webgl-exception-expected.txt
deleted file mode 100644
index 4ec15933..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas-api/offscreencanvas.transferrable-webgl-exception-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that transfer an OffscreenCanvas that has a context throws exception.
-  assert_throws_dom: function "function() {\n            worker.postMessage({offscreenCanvas}, [offscreenCanvas]);\n        }" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/OffscreenCanvas-2d-drawImage-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/OffscreenCanvas-2d-drawImage-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/OffscreenCanvas-2d-drawImage-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/bug1283434-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/bug1283434-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/bug1283434-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/canvas-createImageBitmap-webgl-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/canvas-createImageBitmap-webgl-expected.txt
deleted file mode 100644
index e9436f2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/canvas-createImageBitmap-webgl-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-Check the imageBitmap of webgl.
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/color-space/canvas-webgl-imagetex-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/color-space/canvas-webgl-imagetex-expected.txt
deleted file mode 100644
index 1096052..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/color-space/canvas-webgl-imagetex-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Webgl didn't render imagedata with format uint8 and float32 correctly.
-  Cannot read properties of null (reading 'getContextAttributes')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/rendering-contexts-back-references-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/rendering-contexts-back-references-expected.txt
deleted file mode 100644
index 5425c50..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/rendering-contexts-back-references-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] webgl context on html canvas
-  Cannot read properties of null (reading 'canvas')
-[FAIL] webgl context on offscreen canvas
-  Cannot read properties of null (reading 'canvas')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/webgl/create-query-crash-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/webgl/create-query-crash-expected.txt
deleted file mode 100644
index e20643b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/webgl/create-query-crash-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createQuery')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/webgl/webgl-with-neg-outline-offset-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/webgl/webgl-with-neg-outline-offset-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/canvas/webgl/webgl-with-neg-outline-offset-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-capture-out-of-DOM-element-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-capture-out-of-DOM-element-expected.txt
deleted file mode 100644
index eff99bd..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-capture-out-of-DOM-element-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Verify that drawing to a webgl canvas that is not attached to the DOM dispatches frames to an attached MediaRecorder.
-  Cannot read properties of null (reading 'clearColor')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-imagebitmaprenderingcontext-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-imagebitmaprenderingcontext-expected.txt
deleted file mode 100644
index 94d7f038..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-imagebitmaprenderingcontext-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] CanvasCaptureMediaStream-imagebitmaprenderingcontext 1
-  Cannot read properties of null (reading 'clearColor')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-offscreencanvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-offscreencanvas-expected.txt
deleted file mode 100644
index d29df38..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-offscreencanvas-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] CanvasCaptureMediaStream-offscreencanvas 1
-  Cannot read properties of null (reading 'clearColor')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/canvas-getContext-crash-fragment-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/canvas-getContext-crash-fragment-expected.txt
deleted file mode 100644
index f505335..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/canvas-getContext-crash-fragment-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Tests that canvas can create a webgl context in a template
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Canvas failed in creating a webgl context in a template.
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/canvas-resize-crash-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/canvas-resize-crash-expected.txt
deleted file mode 100644
index def5a02..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/canvas-resize-crash-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Tests that canvas does not crash on resize.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS Canvas did not crash on resize.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Unable to fetch WebGL rendering context for Canvas
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/canvas-toDataURL-premul-overflow-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/canvas-toDataURL-premul-overflow-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/canvas-toDataURL-premul-overflow-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/compressed-tex-image-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/compressed-tex-image-expected.txt
deleted file mode 100644
index 443f0712..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/compressed-tex-image-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This test ensures WebGL implementations correctly implement compressedTexImage2D and compressedTexSubImage2D.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-
-FAIL Unable to fetch WebGL rendering context for Canvas
-FAIL context does not exist
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/context-gc-custom-properties-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/context-gc-custom-properties-expected.txt
deleted file mode 100644
index 8c05ddd..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/context-gc-custom-properties-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Verify that the custom properties on a WebGL rendering context object are retained across GCs.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL Unexpected error: Uncaught Unable to fetch WebGL rendering context for Canvas
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-APIs-NOT-alter-webgl-states-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-APIs-NOT-alter-webgl-states-expected.txt
deleted file mode 100644
index 6e30f58..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-APIs-NOT-alter-webgl-states-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] offscreenCanvas.transferToImageBitmap should not alter WebGL states
-  Cannot read properties of null (reading 'enable')
-[FAIL] offscreenCanvas.commit should not alter WebGL states
-  Cannot read properties of null (reading 'enable')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-context-lost-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-context-lost-expected.txt
deleted file mode 100644
index 6669803..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-context-lost-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that WebGL context loss events can be handled with OffscreenCanvas
-  Cannot read properties of null (reading 'isContextLost')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-context-lost-restored-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-context-lost-restored-expected.txt
deleted file mode 100644
index a069789e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-context-lost-restored-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test WebGL context restoration with OffscreenCanvas
-  Cannot read properties of null (reading 'getExtension')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-transferToImageBitmap-texImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-transferToImageBitmap-texImage2D-expected.txt
deleted file mode 100644
index c9b17c9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/offscreenCanvas-transferToImageBitmap-texImage2D-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/pixelated-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/pixelated-expected.png
deleted file mode 100644
index b5daa85..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/pixelated-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/pixelated-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/pixelated-expected.txt
deleted file mode 100644
index f49f29d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/pixelated-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createShader')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/renderer-and-vendor-strings-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/renderer-and-vendor-strings-expected.txt
deleted file mode 100644
index 0caea48..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/renderer-and-vendor-strings-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Verifies the contents of the RENDERER and VENDOR strings to avoid regressions.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/shader-deleted-by-accessor-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/shader-deleted-by-accessor-expected.txt
deleted file mode 100644
index 23729c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/shader-deleted-by-accessor-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Verifies that WebGLRenderingContext::getAttachedShaders doesn't crash when an accessor property is defined on Array.prototype.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/tex-image-10bpc-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/tex-image-10bpc-expected.txt
deleted file mode 100644
index c3612a7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/tex-image-10bpc-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] ensure 10bpc image is not crushed to 8bpc in texImage2D
-  assert_unreached: webgl2 context not available Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/tex-sub-image-cube-maps-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/tex-sub-image-cube-maps-expected.txt
deleted file mode 100644
index 571dc0d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/tex-sub-image-cube-maps-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createBuffer')
-Checks texSubImage2D with cube map textures
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-
-FAIL Unable to fetch WebGL rendering context for Canvas
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-blob-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-blob-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-blob-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-canvas-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-canvas-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-blob-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-blob-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-blob-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-canvas-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-canvas-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-image-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-image-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-image-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-imageData-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-imageData-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-imageData-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt
deleted file mode 100644
index 8dd3516b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageData-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageData-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-imageData-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-video-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-video-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-from-video-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-structured-clone-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-structured-clone-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-structured-clone-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-transferable-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-transferable-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/texImage-imageBitmap-transferable-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-composite-modes-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-composite-modes-expected.png
deleted file mode 100644
index c5b77dc..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-composite-modes-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-composite-modes-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-composite-modes-expected.txt
deleted file mode 100644
index b70ff1bf..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-composite-modes-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'VERTEX_SHADER')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-context-attributes-default-value-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-context-attributes-default-value-expected.txt
deleted file mode 100644
index 998c659..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-context-attributes-default-value-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Testing default value:
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-exceptions-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-exceptions-expected.txt
deleted file mode 100644
index 63c66eb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-exceptions-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-layer-update-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-layer-update-expected.png
deleted file mode 100644
index e0d6f58..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-layer-update-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-layer-update-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-layer-update-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-layer-update-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-sharedarraybuffer-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-sharedarraybuffer-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/webgl/webgl-sharedarraybuffer-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/http/tests/security/cross-origin-OffscreenCanvasWebGL-texImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/http/tests/security/cross-origin-OffscreenCanvasWebGL-texImage2D-expected.txt
deleted file mode 100644
index 4f60ce0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/http/tests/security/cross-origin-OffscreenCanvasWebGL-texImage2D-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] cross-origin image/canvas passed to Offscreen webgl texImage2D should be thrown
-  Cannot read properties of null (reading 'createTexture')
-[FAIL] cross-origin video passed to Offscreen webgl texImage2D should be thrown
-  Cannot read properties of null (reading 'createTexture')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/http/tests/security/webgl-cross-origin-ImageBitmap-blocked-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/http/tests/security/webgl-cross-origin-ImageBitmap-blocked-expected.txt
deleted file mode 100644
index 890bf130..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/http/tests/security/webgl-cross-origin-ImageBitmap-blocked-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createTexture')
-WebGL's tex(Sub)Image2D should throw a SecurityError exception when the ImageBitmap is not origin clean.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'createTexture')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/images/webgl-teximage2d-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/images/webgl-teximage2d-expected.txt
deleted file mode 100644
index b617705..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/images/webgl-teximage2d-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] webgl-teximage2d
-  Unable to fetch WebGL rendering context for Canvas
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/printing/offscreencanvas-webgl-printing-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/printing/offscreencanvas-webgl-printing-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/printing/offscreencanvas-webgl-printing-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/printing/webgl-repeated-printing-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/printing/webgl-repeated-printing-expected.txt
deleted file mode 100644
index 88105c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/printing/webgl-repeated-printing-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Test requires WebGL
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/printing/webgl-repeated-printing-preservedrawingbuffer-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/printing/webgl-repeated-printing-preservedrawingbuffer-expected.txt
deleted file mode 100644
index 88105c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/printing/webgl-repeated-printing-preservedrawingbuffer-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Test requires WebGL
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
deleted file mode 100644
index 6c933a31..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
deleted file mode 100644
index 4421a43f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
deleted file mode 100644
index 9856dea..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures lighting estimation feature works when enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures lighting estimation feature works when enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
deleted file mode 100644
index faa17a0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
deleted file mode 100644
index a58b2d4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
deleted file mode 100644
index 4e63def..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Immersive session ends if data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Immersive session ends if data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
deleted file mode 100644
index 863716d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
deleted file mode 100644
index 1fdff744..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
deleted file mode 100644
index c5266d7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
deleted file mode 100644
index d525db8..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Blink appropriately reports when frames are throttled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Blink appropriately reports when frames are throttled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
deleted file mode 100644
index 46f6739..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
deleted file mode 100644
index eadb6da..0000000
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that xrview.projection being detached doesn't cause a crash.
-  promise_test: Unhandled rejection with value: object "TypeError: Failed to construct 'XRWebGLLayer': The provided value is not of type '(WebGL2RenderingContext or WebGLRenderingContext)'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.png
deleted file mode 100644
index 74d464c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.txt
deleted file mode 100644
index 11dc479..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/backface-visibility/backface-visibility-webgl-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/draws-content/webgl-background-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/draws-content/webgl-background-layer-expected.txt
deleted file mode 100644
index f0943201..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/draws-content/webgl-background-layer-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/draws-content/webgl-simple-background-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/draws-content/webgl-simple-background-expected.txt
deleted file mode 100644
index 3f00f61..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/draws-content/webgl-simple-background-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'enable')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.png
deleted file mode 100644
index 89efc62..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/visibility/visibility-simple-webgl-layer-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-background-color-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-background-color-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-background-color-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-copy-image-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-copy-image-expected.txt
deleted file mode 100644
index 88105c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-copy-image-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Test requires WebGL
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-no-alpha-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-no-alpha-expected.png
deleted file mode 100644
index b768272..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-no-alpha-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-no-alpha-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-no-alpha-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-no-alpha-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.png
deleted file mode 100644
index b5daa85..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-nonpremultiplied-blend-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-reflection-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-reflection-expected.png
deleted file mode 100644
index 74caef3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-reflection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-reflection-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-reflection-expected.txt
deleted file mode 100644
index f49f29d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-reflection-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createShader')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-repaint-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-repaint-expected.png
deleted file mode 100644
index b5daa85..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-repaint-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-repaint-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-repaint-expected.txt
deleted file mode 100644
index f2cdf4e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/compositing/webgl/webgl-repaint-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ALERT: No WebGL context found
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'viewport')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext-expected.txt
deleted file mode 100644
index 09f603d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that getContext with supported string returns correct results
-  assert_true: expected true got false
-[FAIL] Test that getContext twice with different context type returns null the second time
-  assert_equals: expected null but got object "[object OffscreenCanvasRenderingContext2D]"
-[FAIL] Test that webglcontext.canvas should return the original OffscreenCanvas
-  Cannot read properties of null (reading 'canvas')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker-expected.txt
deleted file mode 100644
index 09f603d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that getContext with supported string returns correct results
-  assert_true: expected true got false
-[FAIL] Test that getContext twice with different context type returns null the second time
-  assert_equals: expected null but got object "[object OffscreenCanvasRenderingContext2D]"
-[FAIL] Test that webglcontext.canvas should return the original OffscreenCanvas
-  Cannot read properties of null (reading 'canvas')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt
deleted file mode 100644
index a699d01..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that transferToImageBitmap returns an ImageBitmap with correct width and height
-  Failed to execute 'transferToImageBitmap' on 'OffscreenCanvas': Cannot transfer an ImageBitmap from an OffscreenCanvas with no context
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt
deleted file mode 100644
index dd10ee11..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that transfer an OffscreenCanvas that has a webgl context throws exception in a worker.
-  assert_true: expected true got object "[object OffscreenCanvas]"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/paint-timing/with-first-paint/first-contentful-canvas-webgl2-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/paint-timing/with-first-paint/first-contentful-canvas-webgl2-expected.txt
deleted file mode 100644
index 786f52d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/paint-timing/with-first-paint/first-contentful-canvas-webgl2-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[PRECONDITION_FAILED] First contentful paint fires due to webgl2 canvas render.
-  WebGL 2 Canvas isn't supported.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/video-rvfc/request-video-frame-callback-before-xr-session.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/video-rvfc/request-video-frame-callback-before-xr-session.https-expected.txt
deleted file mode 100644
index 9033112..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/video-rvfc/request-video-frame-callback-before-xr-session.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Make sure video.rVFC works during a non-immersive session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Make sure video.rVFC works during a non-immersive session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Make sure video.rVFC works during an immersive session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Make sure video.rVFC works during an immersive session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/video-rvfc/request-video-frame-callback-during-xr-session.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/video-rvfc/request-video-frame-callback-during-xr-session.https-expected.txt
deleted file mode 100644
index 1f555b73..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/video-rvfc/request-video-frame-callback-during-xr-session.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Make sure video.rVFC callbacks started during an immersive session continue after it ends - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Make sure video.rVFC callbacks started during an immersive session continue after it ends - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webcodecs/videoFrame-texImage.any-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webcodecs/videoFrame-texImage.any-expected.txt
deleted file mode 100644
index 9f2bac9f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webcodecs/videoFrame-texImage.any-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texImage2D with 48x36 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texSubImage2D with 48x36 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texImage2D with 480x360 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texSubImage2D with 480x360 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texImage2D with a closed VideoFrame.
-  Cannot read properties of null (reading 'texImage2D')
-[FAIL] texSubImage2D with a closed VideoFrame.
-  Cannot read properties of null (reading 'texSubImage2D')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webcodecs/videoFrame-texImage.any.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webcodecs/videoFrame-texImage.any.worker-expected.txt
deleted file mode 100644
index 9f2bac9f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webcodecs/videoFrame-texImage.any.worker-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texImage2D with 48x36 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texSubImage2D with 48x36 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texImage2D with 480x360 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texSubImage2D with 480x360 srgb VideoFrame.
-  Cannot read properties of null (reading 'VERTEX_SHADER')
-[FAIL] texImage2D with a closed VideoFrame.
-  Cannot read properties of null (reading 'texImage2D')
-[FAIL] texSubImage2D with a closed VideoFrame.
-  Cannot read properties of null (reading 'texSubImage2D')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/bufferSubData-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/bufferSubData-expected.txt
deleted file mode 100644
index 908c399..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/bufferSubData-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] bufferSubData
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/compressedTexImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/compressedTexImage2D-expected.txt
deleted file mode 100644
index 239a2cb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/compressedTexImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] compressedTexImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/compressedTexSubImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/compressedTexSubImage2D-expected.txt
deleted file mode 100644
index 704e64a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/compressedTexSubImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] compressedTexSubImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/texImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/texImage2D-expected.txt
deleted file mode 100644
index 3630e6c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/texImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/texSubImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/texSubImage2D-expected.txt
deleted file mode 100644
index 6b27ed439..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/texSubImage2D-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] texSubImage2D
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/uniformMatrixNfv-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/uniformMatrixNfv-expected.txt
deleted file mode 100644
index 14cbc0a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webgl/uniformMatrixNfv-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Should not throw for 2
-  assert_true: Should be able to get a context. expected true got false
-[FAIL] Should not throw for 3
-  assert_true: Should be able to get a context. expected true got false
-[FAIL] Should not throw for 4
-  assert_true: Should be able to get a context. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_create_move.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_create_move.https-expected.txt
deleted file mode 100644
index 77aeb33..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_create_move.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures free-floating anchor move gets propagated to anchor poses - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor move gets propagated to anchor poses - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_delay_creation.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_delay_creation.https-expected.txt
deleted file mode 100644
index 2f825e6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_delay_creation.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures free-floating anchor creation with delayed success is handled correctly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor creation with delayed success is handled correctly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor creation with delayed failure is handled correctly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor creation with delayed failure is handled correctly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_failure.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_failure.https-expected.txt
deleted file mode 100644
index 170dc358..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_failure.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures free-floating anchor creation failure is handled correctly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor creation failure is handled correctly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_pause_resume_stop.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_pause_resume_stop.https-expected.txt
deleted file mode 100644
index e7b94f1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_freefloating_pause_resume_stop.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures free-floating anchor state changes get propagated - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures free-floating anchor state changes get propagated - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_getAnchors.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_getAnchors.https-expected.txt
deleted file mode 100644
index eccfc6f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_getAnchors.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame's trackedAnchors is empty when the feature was not requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was not requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_states.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_states.https-expected.txt
deleted file mode 100644
index 5a8b6ba6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/anchors/ar_anchor_states.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Anchor creation succeeds if the feature was requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation succeeds if the feature was requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation fails if the feature was not requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation fails if the feature was not requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation fails if the feature was requested but the session already ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Anchor creation fails if the feature was requested but the session already ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/ar-module/xrDevice_requestSession_immersive-ar.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/ar-module/xrDevice_requestSession_immersive-ar.https-expected.txt
deleted file mode 100644
index e6e7bd2f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/ar-module/xrDevice_requestSession_immersive-ar.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests requestSession accepts immersive-ar mode - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts immersive-ar mode - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/ar-module/xrSession_environmentBlendMode.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/ar-module/xrSession_environmentBlendMode.https-expected.txt
deleted file mode 100644
index 33b3a708..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/ar-module/xrSession_environmentBlendMode.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests environmentBlendMode for an AR device - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests environmentBlendMode for an AR device - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests environmentBlendMode for a VR device - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests environmentBlendMode for a VR device - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/ar-module/xrSession_interactionMode.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/ar-module/xrSession_interactionMode.https-expected.txt
deleted file mode 100644
index 828c93a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/ar-module/xrSession_interactionMode.https-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests interactionMode for an VR_HMD_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an VR_HMD_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an VR_SCREEN_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an VR_SCREEN_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an AR_HMD_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an AR_HMD_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an AR_SCREEN_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for an AR_SCREEN_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for a INLINE_SCREEN_DEVICE - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests interactionMode for a INLINE_SCREEN_DEVICE - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/camera-access/xrCamera_resolution.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/camera-access/xrCamera_resolution.https-expected.txt
deleted file mode 100644
index 8201f37..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/camera-access/xrCamera_resolution.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRCamera object is present and carries expected dimensions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRCamera object is present and carries expected dimensions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_dataUnavailable.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_dataUnavailable.https-expected.txt
deleted file mode 100644
index 881161c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_dataUnavailable.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures depth data is not available when cleared in the controller, `cpu-optimized` - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures depth data is not available when cleared in the controller, `cpu-optimized` - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_inactiveFrame.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_inactiveFrame.https-expected.txt
deleted file mode 100644
index a0f3b74..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_inactiveFrame.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures getDepthInformation() throws when not run in an active frame, `cpu-optimized` - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures getDepthInformation() throws when not run in an active frame, `cpu-optimized` - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_incorrectUsage.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_incorrectUsage.https-expected.txt
deleted file mode 100644
index 68d1931..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_incorrectUsage.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures XRWebGLDepthInformation is not obtainable in `cpu-optimized` usage mode - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures XRWebGLDepthInformation is not obtainable in `cpu-optimized` usage mode - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_luminance_alpha_dataValid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_luminance_alpha_dataValid.https-expected.txt
deleted file mode 100644
index cc04207..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_luminance_alpha_dataValid.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures depth data is returned and values match expectation, cpu-optimized, luminance-alpha. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures depth data is returned and values match expectation, cpu-optimized, luminance-alpha. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_staleView.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_staleView.https-expected.txt
deleted file mode 100644
index d3bfaa68..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_staleView.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures getDepthInformation() throws when run with stale XRView, `cpu-optimized` - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures getDepthInformation() throws when run with stale XRView, `cpu-optimized` - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/depth_sensing_notEnabled.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/depth_sensing_notEnabled.https-expected.txt
deleted file mode 100644
index 2f85c8c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/depth-sensing/depth_sensing_notEnabled.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.getDepthInformation() rejects if depth sensing is not enabled on a session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getDepthInformation() rejects if depth sensing is not enabled on a session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLBinding.getDepthInformation() rejects if depth sensing is not enabled on a session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLBinding.getDepthInformation() rejects if depth sensing is not enabled on a session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay.https-expected.txt
deleted file mode 100644
index 374b44b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay.https-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures DOM Overlay feature works for immersive-ar, body element - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay feature works for immersive-ar, body element - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay feature works for immersive-ar, div element - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay feature works for immersive-ar, div element - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay input deduplication works - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay input deduplication works - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay Fullscreen API doesn't change DOM overlay - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay Fullscreen API doesn't change DOM overlay - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay interactions on cross origin iframe are ignored - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay interactions on cross origin iframe are ignored - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay_hit_test.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay_hit_test.https-expected.txt
deleted file mode 100644
index 9b2b87de..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/dom-overlay/ar_dom_overlay_hit_test.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures DOM Overlay interactions on cross origin iframe do not cause hit test results to come up - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures DOM Overlay interactions on cross origin iframe do not cause hit test results to come up - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_input_source_recreation.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_input_source_recreation.https-expected.txt
deleted file mode 100644
index 10eaec6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_input_source_recreation.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Input sources are re-created when handedness or target ray mode changes - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Input sources are re-created when handedness or target ray mode changes - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_input_sources_change.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_input_sources_change.https-expected.txt
deleted file mode 100644
index f19b283..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_input_sources_change.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Transient input sources fire events in the right order - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient input sources fire events in the right order - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_referenceSpace_reset_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_referenceSpace_reset_immersive.https-expected.txt
deleted file mode 100644
index b47301e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_referenceSpace_reset_immersive.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession resetpose from a device properly fires off the right events for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession resetpose from a device properly fires off the right events for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_session_select.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_session_select.https-expected.txt
deleted file mode 100644
index 2e23352..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_session_select.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources primary input presses properly fires off the right events - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources primary input presses properly fires off the right events - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_session_select_subframe.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_session_select_subframe.https-expected.txt
deleted file mode 100644
index 24d4ea7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_session_select_subframe.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures that an XRInputSources primary input being pressed and released in the space of a single frame properly fires off the right events - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures that an XRInputSources primary input being pressed and released in the space of a single frame properly fires off the right events - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_session_squeeze.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_session_squeeze.https-expected.txt
deleted file mode 100644
index 2e23352..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/events_session_squeeze.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources primary input presses properly fires off the right events - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources primary input presses properly fires off the right events - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/exclusive_requestFrame_nolayer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/exclusive_requestFrame_nolayer.https-expected.txt
deleted file mode 100644
index 82ad74f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/exclusive_requestFrame_nolayer.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession requestAnimationFrame must fail if the session has no baseLayer for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame must fail if the session has no baseLayer for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame must fail if the session has no baseLayer for non immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame must fail if the session has no baseLayer for non immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_disconnect.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_disconnect.https-expected.txt
deleted file mode 100644
index f01425c0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_disconnect.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] WebXR InputSource's gamepad gets disconnected when the input source is removed - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] WebXR InputSource's gamepad gets disconnected when the input source is removed - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_input_registered.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_input_registered.https-expected.txt
deleted file mode 100644
index f744bb1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/gamepads-module/xrInputSource_gamepad_input_registered.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] WebXR InputSource's gamepad properly registers input - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] WebXR InputSource's gamepad properly registers input - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/getInputPose_handedness.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/getInputPose_handedness.https-expected.txt
deleted file mode 100644
index 752041a2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/getInputPose_handedness.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources properly communicate their handedness - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources properly communicate their handedness - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/getInputPose_pointer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/getInputPose_pointer.https-expected.txt
deleted file mode 100644
index 50313c86..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/getInputPose_pointer.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources with a target ray mode of 'tracked-pointer' properly communicate their poses - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources with a target ray mode of 'tracked-pointer' properly communicate their poses - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/getViewerPose_emulatedPosition.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/getViewerPose_emulatedPosition.https-expected.txt
deleted file mode 100644
index 3957dd38..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/getViewerPose_emulatedPosition.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame getViewerPose has emulatedPosition set properly. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose has emulatedPosition set properly. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_source_cancel.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_source_cancel.https-expected.txt
deleted file mode 100644
index 97b8bb74..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_source_cancel.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures hit test source cancellation works when the session has not ended. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation works when the session has not ended. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation works when the session has not ended. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation works when the session has not ended. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation works when the session has ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation works when the session has ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation works when the session has ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation works when the session has ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_inputSources.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_inputSources.https-expected.txt
deleted file mode 100644
index b790f768..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_inputSources.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - no move - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - no move - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - after move - no results - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - after move - no results - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - after move - 1 result - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with an XRSpace from input source - after move - 1 result - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_refSpaces.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_refSpaces.https-expected.txt
deleted file mode 100644
index dfa99f7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_refSpaces.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures subscription to hit test works with viewer space - straight ahead - plane - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with viewer space - straight ahead - plane - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with viewer space - straight up - no results - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with viewer space - straight up - no results - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with local space - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with local space - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with local-floor space - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to hit test works with local-floor space - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_regular.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_regular.https-expected.txt
deleted file mode 100644
index c7b7cb6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_regular.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Hit test subscription succeeds if the feature was requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription succeeds if the feature was requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription fails if the feature was not requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription fails if the feature was not requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription fails if the feature was requested but the session already ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Hit test subscription fails if the feature was requested but the session already ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_transient.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_transient.https-expected.txt
deleted file mode 100644
index 597ed5b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_states_transient.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Transient hit test subscription succeeds if the feature was requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient hit test subscription succeeds if the feature was requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient hit test subscription fails if the feature was not requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient hit test subscription fails if the feature was not requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient test subscription fails if the feature was requested but the session already ended - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Transient test subscription fails if the feature was requested but the session already ended - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_transientInputSources.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_transientInputSources.https-expected.txt
deleted file mode 100644
index 8428388..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_transientInputSources.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - no move - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - no move - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - after move - no results - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - after move - no results - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - after move - 1 result - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures subscription to transient hit test works with an XRSpace from input source - after move - 1 result - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_unlocalizable.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_unlocalizable.https-expected.txt
deleted file mode 100644
index 7f33807..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/hit-test/ar_hittest_subscription_unlocalizable.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures hit test result returns null pose w/unlocalizable space - viewer space - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test result returns null pose w/unlocalizable space - viewer space - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/layers/xrSession_updateRenderState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/layers/xrSession_updateRenderState.https-expected.txt
deleted file mode 100644
index b832bb2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/layers/xrSession_updateRenderState.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure XRSession throws appropriate errors when updating render state without layers feature enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure XRSession throws appropriate errors when updating render state without layers feature enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure XRSession throws appropriate errors when updating render state with layers feature enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure XRSession throws appropriate errors when updating render state with layers feature enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/layers/xrWebGLBinding_constructor.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/layers/xrWebGLBinding_constructor.https-expected.txt
deleted file mode 100644
index a13a630..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/layers/xrWebGLBinding_constructor.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure that XRWebGLBinding's constructor throws appropriate errors using webgl
-  assert_equals: Should get InvalidStateError for creating with inline session. expected "InvalidStateError" but got "TypeError"
-[FAIL] Ensure that XRWebGLBinding's constructor throws appropriate errors using webgl2
-  assert_equals: Should get InvalidStateError for creating with inline session. expected "InvalidStateError" but got "TypeError"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_oldSession.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_oldSession.https-expected.txt
deleted file mode 100644
index 9625c30..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_oldSession.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] getLightEstimate rejects if probe is from wrong session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] getLightEstimate rejects if probe is from wrong session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_staleFrame.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_staleFrame.https-expected.txt
deleted file mode 100644
index 54c171e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_staleFrame.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Cannot get XrLightEstimate from stale frame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Cannot get XrLightEstimate from stale frame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_valid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_valid.https-expected.txt
deleted file mode 100644
index 2130398..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrFrame_getLightEstimate_valid.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Can get XRLightEstimates during frame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Can get XRLightEstimates during frame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_ended.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_ended.https-expected.txt
deleted file mode 100644
index 2a64557..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_ended.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] getLightProbe rejects on an ended session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] getLightProbe rejects on an ended session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_notEnabled.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_notEnabled.https-expected.txt
deleted file mode 100644
index 4335f4cb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_notEnabled.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] getLightProbe rejects if not enabled on session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] getLightProbe rejects if not enabled on session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_valid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_valid.https-expected.txt
deleted file mode 100644
index 538b2742..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrSession_getLightProbe_valid.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Can create valid XRLightProbe objects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Can create valid XRLightProbe objects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https-expected.txt
deleted file mode 100644
index 3565636..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that getReflectionCubeMap returns or throws appropriately without a reflection map. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Test that getReflectionCubeMap returns or throws appropriately without a reflection map. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/navigator_xr_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/navigator_xr_sameObject.https-expected.txt
deleted file mode 100644
index 9d8a5844..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/navigator_xr_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Navigator.xr meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Navigator.xr meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/render_state_update.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/render_state_update.https-expected.txt
deleted file mode 100644
index 1d9c4f0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/render_state_update.https-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] updateRenderState handles appropriately ended sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately ended sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately baseLayers created with different sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately baseLayers created with different sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately immersive sessions with specified inlineVerticalFieldOfView - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately immersive sessions with specified inlineVerticalFieldOfView - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately XRRenderStateInit with no params - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately XRRenderStateInit with no params - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately XRRenderStateInit params - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState handles appropriately XRRenderStateInit params - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState clamps appropriately near/far clipping planes - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] updateRenderState clamps appropriately near/far clipping planes - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/render_state_vertical_fov_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/render_state_vertical_fov_immersive.https-expected.txt
deleted file mode 100644
index 7175625..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/render_state_vertical_fov_immersive.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] inlineVerticalFieldOfView is set appropriately on immersively sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] inlineVerticalFieldOfView is set appropriately on immersively sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/render_state_vertical_fov_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/render_state_vertical_fov_inline.https-expected.txt
deleted file mode 100644
index faa17a0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/render_state_vertical_fov_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webGLCanvasContext_create_xrcompatible.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webGLCanvasContext_create_xrcompatible.https-expected.txt
deleted file mode 100644
index 6ffe66bd..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webGLCanvasContext_create_xrcompatible.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Creating a webgl context with no device
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] Creating a webgl2 context with no device
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] An XR-compatible webgl context can be created
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] An XR-compatible webgl2 context can be created
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_contextlost.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_contextlost.https-expected.txt
deleted file mode 100644
index 5a4dd22aa..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_contextlost.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] A lost webgl context should not be able to set xr compatibility
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] A lost webgl2 context should not be able to set xr compatibility
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_reentrant.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_reentrant.https-expected.txt
deleted file mode 100644
index 347a47af..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webGLCanvasContext_makecompatible_reentrant.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Verify promise from a non-reentrant call to makeXRCompatible() is resolved for webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] Verify promise from a non-reentrant call to makeXRCompatible() is resolved for webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] Verify promises from reentrant calls to makeXRCompatible() are resolved for webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-[FAIL] Verify promises from reentrant calls to makeXRCompatible() are resolved for webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'getContextAttributes')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webxr_permissions_policy.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webxr_permissions_policy.https-expected.txt
deleted file mode 100644
index 4b92d2c22..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/webxr_permissions_policy.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Validate xr compatibility requests without xr-spatial-tracking policy
-  Cannot read properties of null (reading 'getContextAttributes')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrBoundedReferenceSpace_updates.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrBoundedReferenceSpace_updates.https-expected.txt
deleted file mode 100644
index 3200193..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrBoundedReferenceSpace_updates.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] 'XRBoundedReferenceSpace updates properly when the changes are applied - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] 'XRBoundedReferenceSpace updates properly when the changes are applied - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrDevice_disconnect_ends.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrDevice_disconnect_ends.https-expected.txt
deleted file mode 100644
index af204eb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrDevice_disconnect_ends.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Immersive session ends when device is disconnected - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Immersive session ends when device is disconnected - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrDevice_requestSession_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrDevice_requestSession_immersive.https-expected.txt
deleted file mode 100644
index 0a6951a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrDevice_requestSession_immersive.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests requestSession resolves when supported - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession resolves when supported - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown optionalFeatures - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown optionalFeatures - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrDevice_requestSession_optionalFeatures.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrDevice_requestSession_optionalFeatures.https-expected.txt
deleted file mode 100644
index 51dd97c14f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrDevice_requestSession_optionalFeatures.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests requestSession accepts XRSessionInit dictionary - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary with empty feature lists - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession accepts XRSessionInit dictionary with empty feature lists - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown strings in optionalFeatures - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown strings in optionalFeatures - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown objects in optionalFeatures - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Tests requestSession ignores unknown objects in optionalFeatures - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_getPose.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_getPose.https-expected.txt
deleted file mode 100644
index f6f532b4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_getPose.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.getPose works for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works for non-immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works for non-immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose.https-expected.txt
deleted file mode 100644
index 8211819a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame getViewerPose(refSpace) matches getPose(viewer, refSpace). - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose(refSpace) matches getPose(viewer, refSpace). - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose_identities.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose_identities.https-expected.txt
deleted file mode 100644
index 2c1c94d8..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_getViewerPose_getPose_identities.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame getViewerPose(viewerSpace) & getPose(space, space) return identity even during tracking loss - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose(viewerSpace) & getPose(space, space) return identity even during tracking loss - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_lifetime.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_lifetime.https-expected.txt
deleted file mode 100644
index 16c434f2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_lifetime.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame methods throw exceptions outside of the requestAnimationFrame callback for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame methods throw exceptions outside of the requestAnimationFrame callback for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame methods throw exceptions outside of the requestAnimationFrame callback for non-immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame methods throw exceptions outside of the requestAnimationFrame callback for non-immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_session_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_session_sameObject.https-expected.txt
deleted file mode 100644
index 1c4e5fd9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrFrame_session_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.session meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.session meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_add_remove.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_add_remove.https-expected.txt
deleted file mode 100644
index 4fcb43a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_add_remove.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSources can be properly added and removed from the session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSources can be properly added and removed from the session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_emulatedPosition.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_emulatedPosition.https-expected.txt
deleted file mode 100644
index 0c5474f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_emulatedPosition.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Poses from XRInputSource.gripSpace have emulatedPosition set properly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Poses from XRInputSource.gripSpace have emulatedPosition set properly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_getPose_targetRay_grip.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_getPose_targetRay_grip.https-expected.txt
deleted file mode 100644
index b2508e7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_getPose_targetRay_grip.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Poses between targetRaySpace and gripSpace can be obtained and behave correctly - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Poses between targetRaySpace and gripSpace can be obtained and behave correctly - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_profiles.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_profiles.https-expected.txt
deleted file mode 100644
index fd761739e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_profiles.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] WebXR InputSource's profiles list can be set - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] WebXR InputSource's profiles list can be set - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_sameObject.https-expected.txt
deleted file mode 100644
index c239a8f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrInputSource_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRInputSource attributes meet [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRInputSource attributes meet [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrPose_transform_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrPose_transform_sameObject.https-expected.txt
deleted file mode 100644
index f47f8d0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrPose_transform_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRPose.transform meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRPose.transform meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_originOffset.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_originOffset.https-expected.txt
deleted file mode 100644
index 19f0627..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_originOffset.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Updating XRReferenceSpace origin offset updates view and input matrices. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Updating XRReferenceSpace origin offset updates view and input matrices. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_originOffsetBounded.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_originOffsetBounded.https-expected.txt
deleted file mode 100644
index 31306182..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_originOffsetBounded.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Updating XRBoundedReferenceSpace origin offset updates view, input matrices, and bounds geometry. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Updating XRBoundedReferenceSpace origin offset updates view, input matrices, and bounds geometry. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_originOffset_viewer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_originOffset_viewer.https-expected.txt
deleted file mode 100644
index 7511ac1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_originOffset_viewer.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Creating XRReferenceSpace origin offset off of `viewer` space works. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Creating XRReferenceSpace origin offset off of `viewer` space works. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_relationships.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_relationships.https-expected.txt
deleted file mode 100644
index f9ed1442..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrReferenceSpace_relationships.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Bounded space, viewer space, local and local-floor space have correct poses w.r.t. each other - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Bounded space, viewer space, local and local-floor space have correct poses w.r.t. each other - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrRigidTransform_constructor.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrRigidTransform_constructor.https-expected.txt
deleted file mode 100644
index d55c86a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrRigidTransform_constructor.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRRigidTransform constructor works - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRRigidTransform constructor works - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrRigidTransform_inverse.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrRigidTransform_inverse.https-expected.txt
deleted file mode 100644
index ff813ff..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrRigidTransform_inverse.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRRigidTransform inverse works - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRRigidTransform inverse works - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrRigidTransform_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrRigidTransform_sameObject.https-expected.txt
deleted file mode 100644
index bfdcba3f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrRigidTransform_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRRigidTransform position and orientation meet [SameObject] requirements - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRRigidTransform position and orientation meet [SameObject] requirements - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame.https-expected.txt
deleted file mode 100644
index a4ca8d2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for non-immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for non-immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame_invalidhandle.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame_invalidhandle.https-expected.txt
deleted file mode 100644
index daa1261..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_cancelAnimationFrame_invalidhandle.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on immersive testSession - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on immersive testSession - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on non-immersive testSession - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on non-immersive testSession - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_enabledFeatures.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_enabledFeatures.https-expected.txt
deleted file mode 100644
index 2b42167..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_enabledFeatures.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Validate enabledFeatures on XRSession - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Validate enabledFeatures on XRSession - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_end.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_end.https-expected.txt
deleted file mode 100644
index 759615a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_end.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] end event fires when immersive session ends - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] end event fires when immersive session ends - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] end event fires when non-immersive session ends - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] end event fires when non-immersive session ends - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_input_events_end.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_input_events_end.https-expected.txt
deleted file mode 100644
index 166244a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_input_events_end.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Calling end during an input callback stops processing at the right time - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Calling end during an input callback stops processing at the right time - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https-expected.txt
deleted file mode 100644
index e821a16..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession requestAnimationFrame calls the provided callback for an immersive session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame calls the provided callback for an immersive session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame calls the provided callback a non-immersive session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession requestAnimationFrame calls the provided callback a non-immersive session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_data_valid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_data_valid.https-expected.txt
deleted file mode 100644
index 823eac7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_data_valid.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] RequestAnimationFrame resolves with good data - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] RequestAnimationFrame resolves with good data - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_timestamp.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_timestamp.https-expected.txt
deleted file mode 100644
index 3570005..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestAnimationFrame_timestamp.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame getViewerPose updates on the next frame for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose updates on the next frame for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose updates on the next frame for non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame getViewerPose updates on the next frame for non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestReferenceSpace.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestReferenceSpace.https-expected.txt
deleted file mode 100644
index 8efb091..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestReferenceSpace.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Immersive XRSession requestReferenceSpace returns expected objects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Immersive XRSession requestReferenceSpace returns expected objects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Non-immersive XRSession requestReferenceSpace returns expected objects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Non-immersive XRSession requestReferenceSpace returns expected objects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestSessionDuringEnd.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestSessionDuringEnd.https-expected.txt
deleted file mode 100644
index 5311707..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_requestSessionDuringEnd.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create new session in OnSessionEnded event - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Create new session in OnSessionEnded event - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Create mew session in end promise - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Create mew session in end promise - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_sameObject.https-expected.txt
deleted file mode 100644
index 02865c1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRSession attributes meet [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRSession attributes meet [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_viewer_referenceSpace.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_viewer_referenceSpace.https-expected.txt
deleted file mode 100644
index 5a3dc53..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_viewer_referenceSpace.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Identity reference space provides correct poses for inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Identity reference space provides correct poses for inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Identity reference space provides correct poses for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Identity reference space provides correct poses for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_visibilityState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_visibilityState.https-expected.txt
deleted file mode 100644
index 89b09ca..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrSession_visibilityState.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures that the XRSession's visibilityState is correctly reported and that the associated visibilitychange event fires. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures that the XRSession's visibilityState is correctly reported and that the associated visibilitychange event fires. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_eyes.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_eyes.https-expected.txt
deleted file mode 100644
index 3984e7a1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_eyes.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRView.eye is correct for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView.eye is correct for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView.eye is correct for non-immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView.eye is correct for non-immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_match.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_match.https-expected.txt
deleted file mode 100644
index e91a9ad..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_match.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame contains the expected views - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame contains the expected views - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_oneframeupdate.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_oneframeupdate.https-expected.txt
deleted file mode 100644
index 8d5ce4c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_oneframeupdate.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRView projection matrices update near and far depths on the next frame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView projection matrices update near and far depths on the next frame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_sameObject.https-expected.txt
deleted file mode 100644
index e2e5bec..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrView_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRView attributes meet [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRView attributes meet [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrViewerPose_secondaryViews.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrViewerPose_secondaryViews.https-expected.txt
deleted file mode 100644
index f3bc0cd0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrViewerPose_secondaryViews.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Only primary views are returned if secondary views are not requested for non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Only primary views are returned if secondary views are not requested for non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Only primary views are returned if secondary views are not requested for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Only primary views are returned if secondary views are not requested for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Requesting secondary views only returns primary views for non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Requesting secondary views only returns primary views for non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Requesting secondary views returns both primary and secondary views for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Requesting secondary views returns both primary and secondary views for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrViewerPose_views_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrViewerPose_views_sameObject.https-expected.txt
deleted file mode 100644
index 93c77f0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrViewerPose_views_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRViewerPose.views meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRViewerPose.views meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrViewport_valid.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrViewport_valid.https-expected.txt
deleted file mode 100644
index 39ed84b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrViewport_valid.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRViewport attributes are valid - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRViewport attributes are valid - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRViewport attributes are valid with secondary views requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRViewport attributes are valid with secondary views requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_constructor.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_constructor.https-expected.txt
deleted file mode 100644
index 7e4a6ec..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_constructor.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure that XRWebGLLayer's constructor throws appropriate errors using webgl
-  assert_unreached: Inline XRWebGLLayers should not fail when created with a context that is not XRCompatible Reached unreachable code
-[FAIL] Ensure that XRWebGLLayer's constructor throws appropriate errors using webgl2
-  assert_unreached: Inline XRWebGLLayers should not fail when created with a context that is not XRCompatible Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt
deleted file mode 100644
index a55fe90e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_draw.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure a WebGL layer's framebuffer can only be drawn to inside a XR frame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure a WebGL layer's framebuffer can only be drawn to inside a XR frame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_sameObject.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_sameObject.https-expected.txt
deleted file mode 100644
index 2cdeea9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_sameObject.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRWebGLLayer.framebuffer meets [SameObject] requirement - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer.framebuffer meets [SameObject] requirement - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_scale.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_scale.https-expected.txt
deleted file mode 100644
index 0a88c47..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_framebuffer_scale.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure framebuffer scaling works as expected. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure framebuffer scaling works as expected. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt
deleted file mode 100644
index a45a1fb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure that the framebuffer given by the WebGL layer is opaque for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure that the framebuffer given by the WebGL layer is opaque for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure that the framebuffer given by the WebGL layer is opaque for non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure that the framebuffer given by the WebGL layer is opaque for non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https-expected.txt
deleted file mode 100644
index 49210ce..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensure that the framebuffer given by the WebGL layer works with stencil for immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure that the framebuffer given by the WebGL layer works with stencil for immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_viewports.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_viewports.https-expected.txt
deleted file mode 100644
index 8684a5b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xrWebGLLayer_viewports.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRWebGLLayer reports a valid viewports for immersive sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for immersive sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for immersive sessions with secondary views requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for immersive sessions with secondary views requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for inline sessions with secondary views requested - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRWebGLLayer reports a valid viewports for inline sessions with secondary views requested - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xr_viewport_scale.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xr_viewport_scale.https-expected.txt
deleted file mode 100644
index ae458e8..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/external/wpt/webxr/xr_viewport_scale.https-expected.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] requestViewportScale valid viewport for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ null scale for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ null scale for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ undefined scale for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ undefined scale for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ very small scale for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ very small scale for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale applied next frame for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale applied next frame for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale same frame for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale same frame for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] recommendedViewportScale for inline session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] recommendedViewportScale for inline session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ null scale for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ null scale for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ undefined scale for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ undefined scale for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ very small scale for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale valid viewport w/ very small scale for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale applied next frame for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale applied next frame for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale same frame for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] requestViewportScale same frame for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] recommendedViewportScale for immersive-vr session - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] recommendedViewportScale for immersive-vr session - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/arraybuffer/webgl2_size_check-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/arraybuffer/webgl2_size_check-expected.txt
deleted file mode 100644
index 1bdc8c1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/arraybuffer/webgl2_size_check-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that webgl accepts a small view of ArrayBuffer bigger than 2GB.
-  Cannot read properties of null (reading 'createShader')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas-api/OffscreenCanvas-getContext-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas-api/OffscreenCanvas-getContext-expected.txt
deleted file mode 100644
index 54e8ba0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas-api/OffscreenCanvas-getContext-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Tests basic functionalities of OffscreenCanvas.getContext on the main thread.
-  assert_true: expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas-api/OffscreenCanvas-getContext-in-worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas-api/OffscreenCanvas-getContext-in-worker-expected.txt
deleted file mode 100644
index 0e6da74..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas-api/OffscreenCanvas-getContext-in-worker-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] OffscreenCanvas-getContext-in-worker
-  assert_true: expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas-api/offscreencanvas.transferrable-webgl-exception-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas-api/offscreencanvas.transferrable-webgl-exception-expected.txt
deleted file mode 100644
index 4ec15933..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas-api/offscreencanvas.transferrable-webgl-exception-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that transfer an OffscreenCanvas that has a context throws exception.
-  assert_throws_dom: function "function() {\n            worker.postMessage({offscreenCanvas}, [offscreenCanvas]);\n        }" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/OffscreenCanvas-2d-drawImage-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/OffscreenCanvas-2d-drawImage-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/OffscreenCanvas-2d-drawImage-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/bug1283434-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/bug1283434-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/bug1283434-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/canvas-createImageBitmap-webgl-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/canvas-createImageBitmap-webgl-expected.txt
deleted file mode 100644
index e9436f2..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/canvas-createImageBitmap-webgl-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-Check the imageBitmap of webgl.
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/color-space/canvas-webgl-imagetex-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/color-space/canvas-webgl-imagetex-expected.txt
deleted file mode 100644
index 1096052..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/color-space/canvas-webgl-imagetex-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Webgl didn't render imagedata with format uint8 and float32 correctly.
-  Cannot read properties of null (reading 'getContextAttributes')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/rendering-contexts-back-references-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/rendering-contexts-back-references-expected.txt
deleted file mode 100644
index 5425c50..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/rendering-contexts-back-references-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] webgl context on html canvas
-  Cannot read properties of null (reading 'canvas')
-[FAIL] webgl context on offscreen canvas
-  Cannot read properties of null (reading 'canvas')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/webgl/create-query-crash-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/webgl/create-query-crash-expected.txt
deleted file mode 100644
index e20643b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/webgl/create-query-crash-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createQuery')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/webgl/webgl-with-neg-outline-offset-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/webgl/webgl-with-neg-outline-offset-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/canvas/webgl/webgl-with-neg-outline-offset-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-capture-out-of-DOM-element-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-capture-out-of-DOM-element-expected.txt
deleted file mode 100644
index eff99bd..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-capture-out-of-DOM-element-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Verify that drawing to a webgl canvas that is not attached to the DOM dispatches frames to an attached MediaRecorder.
-  Cannot read properties of null (reading 'clearColor')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-imagebitmaprenderingcontext-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-imagebitmaprenderingcontext-expected.txt
deleted file mode 100644
index 94d7f038..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-imagebitmaprenderingcontext-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] CanvasCaptureMediaStream-imagebitmaprenderingcontext 1
-  Cannot read properties of null (reading 'clearColor')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-offscreencanvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-offscreencanvas-expected.txt
deleted file mode 100644
index d29df38..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/mediacapturefromelement/CanvasCaptureMediaStream-offscreencanvas-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] CanvasCaptureMediaStream-offscreencanvas 1
-  Cannot read properties of null (reading 'clearColor')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/canvas-getContext-crash-fragment-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/canvas-getContext-crash-fragment-expected.txt
deleted file mode 100644
index f505335..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/canvas-getContext-crash-fragment-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Tests that canvas can create a webgl context in a template
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Canvas failed in creating a webgl context in a template.
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/canvas-resize-crash-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/canvas-resize-crash-expected.txt
deleted file mode 100644
index def5a02..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/canvas-resize-crash-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Tests that canvas does not crash on resize.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS Canvas did not crash on resize.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Unable to fetch WebGL rendering context for Canvas
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/canvas-toDataURL-premul-overflow-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/canvas-toDataURL-premul-overflow-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/canvas-toDataURL-premul-overflow-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/compressed-tex-image-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/compressed-tex-image-expected.txt
deleted file mode 100644
index 443f0712..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/compressed-tex-image-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This test ensures WebGL implementations correctly implement compressedTexImage2D and compressedTexSubImage2D.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-
-FAIL Unable to fetch WebGL rendering context for Canvas
-FAIL context does not exist
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/context-gc-custom-properties-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/context-gc-custom-properties-expected.txt
deleted file mode 100644
index 8c05ddd..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/context-gc-custom-properties-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Verify that the custom properties on a WebGL rendering context object are retained across GCs.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL Unexpected error: Uncaught Unable to fetch WebGL rendering context for Canvas
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-APIs-NOT-alter-webgl-states-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-APIs-NOT-alter-webgl-states-expected.txt
deleted file mode 100644
index 6e30f58..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-APIs-NOT-alter-webgl-states-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] offscreenCanvas.transferToImageBitmap should not alter WebGL states
-  Cannot read properties of null (reading 'enable')
-[FAIL] offscreenCanvas.commit should not alter WebGL states
-  Cannot read properties of null (reading 'enable')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-context-lost-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-context-lost-expected.txt
deleted file mode 100644
index 6669803..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-context-lost-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that WebGL context loss events can be handled with OffscreenCanvas
-  Cannot read properties of null (reading 'isContextLost')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-context-lost-restored-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-context-lost-restored-expected.txt
deleted file mode 100644
index a069789e..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-context-lost-restored-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test WebGL context restoration with OffscreenCanvas
-  Cannot read properties of null (reading 'getExtension')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-transferToImageBitmap-texImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-transferToImageBitmap-texImage2D-expected.txt
deleted file mode 100644
index c9b17c9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/offscreenCanvas-transferToImageBitmap-texImage2D-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/pixelated-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/pixelated-expected.png
deleted file mode 100644
index b5daa85..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/pixelated-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/pixelated-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/pixelated-expected.txt
deleted file mode 100644
index f49f29d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/pixelated-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createShader')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/renderer-and-vendor-strings-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/renderer-and-vendor-strings-expected.txt
deleted file mode 100644
index 0caea48..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/renderer-and-vendor-strings-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Verifies the contents of the RENDERER and VENDOR strings to avoid regressions.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/shader-deleted-by-accessor-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/shader-deleted-by-accessor-expected.txt
deleted file mode 100644
index 23729c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/shader-deleted-by-accessor-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Verifies that WebGLRenderingContext::getAttachedShaders doesn't crash when an accessor property is defined on Array.prototype.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/tex-image-10bpc-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/tex-image-10bpc-expected.txt
deleted file mode 100644
index c3612a7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/tex-image-10bpc-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] ensure 10bpc image is not crushed to 8bpc in texImage2D
-  assert_unreached: webgl2 context not available Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/tex-sub-image-cube-maps-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/tex-sub-image-cube-maps-expected.txt
deleted file mode 100644
index 571dc0d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/tex-sub-image-cube-maps-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createBuffer')
-Checks texSubImage2D with cube map textures
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-
-FAIL Unable to fetch WebGL rendering context for Canvas
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-blob-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-blob-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-blob-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-canvas-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-canvas-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-blob-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-blob-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-blob-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-canvas-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-canvas-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-image-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-image-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-image-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-imageData-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-imageData-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-imageData-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt
deleted file mode 100644
index 8dd3516b..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageData-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageData-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-imageData-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt
deleted file mode 100644
index f4d84db..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-video-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-video-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-from-video-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-structured-clone-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-structured-clone-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-structured-clone-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-transferable-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-transferable-expected.txt
deleted file mode 100644
index e2b88a6..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/texImage-imageBitmap-transferable-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
- 
-Test FAILED
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-composite-modes-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-composite-modes-expected.png
deleted file mode 100644
index c5b77dc..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-composite-modes-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-composite-modes-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-composite-modes-expected.txt
deleted file mode 100644
index b70ff1bf..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-composite-modes-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'VERTEX_SHADER')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-context-attributes-default-value-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-context-attributes-default-value-expected.txt
deleted file mode 100644
index 998c659..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-context-attributes-default-value-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-Testing default value:
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-exceptions-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-exceptions-expected.txt
deleted file mode 100644
index 63c66eb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-exceptions-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-CONSOLE ERROR: Uncaught Unable to fetch WebGL rendering context for Canvas
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-layer-update-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-layer-update-expected.png
deleted file mode 100644
index e0d6f58..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-layer-update-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-layer-update-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-layer-update-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-layer-update-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-sharedarraybuffer-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-sharedarraybuffer-expected.txt
deleted file mode 100644
index 42e1de3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/webgl/webgl-sharedarraybuffer-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = done() was called without first defining any tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/http/tests/security/cross-origin-OffscreenCanvasWebGL-texImage2D-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/http/tests/security/cross-origin-OffscreenCanvasWebGL-texImage2D-expected.txt
deleted file mode 100644
index 4f60ce0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/http/tests/security/cross-origin-OffscreenCanvasWebGL-texImage2D-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] cross-origin image/canvas passed to Offscreen webgl texImage2D should be thrown
-  Cannot read properties of null (reading 'createTexture')
-[FAIL] cross-origin video passed to Offscreen webgl texImage2D should be thrown
-  Cannot read properties of null (reading 'createTexture')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/http/tests/security/webgl-cross-origin-ImageBitmap-blocked-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/http/tests/security/webgl-cross-origin-ImageBitmap-blocked-expected.txt
deleted file mode 100644
index 890bf130..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/http/tests/security/webgl-cross-origin-ImageBitmap-blocked-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'createTexture')
-WebGL's tex(Sub)Image2D should throw a SecurityError exception when the ImageBitmap is not origin clean.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-FAIL Unexpected error: Uncaught TypeError: Cannot read properties of null (reading 'createTexture')
-FAIL successfullyParsed should be true. Was false.
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/images/webgl-teximage2d-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/images/webgl-teximage2d-expected.txt
deleted file mode 100644
index b617705..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/images/webgl-teximage2d-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] webgl-teximage2d
-  Unable to fetch WebGL rendering context for Canvas
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/printing/offscreencanvas-webgl-printing-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/printing/offscreencanvas-webgl-printing-expected.txt
deleted file mode 100644
index fd007408..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/printing/offscreencanvas-webgl-printing-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE ERROR: Uncaught TypeError: Cannot read properties of null (reading 'clearColor')
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/printing/webgl-repeated-printing-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/printing/webgl-repeated-printing-expected.txt
deleted file mode 100644
index 88105c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/printing/webgl-repeated-printing-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Test requires WebGL
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/printing/webgl-repeated-printing-preservedrawingbuffer-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/printing/webgl-repeated-printing-preservedrawingbuffer-expected.txt
deleted file mode 100644
index 88105c3..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/printing/webgl-repeated-printing-preservedrawingbuffer-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-FAIL Test requires WebGL
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
deleted file mode 100644
index 6c933a31..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/ar/ar_anchor_getAnchors_null.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame's trackedAnchors is empty when the feature was requested & device returned null anchorsData - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
deleted file mode 100644
index 4421a43f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when manually cancelled. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures transient input hit test source cancellation propagates to the device when relying on GC - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
deleted file mode 100644
index 9856dea..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/ar/ar_light_estimation.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Ensures lighting estimation feature works when enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensures lighting estimation feature works when enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Ensure lighting estimation feature does not work when not explicitly enabled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
deleted file mode 100644
index faa17a0..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/render_state_vertical_fov_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] inlineVerticalFieldOfView is set appropriately on inline sessions - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
deleted file mode 100644
index a58b2d4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrFrame_getPose.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] XRFrame.getPose works between eye-level and floor-level spaces - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
deleted file mode 100644
index 4e63def..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Immersive session ends if data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Immersive session ends if data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
deleted file mode 100644
index 863716d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Inline session ends if magic window data provider disconnects. - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
deleted file mode 100644
index 1fdff744..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_environmentBlendMode.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] environmentBlendMode is correct for a VR device in non-immersive - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
deleted file mode 100644
index c5266d7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Outstanding promises get rejected if environmentProvider disconnects - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
deleted file mode 100644
index d525db8..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrSession_framesThrottled.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Blink appropriately reports when frames are throttled - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] Blink appropriately reports when frames are throttled - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
deleted file mode 100644
index 46f6739..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-[FAIL] A frame should be submitted if the base layer was written to during requestAnimationFrame - webgl2
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'makeXRCompatible')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
deleted file mode 100644
index eadb6da..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/wpt_internal/webxr/xr_view_projection_detached.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Test that xrview.projection being detached doesn't cause a crash.
-  promise_test: Unhandled rejection with value: object "TypeError: Failed to construct 'XRWebGLLayer': The provided value is not of type '(WebGL2RenderingContext or WebGLRenderingContext)'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt
deleted file mode 100644
index 2ea65e6..0000000
--- a/third_party/blink/web_tests/platform/mac/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Timing of fullscreenchange and resize events
-  assert_array_equals: event order lengths differ, expected array ["resize", "fullscreenchange"] length 2, got ["fullscreenchange"] length 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru.https.any.worker_cpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru.https.any.worker_cpu-expected.txt
index 7bd66cdd..e25dcdf 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru.https.any.worker_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru.https.any.worker_cpu-expected.txt
@@ -1,23 +1,23 @@
 This is a testharness.js-based test.
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru.https.any_cpu-expected.txt
index 7bd66cdd..e25dcdf 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru.https.any_cpu-expected.txt
@@ -1,23 +1,23 @@
 This is a testharness.js-based test.
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_cpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_cpu-expected.txt
index b0c7f3d..02934c3 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_cpu-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru_cell.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru_cell.https.any_cpu-expected.txt
index b0c7f3d..02934c3 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru_cell.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/gru_cell.https.any_cpu-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_cpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_cpu-expected.txt
new file mode 100644
index 0000000..9862b6a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_cpu-expected.txt
@@ -0,0 +1,27 @@
+This is a testharness.js-based test.
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialHiddenState
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialCellState
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.returnSequence=false
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.returnSequence=true
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.direction='forward'
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with all options
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.direction='backward'
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=2 with all options
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any_cpu-expected.txt
new file mode 100644
index 0000000..9862b6a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any_cpu-expected.txt
@@ -0,0 +1,27 @@
+This is a testharness.js-based test.
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialHiddenState
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialCellState
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.returnSequence=false
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.returnSequence=true
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.direction='forward'
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=1 with all options
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.direction='backward'
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+[FAIL] lstm float32 tensors steps=2 with all options
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_cpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_cpu-expected.txt
index e7ff0b1..86f56a4 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_cpu-expected.txt
@@ -1,13 +1,13 @@
 This is a testharness.js-based test.
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_cpu-expected.txt
index e7ff0b1..86f56a4 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_cpu-expected.txt
@@ -1,13 +1,13 @@
 This is a testharness.js-based test.
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru.https.any.worker_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru.https.any.worker_gpu-expected.txt
index 7bd66cdd..e25dcdf 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru.https.any.worker_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru.https.any.worker_gpu-expected.txt
@@ -1,23 +1,23 @@
 This is a testharness.js-based test.
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru.https.any_gpu-expected.txt
index 7bd66cdd..e25dcdf 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru.https.any_gpu-expected.txt
@@ -1,23 +1,23 @@
 This is a testharness.js-based test.
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_gpu-expected.txt
index b0c7f3d..02934c3 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_gpu-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru_cell.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru_cell.https.any_gpu-expected.txt
index b0c7f3d..02934c3 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru_cell.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gru_cell.https.any_gpu-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_gpu-expected.txt
index a8b053675..9862b6a 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_gpu-expected.txt
@@ -1,27 +1,27 @@
 This is a testharness.js-based test.
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialCellState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm.https.any_gpu-expected.txt
index a8b053675..9862b6a 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm.https.any_gpu-expected.txt
@@ -1,27 +1,27 @@
 This is a testharness.js-based test.
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialCellState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_gpu-expected.txt
index e7ff0b1..86f56a4 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_gpu-expected.txt
@@ -1,13 +1,13 @@
 This is a testharness.js-based test.
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_gpu-expected.txt
index e7ff0b1..86f56a4 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_gpu-expected.txt
@@ -1,13 +1,13 @@
 This is a testharness.js-based test.
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/win10/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt b/third_party/blink/web_tests/platform/win10/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt
deleted file mode 100644
index 2ea65e6..0000000
--- a/third_party/blink/web_tests/platform/win10/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Timing of fullscreenchange and resize events
-  assert_array_equals: event order lengths differ, expected array ["resize", "fullscreenchange"] length 2, got ["fullscreenchange"] length 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win11-arm64/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt b/third_party/blink/web_tests/platform/win11-arm64/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt
deleted file mode 100644
index 2ea65e6..0000000
--- a/third_party/blink/web_tests/platform/win11-arm64/external/wpt/fullscreen/api/element-request-fullscreen-timing-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Timing of fullscreenchange and resize events
-  assert_array_equals: event order lengths differ, expected array ["resize", "fullscreenchange"] length 2, got ["fullscreenchange"] length 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 61c3e59..f594d76 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -592,6 +592,11 @@
     getter namespaceURI
     getter prefix
     method constructor
+interface CSSNestedDeclarations : CSSRule
+    attribute @@toStringTag
+    getter style
+    method constructor
+    setter style
 interface CSSNumericArray
     attribute @@toStringTag
     getter length
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_cpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_cpu-expected.txt
deleted file mode 100644
index a8b053675..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_cpu-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialCellState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any_cpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any_cpu-expected.txt
deleted file mode 100644
index a8b053675..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lstm.https.any_cpu-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialCellState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=1 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-[FAIL] lstm float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any.worker_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any.worker_npu-expected.txt
index 7bd66cdd..e25dcdf 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any.worker_npu-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any.worker_npu-expected.txt
@@ -1,23 +1,23 @@
 This is a testharness.js-based test.
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any_npu-expected.txt
index 7bd66cdd..e25dcdf 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any_npu-expected.txt
@@ -1,23 +1,23 @@
 This is a testharness.js-based test.
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=1 all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with options.bias, options.recurrentBias, options.direction='backward', options.activations=['relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gru float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gru."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gru' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_npu-expected.txt
index b0c7f3d..02934c3 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_npu-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru_cell.https.any.worker_npu-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru_cell.https.any_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru_cell.https.any_npu-expected.txt
index b0c7f3d..02934c3 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru_cell.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru_cell.https.any_npu-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and explicit options.layout='zrn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu'] and and options.layout='rzn'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] gruCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator gruCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'gruCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_npu-expected.txt
index a8b053675..9862b6a 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_npu-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm.https.any.worker_npu-expected.txt
@@ -1,27 +1,27 @@
 This is a testharness.js-based test.
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialCellState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm.https.any_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm.https.any_npu-expected.txt
index a8b053675..9862b6a 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm.https.any_npu-expected.txt
@@ -1,27 +1,27 @@
 This is a testharness.js-based test.
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialHiddenState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.initialCellState
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.returnSequence=false
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.returnSequence=true
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.direction='forward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=1 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=2 with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.direction='backward'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstm float32 tensors steps=2 with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstm."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstm' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_npu-expected.txt
index e7ff0b1..86f56a4 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_npu-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm_cell.https.any.worker_npu-expected.txt
@@ -1,13 +1,13 @@
 This is a testharness.js-based test.
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_npu-expected.txt
index e7ff0b1..86f56a4 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lstm_cell.https.any_npu-expected.txt
@@ -1,13 +1,13 @@
 This is a testharness.js-based test.
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias and options.activations=['relu', 'relu', 'relu']
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.peepholeWeight
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and explicit options.layout='iofg'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with options.bias, options.recurrentBias, options.activations=['relu', 'relu', 'relu'] and options.layout='ifgo'
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 [FAIL] lstmCell float32 tensors with all options
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': Unsupported operator lstmCell."
+  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'lstmCell' on 'MLGraphBuilder': Unsupported data type float32 for argument input, must be one of []."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index f9f6622c..89a97fa 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -62,6 +62,10 @@
 [Worker]     attribute @@toStringTag
 [Worker]     getter available
 [Worker]     method constructor
+[Worker]     method supportsFormat
+[Worker]     method supportsInputLanguage
+[Worker]     method supportsLength
+[Worker]     method supportsType
 [Worker] interface AISummarizerFactory
 [Worker]     attribute @@toStringTag
 [Worker]     method capabilities
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index d09440f..5975b6e 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -61,6 +61,10 @@
     attribute @@toStringTag
     getter available
     method constructor
+    method supportsFormat
+    method supportsInputLanguage
+    method supportsLength
+    method supportsType
 interface AISummarizerFactory
     attribute @@toStringTag
     method capabilities
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 74c3efc..c1ffb3cd 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -62,6 +62,10 @@
 [Worker]     attribute @@toStringTag
 [Worker]     getter available
 [Worker]     method constructor
+[Worker]     method supportsFormat
+[Worker]     method supportsInputLanguage
+[Worker]     method supportsLength
+[Worker]     method supportsType
 [Worker] interface AISummarizerFactory
 [Worker]     attribute @@toStringTag
 [Worker]     method capabilities
diff --git a/third_party/blink/web_tests/wpt_internal/ai/summarize-api.any.js b/third_party/blink/web_tests/wpt_internal/ai/summarize-api.any.js
index a1ba6c3..17a143fe 100644
--- a/third_party/blink/web_tests/wpt_internal/ai/summarize-api.any.js
+++ b/third_party/blink/web_tests/wpt_internal/ai/summarize-api.any.js
@@ -3,6 +3,16 @@
 // META: timeout=long
 
 promise_test(async () => {
+  const capabilities = await ai.summarizer.capabilities();
+  assert_true(capabilities.available == "readily");
+  assert_true(capabilities.supportsType("tl;dr") == "readily");
+  assert_true(capabilities.supportsFormat("plain-text") == "readily");
+  assert_true(capabilities.supportsLength("long") == "readily");
+  assert_true(capabilities.supportsInputLanguage("en") == "readily");
+  assert_true(capabilities.supportsInputLanguage("es") == "no");
+});
+
+promise_test(async () => {
   const summarizer = await ai.summarizer.create();
   const response = await summarizer.summarize(
     "The web-platform-tests Project is a cross-browser test suite for the Web-platform stack. Writing tests in a way that allows them to be run in all browsers gives browser projects confidence that they are shipping software that is compatible with other implementations, and that later implementations will be compatible with their implementations. This in turn gives Web authors/developers confidence that they can actually rely on the Web platform to deliver on the promise of working across browsers and devices without needing extra layers of abstraction to paper over the gaps left by specification editors and implementors.");
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/at-container-snapped-serialization.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/at-container-snapped-serialization.html
index 1c01243..59cc3d37 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/at-container-snapped-serialization.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/at-container-snapped-serialization.html
@@ -20,7 +20,7 @@
 
   const tests = [
       ["scroll-state(snapped: inline)", "Normalize spaces"],
-      ["scroll-STate(snapped: )", "No value - invalid, serializes as <general-enclosed>"],
+      ["scroll-STate(snapped:    )", "No value - invalid, serializes as <general-enclosed>"],
       ["scroll-state(snapped)", "Boolean context"],
       ["scroll-state((snapped: inline) or (snapped: block))", "Logical with 'or'"],
       ["scroll-state (snapped: inline)", "Not a scroll-state function with space before '('"]
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/at-container-stuck-serialization.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/at-container-stuck-serialization.html
index 0de1a5a..3956b01a 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/at-container-stuck-serialization.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/at-container-stuck-serialization.html
@@ -20,7 +20,7 @@
 
   const tests = [
       ["scroll-state(stuck: top)", "Normalize spaces"],
-      ["scroll-STate(stuck: )", "No value - invalid, serializes as <general-enclosed>"],
+      ["scroll-STate(stuck:    )", "No value - invalid, serializes as <general-enclosed>"],
       ["scroll-state(stuck)", "Boolean context"],
       ["scroll-state((stuck: bottom) or (stuck: inset-inline-start))", "Logical with 'or'"],
       ["scroll-state (stuck: top)", "Not a scroll-state function with space before '('"]
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-markers-resize-crash.html b/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-markers-resize-crash.html
new file mode 100644
index 0000000..b69e73c
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-markers-resize-crash.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>CSS Test: ::scroll-markers don't crash on resize</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#scroll-marker-pseudo">
+<style>
+  .carousel {
+    overflow: auto;
+    width: 501px;
+    scroll-marker-group: after;
+    &::scroll-marker-group {
+      display: block;
+    }
+  }
+  .item {
+    width: 1000px;
+    height: 285px;
+    &::scroll-marker {
+      content: "x";
+    }
+  }
+</style>
+<div id="wheee" class="carousel">
+  <div class="item"></div>
+</ul>
+<script>
+  document.body.offsetTop;
+  wheee.scrollLeft = 100;
+  wheee.style.width = "500px";
+  document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
diff --git a/third_party/catapult b/third_party/catapult
index 663fc20..c282fc3 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 663fc204a73a11aaeb16b736f157f3d6369c7a8b
+Subproject commit c282fc3e7b5112f924f5ef5885b5c0c676a017f5
diff --git a/third_party/chromium-variations b/third_party/chromium-variations
index 3aeb8c5..dc907bc 160000
--- a/third_party/chromium-variations
+++ b/third_party/chromium-variations
@@ -1 +1 @@
-Subproject commit 3aeb8c54b12300bc89701e4b2274f026786c38e3
+Subproject commit dc907bcb5f5a617ed495d29d5eb10c1404f80c1e
diff --git a/third_party/dav1d/BUILD.gn b/third_party/dav1d/BUILD.gn
index 7c400d8..615bd4f29 100644
--- a/third_party/dav1d/BUILD.gn
+++ b/third_party/dav1d/BUILD.gn
@@ -117,8 +117,13 @@
     sources = x86_asm_sources
 
     inputs = [
-      "libdav1d/src/ext/x86/x86inc.asm",
       "$platform_config_root/config.asm",
+      "libdav1d/src/ext/x86/x86inc.asm",
+
+      # This is added here in addition to sources as it is included from some
+      # other files. And in such case, we need to use inputs to teach GN that
+      # some actions depend on this file indirectly.
+      "libdav1d/src/x86/filmgrain_common.asm",
     ]
 
     include_dirs = [
diff --git a/third_party/dawn b/third_party/dawn
index adaa316..39fe382 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit adaa316da8417eba6b63f981351c2908341b67dc
+Subproject commit 39fe382afd1e91d1edcb56f7687f1f3052f4aa36
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index eafa1f8..3d8fef7 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit eafa1f8d0782656b27401d12d223eac3e87ef834
+Subproject commit 3d8fef794f4f2b2667cc40d7153aba2141160109
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index b12a19e..8ace176 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit b12a19e856d1c4696a5dd7f078b34a94a0296a82
+Subproject commit 8ace1760c168472d29a83716ca2255909a27aaa6
diff --git a/third_party/ffmpeg b/third_party/ffmpeg
index 3f8a122..468b9c8 160000
--- a/third_party/ffmpeg
+++ b/third_party/ffmpeg
@@ -1 +1 @@
-Subproject commit 3f8a122bfa6c383460c4bf48db654379b68dc959
+Subproject commit 468b9c8a52555953618ba1677fffb0a778dd6d87
diff --git a/third_party/libaom/BUILD.gn b/third_party/libaom/BUILD.gn
index c49d6fc..589e4a5 100644
--- a/third_party/libaom/BUILD.gn
+++ b/third_party/libaom/BUILD.gn
@@ -97,6 +97,11 @@
     }
     defines = [ "CHROMIUM" ]
     include_dirs = libaom_include_dirs
+    inputs = [
+      "$platform_include_dir/config/aom_config.asm",
+      "source/libaom/aom_ports/x86_abi_support.asm",
+      "source/libaom/third_party/x86inc/x86inc.asm",
+    ]
   }
 
   # The following targets are deliberately source_set rather than
diff --git a/third_party/libvpx/BUILD.gn b/third_party/libvpx/BUILD.gn
index 2603ef2..90cf3fe 100644
--- a/third_party/libvpx/BUILD.gn
+++ b/third_party/libvpx/BUILD.gn
@@ -155,7 +155,12 @@
     } else if (current_cpu == "x64") {
       sources = libvpx_srcs_x86_64_assembly
     }
-
+    inputs = [
+      "$platform_include_dir/vpx_config.asm",
+      "source/libvpx/third_party/x86inc/x86inc.asm",
+      "source/libvpx/vpx_dsp/x86/bitdepth_conversion_sse2.asm",
+      "source/libvpx/vpx_ports/x86_abi_support.asm",
+    ]
     defines = [ "CHROMIUM" ]
     if (is_android) {
       # On Android, define __ANDROID__ to use alternative standard library
diff --git a/third_party/lit/v3_0/BUILD.gn b/third_party/lit/v3_0/BUILD.gn
index 125008d..0c110d6 100644
--- a/third_party/lit/v3_0/BUILD.gn
+++ b/third_party/lit/v3_0/BUILD.gn
@@ -37,6 +37,7 @@
     "//chrome/browser/resources/privacy_sandbox/internals/private_state_tokens:build_ts",
     "//chrome/browser/resources/privacy_sandbox/internals/related_website_sets:build_ts",
     "//chrome/browser/resources/profile_internals:build_ts",
+    "//chrome/browser/resources/search_engine_choice:build_ts",
     "//chrome/browser/resources/side_panel/bookmarks:build_ts",
     "//chrome/browser/resources/side_panel/customize_chrome:build_ts",
     "//chrome/browser/resources/side_panel/history_clusters:build_ts",
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index c67fca0..949c15c 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby
-Version: 725c8e83b6dd3e46ad334468f65902a8361a4d92
+Version: 3917415c7a2833dd3122f0b5ab8b7d0fbdcbc0f6
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/nearby/src b/third_party/nearby/src
index 725c8e8..3917415 160000
--- a/third_party/nearby/src
+++ b/third_party/nearby/src
@@ -1 +1 @@
-Subproject commit 725c8e83b6dd3e46ad334468f65902a8361a4d92
+Subproject commit 3917415c7a2833dd3122f0b5ab8b7d0fbdcbc0f6
diff --git a/third_party/openscreen/README.chromium b/third_party/openscreen/README.chromium
index 447d9122..acb0f8a 100644
--- a/third_party/openscreen/README.chromium
+++ b/third_party/openscreen/README.chromium
@@ -1,7 +1,7 @@
 Name: Open Screen Protocol Library
 Short Name: openscreen
 URL: https://chromium.googlesource.com/openscreen
-Version: 0
+Version: N/A
 Revision: 9499a256bc609e9efdf15a268fd047127870af41
 License: BSD
 License File: src/LICENSE
diff --git a/third_party/openscreen/src b/third_party/openscreen/src
index 25b92ba..351bb78 160000
--- a/third_party/openscreen/src
+++ b/third_party/openscreen/src
@@ -1 +1 @@
-Subproject commit 25b92ba79ba8cf1b4a56cc4bf9219245e7e6d140
+Subproject commit 351bb789b665a9f3a90a76d456a7fd493afeadb0
diff --git a/third_party/pdfium b/third_party/pdfium
index aec00b5..6cf6936 160000
--- a/third_party/pdfium
+++ b/third_party/pdfium
@@ -1 +1 @@
-Subproject commit aec00b5fe296ad2edec9466e5bc59603ef776f5a
+Subproject commit 6cf693686f2253201c49a68b3dc10504d199ac1d
diff --git a/third_party/perfetto b/third_party/perfetto
index f2aa5c0..c0a4867 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit f2aa5c0df5f4b73f14b024288da6f900955b118a
+Subproject commit c0a4867c020aad93d2707c52d6599f6a2c504be0
diff --git a/third_party/polymer/v3_0/BUILD.gn b/third_party/polymer/v3_0/BUILD.gn
index dffdf173..088daeef 100644
--- a/third_party/polymer/v3_0/BUILD.gn
+++ b/third_party/polymer/v3_0/BUILD.gn
@@ -126,7 +126,6 @@
     "//chrome/browser/resources/pdf:build_ts",
     "//chrome/browser/resources/print_preview:build_ts",
     "//chrome/browser/resources/privacy_sandbox:build_ts",
-    "//chrome/browser/resources/search_engine_choice:build_ts",
     "//chrome/browser/resources/settings:build_ts",
     "//chrome/browser/resources/settings_shared:build_ts",
     "//chrome/browser/resources/side_panel/bookmarks:build_ts",
diff --git a/third_party/skia b/third_party/skia
index 5515c08..dd985e9 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 5515c08c2e444dca4d18fa252ea5e629f27850aa
+Subproject commit dd985e9faf6c7d57ad626ed7cbd46cb1dc532a79
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src
index b21dda0..e1782d6 160000
--- a/third_party/spirv-tools/src
+++ b/third_party/spirv-tools/src
@@ -1 +1 @@
-Subproject commit b21dda0ee7a3ea4e0192a7b2b09db1df1de9d5e7
+Subproject commit e1782d6675b88225225e331a6318554d473c54db
diff --git a/third_party/tflite/BUILD.gn b/third_party/tflite/BUILD.gn
index de0c835..78a63109 100644
--- a/third_party/tflite/BUILD.gn
+++ b/third_party/tflite/BUILD.gn
@@ -32,6 +32,15 @@
   cc_generator_options = "lite=true:"
 }
 
+proto_library("tflite_tools_proto") {
+  proto_in_dir = "src"
+  sources = [
+    "src/tensorflow/core/example/example.proto",
+    "src/tensorflow/core/example/feature.proto",
+  ]
+  cc_generator_options = "lite=true:"
+}
+
 config("tflite_config") {
   include_dirs = [
     "$root_gen_dir/third_party/tflite/src",
@@ -612,17 +621,21 @@
 # tflite version.
 tflite_static_library("tflite") {
   sources = [
+    "src/tensorflow/compiler/mlir/lite/allocation.cc",
+    "src/tensorflow/compiler/mlir/lite/allocation.h",
+    "src/tensorflow/compiler/mlir/lite/core/api/error_reporter.cc",
+    "src/tensorflow/compiler/mlir/lite/core/api/error_reporter.h",
     "src/tensorflow/compiler/mlir/lite/core/model_builder_base.cc",
     "src/tensorflow/compiler/mlir/lite/core/model_builder_base.h",
     "src/tensorflow/compiler/mlir/lite/experimental/remat/metadata_util.cc",
     "src/tensorflow/compiler/mlir/lite/experimental/remat/metadata_util.h",
+    "src/tensorflow/compiler/mlir/lite/schema/schema_generated.h",
     "src/tensorflow/compiler/mlir/lite/schema/schema_utils.cc",
     "src/tensorflow/compiler/mlir/lite/utils/string_utils.cc",
     "src/tensorflow/lite/acceleration/configuration/flatbuffer_to_proto.cc",
     "src/tensorflow/lite/acceleration/configuration/flatbuffer_to_proto.h",
     "src/tensorflow/lite/acceleration/configuration/proto_to_flatbuffer.cc",
     "src/tensorflow/lite/acceleration/configuration/proto_to_flatbuffer.h",
-    "src/tensorflow/lite/allocation.cc",
     "src/tensorflow/lite/arena_planner.cc",
     "src/tensorflow/lite/array.cc",
     "src/tensorflow/lite/builtin_ops.h",
@@ -630,8 +643,6 @@
     "src/tensorflow/lite/c/common_internal.h",
     "src/tensorflow/lite/core/acceleration/configuration/delegate_registry.cc",
     "src/tensorflow/lite/core/acceleration/configuration/delegate_registry.h",
-    "src/tensorflow/lite/core/api/error_reporter.cc",
-    "src/tensorflow/lite/core/api/error_reporter.h",
     "src/tensorflow/lite/core/api/flatbuffer_conversions.cc",
     "src/tensorflow/lite/core/api/flatbuffer_conversions.h",
     "src/tensorflow/lite/core/api/op_resolver.cc",
@@ -750,9 +761,10 @@
   }
 
   if (is_win) {
-    sources += [ "src/tensorflow/lite/mmap_allocation_disabled.cc" ]
+    sources +=
+        [ "src/tensorflow/compiler/mlir/lite/mmap_allocation_disabled.cc" ]
   } else {
-    sources += [ "src/tensorflow/lite/mmap_allocation.cc" ]
+    sources += [ "src/tensorflow/compiler/mlir/lite/mmap_allocation.cc" ]
   }
 
   if (is_ios) {
@@ -851,6 +863,7 @@
   deps = [
     ":tflite_builtin_op_resolver_standalone",
     ":tflite_standalone",
+    ":tflite_tools_proto",
     "//third_party/abseil-cpp:absl",
     "//third_party/ruy",
   ]
diff --git a/third_party/tflite/README.chromium b/third_party/tflite/README.chromium
index cb1d26e..c3a9b4d 100644
--- a/third_party/tflite/README.chromium
+++ b/third_party/tflite/README.chromium
@@ -1,8 +1,8 @@
 Name: TensorFlow Lite
 Short Name: tflite
 URL: https://github.com/tensorflow/tensorflow
-Version: 3322062409382997beb3280ea6e130c3f5cc6a86
-Date: 2024-08-05
+Version: 0109742147fab2b753a92090f0480a125a93818b
+Date: 2024-08-30
 License: Apache 2.0
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/tflite/src b/third_party/tflite/src
index 3322062..0109742 160000
--- a/third_party/tflite/src
+++ b/third_party/tflite/src
@@ -1 +1 @@
-Subproject commit 3322062409382997beb3280ea6e130c3f5cc6a86
+Subproject commit 0109742147fab2b753a92090f0480a125a93818b
diff --git a/third_party/turbine/.gitignore b/third_party/turbine/.gitignore
new file mode 100644
index 0000000..a85c0d22
--- /dev/null
+++ b/third_party/turbine/.gitignore
@@ -0,0 +1 @@
+/cipd/
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 5e29f5c..7254991 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 5e29f5c97fc482e48e04309b4460b0d9be3804f3
+Subproject commit 725499142cb601efc3f66bdb16d75843c0760478
diff --git a/third_party/vulkan-loader/src b/third_party/vulkan-loader/src
index d25bd69..c758bac 160000
--- a/third_party/vulkan-loader/src
+++ b/third_party/vulkan-loader/src
@@ -1 +1 @@
-Subproject commit d25bd69eb7d386bc584cbca4f0f005c7ee57535f
+Subproject commit c758bac8bf1580b5018adafd3a2ec709237b0134
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index a44622e..af7b0a3 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit a44622ed6e1dfaa2754a65eec35ef1b19ec3e782
+Subproject commit af7b0a35d009b5ad6e0b280a5b81388608ebfe39
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index 07d31bd..39d1b23 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit 07d31bdbc0cdde5dc561d563d4898f6ed7dae38c
+Subproject commit 39d1b236b2c57db4725a46e1e95b066572d095f5
diff --git a/third_party/webrtc b/third_party/webrtc
index 55ed950..64d68c3 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 55ed9501d22f8f4e3b93714b790ef15ed3504314
+Subproject commit 64d68c3984e80ab0e7ac56d9c745fc4498708cd0
diff --git a/tools/android/avd/proto_creation/android_34_desktop_x64.textpb b/tools/android/avd/proto_creation/android_34_desktop_x64.textpb
new file mode 100644
index 0000000..6d232ad
--- /dev/null
+++ b/tools/android/avd/proto_creation/android_34_desktop_x64.textpb
@@ -0,0 +1,44 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Configuration for an Android-14 (U, API 34) AVD on android-desktop on x86_64
+
+emulator_package {
+  package_name: "chromium/third_party/android_sdk/public/emulator"
+  version: "5biVQLkXXVm61beRTyVfmPBrmjEJFBJTpdzAUcXY378C"  # 34.2.16 (Stable)
+  dest_path: "android_34_desktop_x64"
+}
+
+system_image_package {
+  package_name: "chromium/third_party/android_sdk/public/system-images/android-34/android-desktop/x86_64"
+  version: "EqbVO7i2XT5Gg8np0kH9aFeAJutciHj7V7lYk7NKNWkC"  # r1
+  dest_path: "android_34_desktop_x64"
+}
+system_image_name: "system-images;android-34;android-desktop;x86_64"
+
+avd_package {
+  package_name: "chromium/third_party/android_sdk/public/avds/android-34/android-desktop/x86_64"
+  dest_path: "android_34_desktop_x64"
+}
+avd_name: "android_34_desktop_x64"
+
+avd_settings {
+  # Small desktop - 14", 1366x768, mdpi
+  screen {
+    density: 160
+    height: 768
+    width: 1366
+  }
+  advanced_features {
+    key: "GLESDynamicVersion"
+    value: "on"
+  }
+}
+
+min_sdk: 34
+additional_apk {
+  package_name: "chrome_internal/third_party/google3/apks/gmscore/x86_64"
+  version: "5mF806AbGnsm29dfwLgz7FkLBXv2l2POLvQ2nojUlhgC" # 24.26.32
+  dest_path: "android_34_google_apis_x64/gmscore_apks"
+}
diff --git a/tools/bisect-builds.py b/tools/bisect-builds.py
index b3215d5cb..9582a10 100755
--- a/tools/bisect-builds.py
+++ b/tools/bisect-builds.py
@@ -687,26 +687,16 @@
     (stdout, stderr) = subproc.communicate()
     return subproc.returncode, stdout, stderr
 
-  def run_revision(self, download, args=()):
+  def run_revision(self, download, tempdir, args=()):
     """Run downloaded archive"""
-    # Create a temp directory and unzip the revision into it.
-    with tempfile.TemporaryDirectory(prefix='bisect_tmp') as tempdir:
-      # On Windows 10, file system needs to be readable from App Container.
-      if sys.platform == 'win32' and platform.release() == '10':
-        icacls_cmd = ['icacls', tempdir, '/grant', '*S-1-15-2-2:(OI)(CI)(RX)']
-        proc = subprocess.Popen(icacls_cmd,
-                                bufsize=0,
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE)
-        proc.communicate()
-      executable = self._install_revision(download, tempdir)
-      result = None
-      for _ in range(self.num_runs):
-        returncode, _, _ = result = self._launch_revision(
-            tempdir, executable, args)
-        if returncode:
-          break
-      return result
+    executable = self._install_revision(download, tempdir)
+    result = None
+    for _ in range(self.num_runs):
+      returncode, _, _ = result = self._launch_revision(tempdir, executable,
+                                                        args)
+      if returncode:
+        break
+    return result
 
 
 class LooseVersion(BaseLooseVersion):
@@ -1351,10 +1341,33 @@
   RunGsutilCommand(command)
 
 
-def RunRevision(archive_build, revision, zip_file, args):
-  """Given a zipped revision, unzip it and run the test."""
-  print('Trying revision %s...' % str(revision))
-  return archive_build.run_revision(zip_file, args)
+def EvaluateRevision(archive_build, download, revision, args, evaluate):
+  """fetch.wait_for(), archive_build.run_revision() and evaluate the result."""
+  while True:
+    exit_status = stdout = stderr = None
+    # Create a temp directory and unzip the revision into it.
+    with tempfile.TemporaryDirectory(prefix='bisect_tmp') as tempdir:
+      # On Windows 10, file system needs to be readable from App Container.
+      if sys.platform == 'win32' and platform.release() == '10':
+        icacls_cmd = ['icacls', tempdir, '/grant', '*S-1-15-2-2:(OI)(CI)(RX)']
+        proc = subprocess.Popen(icacls_cmd,
+                                bufsize=0,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE)
+        proc.communicate()
+      # run_revision
+      print(f'Trying revision {revision!s}: {download!s} in {tempdir!s}')
+      try:
+        exit_status, stdout, stderr = archive_build.run_revision(
+            download, tempdir, args)
+      except SystemExit:
+        raise
+      except Exception:
+        traceback.print_exc(file=sys.stderr)
+      # evaluate
+      answer = evaluate(revision, exit_status, stdout, stderr)
+      if answer != 'r':
+        return answer
 
 
 # The arguments release_builds, status, stdout and stderr are unused.
@@ -1516,23 +1529,6 @@
       raise
 
 
-def VerifyEndpoint(fetch, archive_build, rev, try_args, evaluate,
-                   expected_answer):
-  zip_file = fetch.wait_for()
-  try:
-    (exit_status, stdout, stderr) = RunRevision(archive_build, rev, zip_file,
-                                                try_args)
-  except Exception as e:
-    if not isinstance(e, SystemExit):
-      traceback.print_exc(file=sys.stderr)
-    exit_status = None
-    stdout = None
-    stderr = None
-  if (evaluate(rev, exit_status, stdout, stderr) != expected_answer):
-    print('Unexpected result at a range boundary! Your range is not correct.')
-    raise SystemExit
-
-
 def Bisect(archive_build,
            try_args=(),
            evaluate=AskIsGoodBuild,
@@ -1560,163 +1556,108 @@
     - If rev 50 is bad, the download of rev 75 is cancelled, and the next test
       is run on rev 25.
   """
-  good_rev = archive_build.good_revision
-  bad_rev = archive_build.bad_revision
-
   print('Downloading list of known revisions.', end=' ')
   print('If the range is large, this can take several minutes...')
   if not archive_build.use_local_cache:
     print('(use --use-local-cache to cache and re-use the list of revisions)')
   else:
     print()
-  revlist = archive_build.get_rev_list()
-
-  # Figure out our bookends and first pivot point; fetch the pivot revision.
-  minrev = 0
-  maxrev = len(revlist) - 1
-  pivot = maxrev // 2
-  rev = revlist[pivot]
-  fetch = archive_build.get_download_job(rev, 'initial_fetch').start()
+  rev_list = archive_build.get_rev_list()
+  # Ensure rev_list[0] is good and rev_list[-1] is bad for easier process.
+  if archive_build.good_revision > archive_build.bad_revision:
+    rev_list = rev_list[::-1]
 
   if verify_range:
-    minrev_fetch = archive_build.get_download_job(revlist[minrev],
-                                                  'minrev_fetch').start()
-    maxrev_fetch = archive_build.get_download_job(revlist[maxrev],
-                                                  'maxrev_fetch').start()
+    good_rev_fetch = archive_build.get_download_job(rev_list[0],
+                                                    'good_rev_fetch').start()
+    bad_rev_fetch = archive_build.get_download_job(rev_list[-1],
+                                                   'bad_rev_fetch').start()
     try:
-      VerifyEndpoint(minrev_fetch, archive_build, revlist[minrev], try_args,
-                     evaluate, 'b' if bad_rev < good_rev else 'g')
-      VerifyEndpoint(maxrev_fetch, archive_build, revlist[maxrev], try_args,
-                     evaluate, 'g' if bad_rev < good_rev else 'b')
+      good_download = good_rev_fetch.wait_for()
+      answer = EvaluateRevision(archive_build, good_download, rev_list[0],
+                                try_args, evaluate)
+      if answer != 'g':
+        print(f'Expecting revision {rev_list[0]} to be good but got {answer}. '
+              'Please make sure the --good is a good revision.')
+        raise SystemExit
+      bad_download = bad_rev_fetch.wait_for()
+      answer = EvaluateRevision(archive_build, bad_download, rev_list[-1],
+                                try_args, evaluate)
+      if answer != 'b':
+        print(f'Expecting revision {rev_list[-1]} to be bad but got {answer}. '
+              'Please make sure that the issue can be reproduced for --bad.')
+        raise SystemExit
     except (KeyboardInterrupt, SystemExit):
       print('Cleaning up...')
-      fetch.stop()
-      sys.exit(0)
+      return None, None
     finally:
-      minrev_fetch.stop()
-      maxrev_fetch.stop()
+      good_rev_fetch.stop()
+      bad_rev_fetch.stop()
 
-  fetch.wait_for()
-
-  # Binary search time!
-  prefetch_revisions = True
-  while fetch and maxrev - minrev > 1:
-    if bad_rev < good_rev:
-      min_str, max_str = 'bad', 'good'
-    else:
-      min_str, max_str = 'good', 'bad'
-    print('You have about %d more steps left.' %
-          ((maxrev - minrev).bit_length() - 1))
-    print('Bisecting range [%s (%s), %s (%s)].' %
-          (revlist[minrev], min_str, revlist[maxrev], max_str))
-
-    # Pre-fetch next two possible pivots
-    #   - down_pivot is the next revision to check if the current revision turns
-    #     out to be bad.
-    #   - up_pivot is the next revision to check if the current revision turns
-    #     out to be good.
-    down_pivot = int((pivot - minrev) / 2) + minrev
-    down_fetch = None
-    if prefetch_revisions:
-      if down_pivot != pivot and down_pivot != minrev:
-        down_rev = revlist[down_pivot]
-        down_fetch = archive_build.get_download_job(down_rev,
-                                                    'down_fetch').start()
-    up_pivot = int((maxrev - pivot) / 2) + pivot
-    up_fetch = None
-    if prefetch_revisions:
-      if up_pivot != pivot and up_pivot != maxrev:
-        up_rev = revlist[up_pivot]
-        up_fetch = archive_build.get_download_job(up_rev, 'up_fetch').start()
-
-    # Run test on the pivot revision.
-    exit_status = None
-    stdout = None
-    stderr = None
-    try:
-      zip_file = fetch.wait_for()
-      (exit_status, stdout, stderr) = RunRevision(archive_build, rev, zip_file,
-                                                  try_args)
-    except SystemExit:
-      raise
-    except Exception:
-      traceback.print_exc(file=sys.stderr)
-
-    # Call the evaluate function to see if the current revision is good or bad.
-    # On that basis, kill one of the background downloads and complete the
-    # other, as described in the comments above.
-    try:
-      answer = evaluate(rev, exit_status, stdout, stderr)
-      prefetch_revisions = True
-      if ((answer == 'g' and good_rev < bad_rev)
-          or (answer == 'b' and bad_rev < good_rev)):
-        fetch.stop()
-        minrev = pivot
-        if down_fetch:
-          down_fetch.stop()  # Kill the download of the older revision.
-          fetch = None
-        if up_fetch:
-          up_fetch.wait_for()
-          pivot = up_pivot
-          fetch = up_fetch
-      elif ((answer == 'b' and good_rev < bad_rev)
-            or (answer == 'g' and bad_rev < good_rev)):
-        fetch.stop()
-        maxrev = pivot
-        if up_fetch:
-          up_fetch.stop()  # Kill the download of the newer revision.
-          fetch = None
-        if down_fetch:
-          down_fetch.wait_for()
-          pivot = down_pivot
-          fetch = down_fetch
-      elif answer == 'r':
-        # Don't redundantly prefetch.
-        prefetch_revisions = False
-      elif answer == 'u':
-        # Nuke the revision from the revlist and choose a new pivot.
-        fetch.stop()
-        revlist.pop(pivot)
-        maxrev -= 1  # Assumes maxrev >= pivot.
-
-        if maxrev - minrev > 1:
-          # Alternate between using down_pivot or up_pivot for the new pivot
-          # point, without affecting the range. Do this instead of setting the
-          # pivot to the midpoint of the new range because adjacent revisions
-          # are likely affected by the same issue that caused the (u)nknown
-          # response.
-          if up_fetch and down_fetch:
-            fetch = [up_fetch, down_fetch][len(revlist) % 2]
-          elif up_fetch:
-            fetch = up_fetch
-          else:
-            fetch = down_fetch
-          fetch.wait_for()
-          if fetch == up_fetch:
-            pivot = up_pivot - 1  # Subtracts 1 because revlist was resized.
-          else:
-            pivot = down_pivot
-
-        if down_fetch and fetch != down_fetch:
-          down_fetch.stop()
-        if up_fetch and fetch != up_fetch:
-          up_fetch.stop()
+  prefetch = {}
+  try:
+    while len(rev_list) > 2:
+      print('You have %d revisions with about %d steps left.' %
+            (len(rev_list), (len(rev_list).bit_length() - 1)))
+      print('Bisecting range [%s (bad), %s (good)].' %
+            (rev_list[0], rev_list[-1]))
+      # clean prefetch to keep only the valid fetches
+      for key in list(prefetch.keys()):
+        if key not in rev_list:
+          prefetch.pop(key).stop()
+      # get next revision to evaluate from prefetch
+      if prefetch:
+        fetch = None
+        # For any possible index in rev_list, abs(mid - index) < abs(mid -
+        # pivot). This will ensure that we can always get a fetch from prefetch.
+        pivot = len(rev_list)
+        for revision, pfetch in prefetch.items():
+          prefetch_pivot = rev_list.index(revision)
+          # Prefer the revision closer to the mid point.
+          mid_point = len(rev_list) // 2
+          if abs(mid_point - pivot) > abs(mid_point - prefetch_pivot):
+            fetch = pfetch
+            pivot = prefetch_pivot
+        prefetch.pop(rev_list[pivot])
+      # or just the mid point
       else:
-        assert False, 'Unexpected return value from evaluate(): ' + answer
-    except (KeyboardInterrupt, SystemExit):
-      print('Cleaning up...')
-      if fetch:
+        pivot = len(rev_list) // 2
+        fetch = archive_build.get_download_job(rev_list[pivot], 'fetch').start()
+      # prefetch left_pivot = len(rev_list[:pivot+1]) // 2
+      left_revision = rev_list[(pivot + 1) // 2]
+      if left_revision != rev_list[0] and left_revision not in prefetch:
+        prefetch[left_revision] = archive_build.get_download_job(
+            left_revision, 'prefetch').start()
+      # prefetch right_pivot = len(rev_list[pivot:]) // 2
+      right_revision = rev_list[(len(rev_list) + pivot) // 2]
+      if right_revision != rev_list[-1] and right_revision not in prefetch:
+        prefetch[right_revision] = archive_build.get_download_job(
+            right_revision, 'prefetch').start()
+      try:
+        # evaluate the revision
+        download = fetch.wait_for()
+        answer = EvaluateRevision(archive_build, download, rev_list[pivot],
+                                  try_args, evaluate)
+        # Ensure rev_list[0] is good and rev_list[-1] is bad after adjust.
+        if answer == 'g':  # good
+          rev_list = rev_list[pivot:]
+        elif answer == 'b':  # bad
+          # Retain the pivot element within the list to act as a confirmed
+          # boundary for identifying bad revisions.
+          rev_list = rev_list[:pivot + 1]
+        elif answer == 'u':  # unknown
+          # Nuke the revision from the rev_list.
+          rev_list.pop(pivot)
+        else:
+          assert False, 'Unexpected return value from evaluate(): ' + answer
+      finally:
         fetch.stop()
-      if down_fetch:
-        down_fetch.stop()
-      if up_fetch:
-        up_fetch.stop()
-      sys.exit(0)
-
-    rev = revlist[pivot]
-
-  return (revlist[minrev], revlist[maxrev])
-
+    # end of `while len(rev_list) > 2`
+  finally:
+    for each in prefetch.values():
+      each.stop()
+    prefetch.clear()
+  return sorted((rev_list[0], rev_list[-1]))
 
 def GetChromiumRevision(url, default=999999999):
   """Returns the chromium revision read from given URL."""
@@ -2198,7 +2139,8 @@
 
   min_chromium_rev, max_chromium_rev = Bisect(archive_build, args, evaluator,
                                               opts.verify_range)
-
+  if min_chromium_rev is None or max_chromium_rev is None:
+    return
   # We're done. Let the user know the results in an official manner.
   if good_rev > bad_rev:
     print(DONE_MESSAGE_GOOD_MAX %
diff --git a/tools/bisect_test.py b/tools/bisect_test.py
index c4a61f8..a243d0e 100644
--- a/tools/bisect_test.py
+++ b/tools/bisect_test.py
@@ -9,6 +9,7 @@
 import re
 import subprocess
 import sys
+import tempfile
 import unittest
 from unittest.mock import ANY, Mock, MagicMock, mock_open, patch, call
 
@@ -67,6 +68,22 @@
 
   max_rev = 10000
 
+  def setUp(self):
+    self.patchers = []
+    self.patchers.append(patch('bisect-builds.DownloadJob._fetch'))
+    self.patchers.append(
+        patch('bisect-builds.ArchiveBuild.run_revision',
+              return_value=(0, '', '')))
+    self.patchers.append(
+        patch('bisect-builds.SnapshotBuild._get_rev_list',
+              return_value=range(self.max_rev)))
+    for each in self.patchers:
+      each.start()
+
+  def tearDown(self):
+    for each in self.patchers:
+      each.stop()
+
   def bisect(self, good_rev, bad_rev, evaluate, num_runs=1):
     options, args = bisect_builds.ParseCommandLine([
         '-a', 'linux64', '-g', good_rev, '-b', bad_rev, '--times',
@@ -78,17 +95,47 @@
                                             try_args=args)
     return (minrev, maxrev)
 
-  @patch('bisect-builds.DownloadJob._fetch')
-  @patch('bisect-builds.ArchiveBuild.run_revision', return_value=(0, '', ''))
-  @patch('bisect-builds.SnapshotBuild._get_rev_list',
-         return_value=range(max_rev))
-  def testBisectConsistentAnswer(self, mock_get_rev_list, mock_run_revision,
-                                 mock_fetch):
+  def testBisectConsistentAnswer(self):
     self.assertEqual(self.bisect(1000, 100, lambda *args: 'g'), (100, 101))
     self.assertEqual(self.bisect(100, 1000, lambda *args: 'b'), (100, 101))
     self.assertEqual(self.bisect(2000, 200, lambda *args: 'b'), (1999, 2000))
     self.assertEqual(self.bisect(200, 2000, lambda *args: 'g'), (1999, 2000))
 
+  @patch('bisect-builds.ArchiveBuild.run_revision', return_value=(0, '', ''))
+  def test_bisect_should_retry(self, mock_run_revision):
+    evaluator = Mock(side_effect='rgrgrbr')
+    self.assertEqual(self.bisect(9, 1, evaluator), (2, 3))
+    tested_revisions = [c.args[0] for c in evaluator.call_args_list]
+    self.assertEqual(tested_revisions, [5, 5, 3, 3, 2, 2])
+    self.assertEqual(mock_run_revision.call_count, 6)
+
+    evaluator = Mock(side_effect='rgrrrgrbr')
+    self.assertEqual(self.bisect(1, 10, evaluator), (8, 9))
+    tested_revisions = [c.args[0] for c in evaluator.call_args_list]
+    self.assertEqual(tested_revisions, [6, 6, 8, 8, 8, 8, 9, 9])
+
+  def test_bisect_should_unknown(self):
+    evaluator = Mock(side_effect='uuuggggg')
+    self.assertEqual(self.bisect(9, 1, evaluator), (1, 2))
+    tested_revisions = [c.args[0] for c in evaluator.call_args_list]
+    self.assertEqual(tested_revisions, [5, 3, 6, 7, 2])
+
+    evaluator = Mock(side_effect='uuugggggg')
+    self.assertEqual(self.bisect(1, 9, evaluator), (8, 9))
+    tested_revisions = [c.args[0] for c in evaluator.call_args_list]
+    self.assertEqual(tested_revisions, [5, 7, 4, 3, 8])
+
+  def test_bisect_should_quit(self):
+    evaluator = Mock(side_effect=SystemExit())
+    with self.assertRaises(SystemExit):
+      self.assertEqual(self.bisect(9, 1, evaluator), (None, None))
+
+  def test_edge_cases(self):
+    with self.assertRaises(bisect_builds.BisectException):
+      self.assertEqual(self.bisect(1, 1, Mock()), (1, 1))
+    self.assertEqual(self.bisect(2, 1, Mock()), (1, 2))
+    self.assertEqual(self.bisect(1, 2, Mock()), (1, 2))
+
 
 class DownloadJobTest(BisectTestCase):
 
@@ -261,7 +308,7 @@
   def test_run_revision_should_return_early(self, mock_launch_revision,
                                             mock_install_revision):
     build = self.create_build()
-    build.run_revision('', [])
+    build.run_revision('', '', [])
     mock_launch_revision.assert_called_once()
 
   @patch('bisect-builds.ArchiveBuild._install_revision')
@@ -271,7 +318,7 @@
                                            mock_install_revision):
     build = self.create_build(
         ['-a', 'linux64', '-g', '0', '-b', '9', '--time', '10'])
-    build.run_revision('', [])
+    build.run_revision('', '', [])
     self.assertEqual(mock_launch_revision.call_count, 10)
 
   @patch('bisect-builds.UnzipFilenameToDir')
@@ -804,7 +851,8 @@
     self.assertIsInstance(build, bisect_builds.AndroidTrichromeOfficialBuild)
     download_job = build.get_download_job(1334339)
     zip_file = download_job.start().wait_for()
-    build.run_revision(zip_file, [])
+    with tempfile.TemporaryDirectory(prefix='bisect_tmp') as tempdir:
+      build.run_revision(zip_file, tempdir, [])
     print(mock_InstallOnAndroid.call_args_list)
     self.assertRegex(mock_InstallOnAndroid.mock_calls[0].args[1],
                      'full-build-linux/apks/TrichromeLibraryGoogle6432.apk$')
@@ -934,7 +982,8 @@
     self.assertIsInstance(build, bisect_builds.IOSReleaseBuild)
     job = build.get_download_job('127.0.6533.76')
     ipa = job.start().wait_for()
-    build.run_revision(ipa, args)
+    with tempfile.TemporaryDirectory(prefix='bisect_tmp') as tempdir:
+      build.run_revision(ipa, tempdir, args)
     mock_run.assert_has_calls([
         call([
             'xcrun', 'devicectl', 'device', 'install', 'app', '--device', '321',
diff --git a/tools/clang/raw_ptr_plugin/RawPtrHelpers.h b/tools/clang/raw_ptr_plugin/RawPtrHelpers.h
index 74c3c1f..6ef2bb08 100644
--- a/tools/clang/raw_ptr_plugin/RawPtrHelpers.h
+++ b/tools/clang/raw_ptr_plugin/RawPtrHelpers.h
@@ -384,14 +384,18 @@
       field_decl = field_decls[i];
     }
 
-    clang::ast_matchers::internal::BoundNodesTreeBuilder field_matches(
-        *Builder);
-    if (field_decl_matcher.matches(*field_decl, Finder, &field_matches)) {
-      clang::ast_matchers::internal::BoundNodesTreeBuilder expr_matches(
-          field_matches);
-      if (init_expr_matcher.matches(*expr, Finder, &expr_matches)) {
-        result.addMatch(expr_matches);
-        is_matching = true;
+    // `field_decl` might be equal to `nullptr`. In the case, we should not
+    // run `field_decl_matcher`.
+    if (field_decl) {
+      clang::ast_matchers::internal::BoundNodesTreeBuilder field_matches(
+          *Builder);
+      if (field_decl_matcher.matches(*field_decl, Finder, &field_matches)) {
+        clang::ast_matchers::internal::BoundNodesTreeBuilder expr_matches(
+            field_matches);
+        if (init_expr_matcher.matches(*expr, Finder, &expr_matches)) {
+          result.addMatch(expr_matches);
+          is_matching = true;
+        }
       }
     }
   }
diff --git a/tools/clang/spanify/Spanifier.cpp b/tools/clang/spanify/Spanifier.cpp
index 4215bc5..88a581c9 100644
--- a/tools/clang/spanify/Spanifier.cpp
+++ b/tools/clang/spanify/Spanifier.cpp
@@ -529,6 +529,57 @@
   assert(false && "Unable to determine array size.");
 }
 
+// Checks if the given array definition involves an unnamed struct type
+// or is declared inline within a struct/class definition.
+//
+// These cases currently pose challenges for the C array to std::array
+// conversion and are therefore skipped by the tool.
+//
+// Examples of problematic definitions:
+//   - Unnamed struct:
+//     `struct { int x, y; } point_array[10];`
+//   - Inline definition:
+//     `struct Point { int x, y; } inline_points[5];`
+//
+// Returns true if the definition is unnamed or inline, false otherwise.
+bool IsUnnamedOrInlinedDefinition(const std::string& element_type,
+                                  const std::string& variable_name,
+                                  const clang::SourceRange replacement_range,
+                                  const clang::SourceManager& source_manager,
+                                  const clang::ASTContext& ast_context) {
+  // Look for unnamed types. In future we could look for the ending ')' and
+  // replace it with a new type name if we determine how to split into two.
+  if (element_type.find("(unnamed struct") != std::string::npos) {
+    return true;
+  }
+
+  // Extract the source code within the replacement range.
+  // If it contains the class/struct definition itself, we cannot perform the
+  // rewrite.
+  const auto& lang_opts = ast_context.getLangOpts();
+  std::string initial_text =
+      clang::Lexer::getSourceText(
+          clang::CharSourceRange::getCharRange(replacement_range),
+          source_manager, lang_opts)
+          .str();
+
+  // Recall that inline definitions are of the form:
+  // struct TypeName { <body> } variable_name;
+  // So below we see if the location of variable_name (which has to be in the
+  // replacement_range) is after the first occurrence of a '}' bracket (if it
+  // exists). This would mean we have a class/struct definition with an inline
+  // variable and we can't rewrite without breaking into two separate nodes.
+  assert(initial_text.find(variable_name) != std::string::npos);
+  const size_t bracket_location = initial_text.find("}");
+  if (bracket_location != std::string::npos &&
+      initial_text.find(variable_name) > bracket_location) {
+    // The class definition is then:
+    // initial_text.substr(0, bracket_location + 1)
+    return true;
+  }
+  return false;
+}
+
 // Creates a replacement node for c-style arrays on which we invoke operator[].
 // These arrays are rewritten to std::array<Type, Size>.
 Node getNodeFromArrayType(const MatchFinder::MatchResult& result) {
@@ -548,16 +599,29 @@
   printing_policy.PrintCanonicalTypes = 1;
   std::string element_type_as_string =
       element_type.getAsString(printing_policy);
-
   std::string array_size_as_string = getArraySize(result);
-  std::string replacement_text =
-      llvm::formatv("std::array<{0},{1}>{2}", element_type_as_string,
-                    array_size_as_string, array_variable->getNameAsString());
+  std::string array_variable_as_string = array_variable->getNameAsString();
 
   clang::SourceRange replacement_range = {
       array_type_loc->getSourceRange().getBegin(),
       array_type_loc->getSourceRange().getEnd().getLocWithOffset(1)};
 
+  if (IsUnnamedOrInlinedDefinition(element_type_as_string,
+                                   array_variable_as_string, replacement_range,
+                                   source_manager, ast_context)) {
+    // TODO(362644557): Handle unnamed types more reasonably, perhaps by
+    // inserting the variable name as the type but capitalized. Also figure out
+    // how to write a replacement that generates multiple output nodes.
+    // We've tried making the replacement emit the class definition with a
+    // semi-colon between to separate the inline definition but this hit an
+    // assertion node in extract_edits.
+    return Node{};
+  }
+
+  std::string replacement_text =
+      llvm::formatv("std::array<{0},{1}>{2}", element_type_as_string,
+                    array_size_as_string, array_variable_as_string);
+
   auto replacement_and_include_pair = GetReplacementAndIncludeDirectives(
       replacement_range, replacement_text, source_manager, "array",
       /* is_system_include_header =*/true);
diff --git a/tools/clang/spanify/tests/structs-with-inline-arrays-expected.cc b/tools/clang/spanify/tests/structs-with-inline-arrays-expected.cc
new file mode 100644
index 0000000..cb2de68548
--- /dev/null
+++ b/tools/clang/spanify/tests/structs-with-inline-arrays-expected.cc
@@ -0,0 +1,51 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <array>
+
+// No expected rewrite:
+// We don't handle global C arrays.
+// TODO(364338808) Handle this case.
+struct {
+  int val;
+} globalBuffer[4];
+
+// No expected rewrite:
+// We don't handle global C arrays.
+// TODO(364338808) Handle this case.
+struct GlobalHasName {
+  int val;
+} globalNamedBuffer[4];
+
+// No expected rewrite:
+// We don't handle global C arrays.
+// TODO(364338808) Handle this case.
+GlobalHasName globalNamedBufferButNotInline[4];
+
+void fct() {
+  // No expected rewrite
+  // Unnamed type is hard to add for std::array
+  // TODO(362644557) Handle this case.
+  struct {
+    int val;
+  } funcBuffer[4];
+  // No expected rewrite:
+  // Inline definitions are to hard to add for std::array.
+  // TODO(362644557) Handle this case.
+  struct funcHasName {
+    int val;
+  } funcNamedBuffer[4];
+
+  // Expected rewrite:
+  // std::array<funcHasName, 4> funcNamedBufferButNotInline;
+  std::array<funcHasName, 4> funcNamedBufferButNotInline;
+
+  // Buffer accesses to trigger spanification.
+  funcBuffer[2].val = 3;
+  globalBuffer[2].val = 3;
+  funcNamedBuffer[2].val = 3;
+  globalNamedBuffer[2].val = 3;
+  globalNamedBufferButNotInline[2].val = 3;
+  funcNamedBufferButNotInline[3].val = 3;
+}
diff --git a/tools/clang/spanify/tests/structs-with-inline-arrays-original.cc b/tools/clang/spanify/tests/structs-with-inline-arrays-original.cc
new file mode 100644
index 0000000..4c34a86
--- /dev/null
+++ b/tools/clang/spanify/tests/structs-with-inline-arrays-original.cc
@@ -0,0 +1,49 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// No expected rewrite:
+// We don't handle global C arrays.
+// TODO(364338808) Handle this case.
+struct {
+  int val;
+} globalBuffer[4];
+
+// No expected rewrite:
+// We don't handle global C arrays.
+// TODO(364338808) Handle this case.
+struct GlobalHasName {
+  int val;
+} globalNamedBuffer[4];
+
+// No expected rewrite:
+// We don't handle global C arrays.
+// TODO(364338808) Handle this case.
+GlobalHasName globalNamedBufferButNotInline[4];
+
+void fct() {
+  // No expected rewrite
+  // Unnamed type is hard to add for std::array
+  // TODO(362644557) Handle this case.
+  struct {
+    int val;
+  } funcBuffer[4];
+  // No expected rewrite:
+  // Inline definitions are to hard to add for std::array.
+  // TODO(362644557) Handle this case.
+  struct funcHasName {
+    int val;
+  } funcNamedBuffer[4];
+
+  // Expected rewrite:
+  // std::array<funcHasName, 4> funcNamedBufferButNotInline;
+  funcHasName funcNamedBufferButNotInline[4];
+
+  // Buffer accesses to trigger spanification.
+  funcBuffer[2].val = 3;
+  globalBuffer[2].val = 3;
+  funcNamedBuffer[2].val = 3;
+  globalNamedBuffer[2].val = 3;
+  globalNamedBufferButNotInline[2].val = 3;
+  funcNamedBufferButNotInline[3].val = 3;
+}
diff --git a/tools/codeql/queries/hello_world.ql b/tools/codeql/queries/hello_world.ql
new file mode 100644
index 0000000..ed26508
--- /dev/null
+++ b/tools/codeql/queries/hello_world.ql
@@ -0,0 +1,19 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @name Find all Functions in target.
+ * @description Finds all Functions in the given target.
+                (This is a trivial query intended to be used for test
+                purposes only.)
+ *              but not immediately followed by a return statement.
+ * @kind problem
+ * @problem.severity recommendation
+ * @id cpp/hello-world
+ */
+
+import cpp
+
+from Function f
+select f
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 88d08c92..5dfb25ea 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -19918,6 +19918,24 @@
   </description>
 </action>
 
+<action name="ManualFallback_PlusAddress_OpenCreatePlusAddress">
+  <owner>vidhanj@google.com</owner>
+  <owner>keitel@google.com</owner>
+  <description>
+    The user tapped on &quot;Create plus address&quot; on the Address/Password
+    Manual Fallback view.
+  </description>
+</action>
+
+<action name="ManualFallback_PlusAddress_OpenManagePlusAddress">
+  <owner>vidhanj@google.com</owner>
+  <owner>keitel@google.com</owner>
+  <description>
+    The user tapped on &quot;Manage plus addresses&quot; on the Address/Password
+    Manual Fallback view.
+  </description>
+</action>
+
 <action name="ManualFallback_Profiles_Address1">
   <owner>djean@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 165972c..268b82f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -12330,6 +12330,7 @@
   <int value="-2147024726" label="ERROR_BUSY"/>
   <int value="-2147024713" label="ERROR_ALREADY_EXISTS"/>
   <int value="-2147024703" label="ERROR_BAD_EXE_FORMAT"/>
+  <int value="-2147024637" label="DRM_E_NOMORE_DATA"/>
   <int value="-2147024110" label="ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY"/>
   <int value="-2147023843" label="ERROR_SERVICE_REQUEST_TIMEOUT"/>
   <int value="-2147023838" label="ERROR_SERVICE_DISABLED"/>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index 3cdc717..1e46412 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -37,6 +37,7 @@
   <variant name=".AppInstallUriLauncher"
       summary="URI navigation from Launcher"/>
   <variant name=".AppInstallUriMall" summary="URI navigation from Mall"/>
+  <variant name=".AppInstallUriMallV2" summary="URI navigation from Mall V2"/>
   <variant name=".AppInstallUriPeripherals"
       summary="URI navigation from peripherals"/>
   <variant name=".AppInstallUriShowoff" summary="URI navigation from Showoff"/>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index e72837a..4d2c78f 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -302,6 +302,21 @@
   <variant name=".TabletMode" summary="Animates in Tablet mode w/o split view"/>
 </variants>
 
+<variants name="OverviewStartReason">
+  <variant name="InformedRestore"
+      summary="Not user initiated. Overview is shown automatically after
+               login in a dialogue asking if windows should be restored."/>
+  <variant name="Other"
+      summary="Debugging and other use cases that shouldn't have much traffic
+               in production."/>
+  <variant name="UserInitiatedClamshell"
+      summary="User provided some input that suggests they explicitly want to
+               see overview while in clamshell mode."/>
+  <variant name="UserInitiatedTablet"
+      summary="User provided some input that suggests they explicitly want to
+               see overview while in tablet mode."/>
+</variants>
+
 <variants name="PsmRequest">
   <variant name="Import" summary="Import data request to Fresnel"/>
   <variant name="Oprf" summary="First phase of check membership requests"/>
@@ -1655,7 +1670,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.CalendarKeyboardNavigationSource"
-    enum="CalendarKeyboardNavigationSource" expires_after="2024-09-04">
+    enum="CalendarKeyboardNavigationSource" expires_after="2025-09-02">
   <owner>kradtke@chromium.org</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1667,7 +1682,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.EventListView.JoinMeetingButton.Pressed"
-    enum="CalendarEventSource" expires_after="2025-01-05">
+    enum="CalendarEventSource" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1679,7 +1694,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.EventListViewJelly.EventDisplayedCount"
-    units="int" expires_after="2025-01-05">
+    units="int" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1691,7 +1706,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.EventsDisplayedToUser" enum="Boolean"
-    expires_after="2025-01-05">
+    expires_after="2025-09-02">
   <owner>samcackett@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1701,7 +1716,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.FetchCalendars.TotalSelectedCalendars"
-    units="int" expires_after="2024-09-04">
+    units="int" expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1711,7 +1726,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.FetchEvents.PreFetched" units="fetches"
-    expires_after="2024-09-04">
+    expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1721,7 +1736,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.FetchEvents.SingleMonthSize" units="bytes"
-    expires_after="2024-11-03">
+    expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1731,7 +1746,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.FetchEvents.TotalCacheSizeMonths" units="months"
-    expires_after="2024-11-03">
+    expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1741,7 +1756,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.FetchEvents.TotalFetchDuration" units="ms"
-    expires_after="2024-09-04">
+    expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1752,7 +1767,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.Fetch{DataType}s.FetchDuration" units="ms"
-    expires_after="2025-01-05">
+    expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1763,7 +1778,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.Fetch{DataType}s.Result"
-    enum="CalendarDataFetchApiError" expires_after="2025-01-05">
+    enum="CalendarDataFetchApiError" expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1774,7 +1789,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.Fetch{DataType}s.Timeout" enum="Boolean"
-    expires_after="2024-09-04">
+    expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1785,7 +1800,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.MaxDistanceBrowsed" units="months"
-    expires_after="2025-01-05">
+    expires_after="2025-09-02">
   <owner>newcomer@chromium.org</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1795,7 +1810,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.MonthDwellTime" units="ms"
-    expires_after="2025-01-05">
+    expires_after="2025-09-02">
   <owner>kradtke@chromium.org</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1805,7 +1820,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.ScrollSource" enum="CalendarViewScrollSource"
-    expires_after="2025-01-05">
+    expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1816,7 +1831,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.ShowSource.{ViewShowSource}"
-    enum="CalendarEventSource" expires_after="2025-01-05">
+    enum="CalendarEventSource" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1830,7 +1845,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.TimeToSeeTodaysEventDots" units="ms"
-    expires_after="2025-01-05">
+    expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1842,7 +1857,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.TimeToSeeTodaysEventDotsForMultiCalendar"
-    units="ms" expires_after="2025-02-16">
+    units="ms" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1854,7 +1869,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.UpNextView.EventDisplayedCount" units="int"
-    expires_after="2025-01-05">
+    expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1866,7 +1881,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.UpNextView.EventListItem.Pressed"
-    enum="CalendarEventSource" expires_after="2025-01-05">
+    enum="CalendarEventSource" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1878,7 +1893,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.UpNextView.JoinMeetingButton.Pressed"
-    enum="CalendarEventSource" expires_after="2025-01-05">
+    enum="CalendarEventSource" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1890,7 +1905,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.UserAction" enum="CalendarUserAction"
-    expires_after="2025-02-02">
+    expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>tbarzic@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
@@ -1901,7 +1916,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.UserActionToOpenCalendarWebUi"
-    enum="CalendarUserAction" expires_after="2025-02-02">
+    enum="CalendarUserAction" expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>tbarzic@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
@@ -1913,7 +1928,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.UserJourneyTime.{EventLaunchState}" units="ms"
-    expires_after="2024-11-03">
+    expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1928,7 +1943,7 @@
 </histogram>
 
 <histogram name="Ash.Calendar.{CalendarChildView}.Activated"
-    enum="CalendarEventSource" expires_after="2025-01-05">
+    enum="CalendarEventSource" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>Recorded when the {CalendarChildView} is activated.</summary>
@@ -1942,7 +1957,7 @@
 
 <histogram
     name="Ash.CalendarView.CloseEventList.{CalendarChildView}.AnimationSmoothness"
-    units="%" expires_after="2024-09-04">
+    units="%" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
@@ -1956,7 +1971,7 @@
 </histogram>
 
 <histogram name="Ash.CalendarView.ConstructionTime" units="ms"
-    expires_after="2024-09-04">
+    expires_after="2025-09-02">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -1967,7 +1982,7 @@
 </histogram>
 
 <histogram name="Ash.CalendarView.FadeInUpNextView.AnimationSmoothness"
-    units="%" expires_after="2025-01-12">
+    units="%" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>samcackett@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
@@ -1978,7 +1993,7 @@
 </histogram>
 
 <histogram name="Ash.CalendarView.FadeOutUpNextView.AnimationSmoothness"
-    units="%" expires_after="2024-09-04">
+    units="%" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>sylvieliu@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
@@ -1989,7 +2004,7 @@
 </histogram>
 
 <histogram name="Ash.CalendarView.OnMonthChanged.AnimationSmoothness" units="%"
-    expires_after="2024-09-04">
+    expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
@@ -1998,7 +2013,7 @@
 
 <histogram
     name="Ash.CalendarView.OpenEventList.{CalendarChildView}.AnimationSmoothness"
-    units="%" expires_after="2024-11-03">
+    units="%" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
@@ -2022,7 +2037,7 @@
 
 <histogram
     name="Ash.CalendarView.ScrollOneMonth.{CalendarChildView}.AnimationSmoothness"
-    units="%" expires_after="2024-11-03">
+    units="%" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
@@ -2039,7 +2054,7 @@
 
 <histogram
     name="Ash.CalendarView.{Animation}.{CalendarChildView}.AnimationSmoothness"
-    units="%" expires_after="2024-11-03">
+    units="%" expires_after="2025-09-02">
   <owner>newcomer@google.com</owner>
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
@@ -6555,6 +6570,22 @@
   <token key="NumWindows" variants="NumWindows"/>
 </histogram>
 
+<histogram name="Ash.Overview.Enter.PresentationTime.{OverviewStartReason}"
+    units="ms" expires_after="2025-07-15">
+  <owner>esum@google.com</owner>
+  <owner>cros-sw-perf@google.com</owner>
+  <summary>
+    Records the presentation time, which is the time in milliseconds it takes
+    from when the overview enter event was received and successfully processed
+    to when the next frame is shown to the user.
+
+    This is the exact same measurement as Ash.Overview.Enter.PresentationTime,
+    but segmented by different reasons overview mode was started, which have
+    different profile and performance characteristics.
+  </summary>
+  <token key="OverviewStartReason" variants="OverviewStartReason"/>
+</histogram>
+
 <histogram name="Ash.Overview.Exit.PresentationTime" units="ms"
     expires_after="2025-07-15">
   <owner>sammiequon@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/attribution_reporting/histograms.xml b/tools/metrics/histograms/metadata/attribution_reporting/histograms.xml
index fe49bc22f..017fffa 100644
--- a/tools/metrics/histograms/metadata/attribution_reporting/histograms.xml
+++ b/tools/metrics/histograms/metadata/attribution_reporting/histograms.xml
@@ -815,6 +815,18 @@
   </summary>
 </histogram>
 
+<histogram name="Conversions.NonAttributionSrcRequestUnsetSupport.Detached"
+    enum="Boolean" expires_after="2025-03-02">
+  <owner>linnan@chromium.org</owner>
+  <owner>measurement-api-dev+metrics@google.com</owner>
+  <summary>
+    Records whether a non-attributionsrc request that doesn't have attribution
+    support set received response in a detached frame. This is only recorded
+    when attribution response headers were registered from a suitable reporting
+    origin.
+  </summary>
+</histogram>
+
 <histogram name="Conversions.NonDefaultAggregatableFilteringId" enum="Boolean"
     expires_after="2025-03-02">
   <owner>linnan@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/enums.xml b/tools/metrics/histograms/metadata/autofill/enums.xml
index 727bc07f..62bce04 100644
--- a/tools/metrics/histograms/metadata/autofill/enums.xml
+++ b/tools/metrics/histograms/metadata/autofill/enums.xml
@@ -730,6 +730,9 @@
   <int value="10241"
       label="SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES: Autofilled value
              accepted"/>
+  <int value="10368"
+      label="IMPROVED_PREDICTION: Not autofilled or autofilled value edited"/>
+  <int value="10369" label="IMPROVED_PREDICTION: Autofilled value accepted"/>
 </enum>
 
 <enum name="AutofillDeveloperEngagement">
@@ -950,6 +953,8 @@
   <int value="2513" label="ADDRESS_HOME_APT_TYPE: accepted"/>
   <int value="2560" label="SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES: edited"/>
   <int value="2561" label="SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES: accepted"/>
+  <int value="2592" label="IMPROVED_PREDICTION: edited"/>
+  <int value="2593" label="IMPROVED_PREDICTION: accepted"/>
 </enum>
 
 <enum name="AutofillErrorDialogType">
@@ -1763,6 +1768,7 @@
   <int value="157" label="ADDRESS_HOME_APT_TYPE"/>
   <int value="160" label="SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES"/>
   <int value="161" label="SERVER_RESPONSE_PENDING"/>
+  <int value="162" label="IMPROVED_PREDICTION"/>
 </enum>
 
 <enum name="AutofillFillingMethodMetric">
@@ -3701,6 +3707,22 @@
   <int value="966"
       label="SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES - Predictions different
              - Value agrees with both predictions"/>
+  <int value="973"
+      label="IMPROVED_PREDICTION - Predictions equal - Value agrees"/>
+  <int value="974"
+      label="IMPROVED_PREDICTION - Predictions equal - Value disagrees"/>
+  <int value="975"
+      label="IMPROVED_PREDICTION - Predictions different - Value agrees with
+             old prediction"/>
+  <int value="976"
+      label="IMPROVED_PREDICTION - Predictions different - Value agrees with
+             new prediction"/>
+  <int value="977"
+      label="IMPROVED_PREDICTION - Predictions different - Value agrees with
+             neither prediction"/>
+  <int value="978"
+      label="IMPROVED_PREDICTION - Predictions different - Value agrees with
+             both predictions"/>
 </enum>
 
 <enum name="AutofillPreferenceSetter">
@@ -3975,6 +3997,8 @@
              load"/>
   <int value="2561"
       label="SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES: Empty on page load"/>
+  <int value="2592" label="IMPROVED_PREDICTION: Pre-filled on page load"/>
+  <int value="2593" label="IMPROVED_PREDICTION: Empty on page load"/>
 </enum>
 
 <enum name="AutofillPreFilledValueStatus">
diff --git a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
index eba8762..0e12bb0 100644
--- a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
@@ -944,7 +944,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Settings.UserActionOnSearchResultsShown"
-    enum="OsSettingSearchBoxUserAction" expires_after="2024-10-16">
+    enum="OsSettingSearchBoxUserAction" expires_after="2025-09-03">
   <owner>wesokuhara@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <owner>cros-settings@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index 56bd3b3..f37d5b1 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -809,7 +809,7 @@
 </histogram>
 
 <histogram name="Compositing.SurfaceAggregator.AggregateUs"
-    units="microseconds" expires_after="2025-01-05">
+    units="microseconds" expires_after="2025-06-01">
   <owner>kylechar@chromium.org</owner>
   <owner>graphics-dev@chromium.org</owner>
   <summary>
@@ -825,7 +825,7 @@
 </histogram>
 
 <histogram name="Compositing.SurfaceAggregator.CopiedSurfaceCount"
-    units="surfaces" expires_after="2025-02-10">
+    units="surfaces" expires_after="2025-06-01">
   <owner>kylechar@chromium.org</owner>
   <owner>jonross@chromium.org</owner>
   <summary>
@@ -835,7 +835,7 @@
 </histogram>
 
 <histogram name="Compositing.SurfaceAggregator.CopyUs" units="microseconds"
-    expires_after="2025-01-05">
+    expires_after="2025-06-01">
   <owner>kylechar@chromium.org</owner>
   <owner>jonross@chromium.org</owner>
   <summary>
@@ -875,7 +875,7 @@
 </histogram>
 
 <histogram name="Compositing.SurfaceAggregator.PrewalkedSurfaceCount"
-    units="surfaces" expires_after="2024-11-03">
+    units="surfaces" expires_after="2025-06-01">
   <owner>kylechar@chromium.org</owner>
   <owner>jonross@chromium.org</owner>
   <summary>
@@ -885,7 +885,7 @@
 </histogram>
 
 <histogram name="Compositing.SurfaceAggregator.PrewalkUs" units="microseconds"
-    expires_after="2024-09-15">
+    expires_after="2025-06-01">
   <owner>kylechar@chromium.org</owner>
   <owner>jonross@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cros/histograms.xml b/tools/metrics/histograms/metadata/cros/histograms.xml
index 06ede05..e370c43 100644
--- a/tools/metrics/histograms/metadata/cros/histograms.xml
+++ b/tools/metrics/histograms/metadata/cros/histograms.xml
@@ -53,6 +53,28 @@
   </summary>
 </histogram>
 
+<histogram name="CrosDisks.Error.Fuse.PrematureTermination.{FileSystem}"
+    enum="DaemonError" expires_after="2025-03-01">
+  <owner>fdegros@chromium.org</owner>
+  <owner>src/ui/file_manager/OWNERS</owner>
+  <summary>
+    Error returned by the FUSE daemon serving the {FileSystem} filesystem when
+    it unexpectedly terminated (i.e. before being requested by cros-disks to
+    unmount and exit).
+  </summary>
+  <token key="FileSystem">
+    <variant name="archive"/>
+    <variant name="drivefs"/>
+    <variant name="fuseblk.exfat"/>
+    <variant name="fuseblk.ntfs"/>
+    <variant name="fusebox"/>
+    <variant name="rar"/>
+    <variant name="smbfs"/>
+    <variant name="sshfs"/>
+    <variant name="zip"/>
+  </token>
+</histogram>
+
 <histogram name="CrosDisks.Error.{Action}.{FileSystem}" enum="DaemonError"
     expires_after="2025-03-01">
   <owner>fdegros@chromium.org</owner>
@@ -148,27 +170,6 @@
   </summary>
 </histogram>
 
-<histogram name="CrosDisks.PrematureTermination.{Daemon}" enum="DaemonError"
-    expires_after="2025-03-01">
-  <owner>fdegros@chromium.org</owner>
-  <owner>src/ui/file_manager/OWNERS</owner>
-  <summary>
-    Error returned by a FUSE daemon that unexpectedly terminated (i.e. before
-    being requested by cros-disks to unmount and exit).
-  </summary>
-  <token key="Daemon">
-    <variant name="drivefs"/>
-    <variant name="fuse-archive"/>
-    <variant name="fusebox"/>
-    <variant name="mount-exfat-fuse"/>
-    <variant name="mount-zip"/>
-    <variant name="ntfs-3g"/>
-    <variant name="rar2fs"/>
-    <variant name="smbfs"/>
-    <variant name="sshfs"/>
-  </token>
-</histogram>
-
 <histogram name="CrosDisks.ReadOnlyFileSystemAfterError"
     enum="CrosDisksFilesystemType" expires_after="2025-03-01">
   <owner>fdegros@chromium.org</owner>
@@ -180,6 +181,28 @@
   </summary>
 </histogram>
 
+<histogram name="CrosDisks.Time.Fuse.PrematureTermination.{FileSystem}"
+    units="ms" expires_after="2025-03-01">
+  <owner>fdegros@chromium.org</owner>
+  <owner>src/ui/file_manager/OWNERS</owner>
+  <summary>
+    Time after which the FUSE daemon serving the {FileSystem} filesystem
+    unexpectedly terminated (i.e. before being requested by cros-disks to
+    unmount and exit).
+  </summary>
+  <token key="FileSystem">
+    <variant name="archive"/>
+    <variant name="drivefs"/>
+    <variant name="fuseblk.exfat"/>
+    <variant name="fuseblk.ntfs"/>
+    <variant name="fusebox"/>
+    <variant name="rar"/>
+    <variant name="smbfs"/>
+    <variant name="sshfs"/>
+    <variant name="zip"/>
+  </token>
+</histogram>
+
 <histogram name="CrosDisks.Time.{Action}.{FileSystem}" units="ms"
     expires_after="2025-03-01">
   <owner>fdegros@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/facilitated_payments/histograms.xml b/tools/metrics/histograms/metadata/facilitated_payments/histograms.xml
index 0d4806e..6a29dd1 100644
--- a/tools/metrics/histograms/metadata/facilitated_payments/histograms.xml
+++ b/tools/metrics/histograms/metadata/facilitated_payments/histograms.xml
@@ -31,6 +31,31 @@
       summary="Facilitate a Pix based payment using Google Pay."/>
 </variants>
 
+<histogram
+    name="FacilitatedPayments.Pix.PaymentCodeValidation.{PaymentCodeValidationResult}.Latency"
+    units="ms" expires_after="2025-07-01">
+  <owner>siashah@google.com</owner>
+  <owner>vishwasuppoor@google.com</owner>
+  <owner>rouslan@google.com</owner>
+  <owner>payments-autofill-team@google.com</owner>
+  <summary>
+    Latency for the Pix payment code validation with
+    {PaymentCodeValidationResult} as result. The validation is done in a
+    sandboxed utility process to safely process strings from the renderer
+    without compromising the browser process. The string would have already been
+    verified to contain the Pix identifier prefix (0014br.gov.bcb.pix).
+    [Frequency] As the Pix payflow is only allowed to be triggered once per page
+    load, this metric similarly can only be logged at most once per page load.
+    [Trigger] The user copies a Pix payment code to their clipboard, either
+    manually for via a merchant's copy button.
+  </summary>
+  <token key="PaymentCodeValidationResult">
+    <variant name="InvalidCode" summary="invalid code"/>
+    <variant name="ValidatorFailed" summary="failed validator"/>
+    <variant name="ValidCode" summary="valid code"/>
+  </token>
+</histogram>
+
 <histogram name="FacilitatedPayments.SettingsPage.Shown" enum="BooleanShown"
     expires_after="2025-07-01">
   <owner>siashah@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ios/enums.xml b/tools/metrics/histograms/metadata/ios/enums.xml
index 8e699b43..1e64ee7 100644
--- a/tools/metrics/histograms/metadata/ios/enums.xml
+++ b/tools/metrics/histograms/metadata/ios/enums.xml
@@ -1057,14 +1057,19 @@
   <int value="1" label="Open Instructions"/>
 </enum>
 
+<!-- LINT.IfChange(PushNotificationClientId) -->
+
 <enum name="PushNotificationClientId">
   <int value="1" label="Commerce"/>
   <int value="2" label="Content"/>
   <int value="3" label="Tips"/>
   <int value="4" label="Sports"/>
   <int value="5" label="Safety Check"/>
+  <int value="6" label="Send Tab"/>
 </enum>
 
+<!-- LINT.ThenChange(/ios/chrome/browser/push_notification/model/push_notification_client_id.h:PushNotificationClientId) -->
+
 <enum name="PushNotificationLifecycleEvent">
   <int value="0" label="Notification received on device"/>
   <int value="1"
diff --git a/tools/metrics/histograms/metadata/language/histograms.xml b/tools/metrics/histograms/metadata/language/histograms.xml
index 05a5bb4..68ca321c 100644
--- a/tools/metrics/histograms/metadata/language/histograms.xml
+++ b/tools/metrics/histograms/metadata/language/histograms.xml
@@ -31,7 +31,7 @@
 
     This histogram is recorded once for each invocation of the model. The model
     may be invoked multiple times after pageload, running against samples of
-    text from the page..
+    text from the page.
   </summary>
 </histogram>
 
@@ -40,11 +40,11 @@
   <owner>fergal@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
-    The time takes to prepare the input and run the TFLite detection model.
+    The time taken to prepare the input and run the TFLite detection model.
 
     This histogram is recorded once for each invocation of the model. The model
     may be invoked multiple times after pageload, running against samples of
-    text from the page..
+    text from the page.
   </summary>
 </histogram>
 
@@ -59,13 +59,13 @@
 
     This histogram is recorded once for each invocation of the model. The model
     may be invoked multiple times after pageload, running against samples of
-    text from the page.. A successful invocation will record Detected (above) as
+    text from the page. A successful invocation will record Detected (above) as
     true.
   </summary>
 </histogram>
 
-<histogram name="LanguageDetection.TFLiteModel.ClassifyText.Size" units="bytes"
-    expires_after="2025-01-20">
+<histogram name="LanguageDetection.TFLiteModel.ClassifyText.Size"
+    units="characters" expires_after="2025-01-20">
   <owner>fergal@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -73,7 +73,7 @@
 
     This histogram is recorded once for each invocation of the model. The model
     may be invoked multiple times after pageload, running against samples of
-    text from the page..
+    text from the page.
   </summary>
 </histogram>
 
@@ -96,7 +96,7 @@
   <owner>fergal@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
-    The time takes to load the file and create the TFLite detection model. Note
+    The time taken to load the file and create the TFLite detection model. Note
     that on Windows, this operation is a file read, on other platforms it is an
     MMAP.
 
diff --git a/tools/metrics/histograms/metadata/magic_stack/enums.xml b/tools/metrics/histograms/metadata/magic_stack/enums.xml
index 73a5607..8653e83 100644
--- a/tools/metrics/histograms/metadata/magic_stack/enums.xml
+++ b/tools/metrics/histograms/metadata/magic_stack/enums.xml
@@ -42,6 +42,11 @@
   <int value="5"
       label="Clicked on foreign second tile of config local+foreign (double
              tiles)"/>
+  <int value="6"
+      label="Clicked on history tile of config history (single tile)"/>
+  <int value="7" label="Clicked on a foreign tile of config double tiles"/>
+  <int value="8" label="Clicked on a local tile of config double tiles"/>
+  <int value="9" label="Clicked on a history tile of config double tiles"/>
 </enum>
 
 <enum name="MagicStack.Clank.TabResumption.ModuleNotShownReason">
@@ -56,6 +61,13 @@
   <int value="1" label="Showing config foreign+foreign (double tiles)"/>
   <int value="2" label="Showing config local (single tile)"/>
   <int value="3" label="Showing config local+foreign (double tiles)"/>
+  <int value="4" label="Showing config foreign+history (double tiles)"/>
+  <int value="5" label="Showing config local+local (double tiles)"/>
+  <int value="6" label="Showing config local+history (double tiles)"/>
+  <int value="7" label="Showing config history (single tile)"/>
+  <int value="8" label="Showing config history+history (double tiles)"/>
+  <int value="9" label="Showing config a single tile without type finalized"/>
+  <int value="10" label="Showing config double tiles without type finalized"/>
 </enum>
 
 <enum name="ModulePosition">
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index c979c5f..96f263c6 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -1675,7 +1675,7 @@
 </histogram>
 
 <histogram name="Network.DnsProxy.PlainTextProbe.{Family}.Errors"
-    enum="DnsProxy.QueryResult" expires_after="2025-03-02">
+    enum="DnsProxy.QueryError" expires_after="2025-03-02">
   <owner>jasongustaman@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 88ab5799..431666c 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -242,7 +242,7 @@
 </variants>
 
 <histogram name="AccessCodeCast.Discovery.AddSinkResult.New"
-    enum="AccessCodeCastAddSinkResult" expires_after="2024-09-04">
+    enum="AccessCodeCastAddSinkResult" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -253,7 +253,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Discovery.AddSinkResult.Remembered"
-    enum="AccessCodeCastAddSinkResult" expires_after="2024-09-04">
+    enum="AccessCodeCastAddSinkResult" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -265,7 +265,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Discovery.CastModeOnSuccess"
-    enum="AccessCodeCastCastMode" expires_after="2024-09-04">
+    enum="AccessCodeCastCastMode" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -276,7 +276,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Discovery.DeviceDurationOnRoute"
-    units="seconds" expires_after="2024-09-04">
+    units="seconds" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -287,7 +287,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Discovery.RememberedDevicesCount"
-    units="cast devices" expires_after="2024-09-04">
+    units="cast devices" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -298,7 +298,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Session.FreezeCount" units="instances"
-    expires_after="2024-11-03">
+    expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -309,7 +309,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Session.FreezeDuration" units="ms"
-    expires_after="2024-09-04">
+    expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -320,7 +320,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Session.NewDeviceRouteCreationDuration"
-    units="ms" expires_after="2024-09-04">
+    units="ms" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -333,7 +333,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Session.RouteDiscoveryTypeAndSource"
-    enum="AccessCodeCastDiscoveryTypeAndSource" expires_after="2024-09-04">
+    enum="AccessCodeCastDiscoveryTypeAndSource" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -344,7 +344,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Session.RouteDuration" units="ms"
-    expires_after="2024-09-04">
+    expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -355,7 +355,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Session.SavedDeviceRouteCreationDuration"
-    units="ms" expires_after="2024-09-04">
+    units="ms" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -367,7 +367,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Ui.AccessCodeInputTime" units="ms"
-    expires_after="2024-09-04">
+    expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -379,7 +379,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Ui.AccessCodeNotFoundCount" units="instances"
-    expires_after="2024-09-04">
+    expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -391,7 +391,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Ui.CastAttemptLength" units="ms"
-    expires_after="2024-09-04">
+    expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -403,7 +403,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Ui.DialogCloseReason"
-    enum="AccessCodeCastDialogCloseReason" expires_after="2024-09-04">
+    enum="AccessCodeCastDialogCloseReason" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -413,7 +413,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Ui.DialogLoadTime" units="ms"
-    expires_after="2024-09-04">
+    expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -424,7 +424,7 @@
 </histogram>
 
 <histogram name="AccessCodeCast.Ui.DialogOpenLocation"
-    enum="AccessCodeCastDialogOpenLocation" expires_after="2024-09-04">
+    enum="AccessCodeCastDialogOpenLocation" expires_after="2025-03-04">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index 895342e..78628ab 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -187,6 +187,12 @@
 
 <!-- LINT.ThenChange(//tools/metrics/histograms/metadata/blink/histograms.xml:BlinkRequestDestination) -->
 
+<variants name="ResponseFromCacheSuffix">
+  <variant name="" summary="response came from the server or http cache"/>
+  <variant name=".ResponseFromCache"
+      summary="response came from the http cache (M129)"/>
+</variants>
+
 <variants name="RTTBucketSuffix">
   <variant name="" summary="any"/>
   <variant name=".RTT200To450" summary="RTT between 200 to 450ms"/>
@@ -2196,7 +2202,7 @@
 </histogram>
 
 <histogram
-    name="PageLoad.Clients{ObservedNavigation}.Leakage2.AbandonReasonAt.{NavigationMilestone}{BackgroundSuffix}{HiddenSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
+    name="PageLoad.Clients{ObservedNavigation}.Leakage2.AbandonReasonAt.{NavigationMilestone}{BackgroundSuffix}{HiddenSuffix}{ResponseFromCacheSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
     enum="NavigationAbandonReasonEnum" expires_after="2025-02-10">
   <owner>rakina@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
@@ -2204,12 +2210,14 @@
     Logs the navigation abandonment reason that happens when the last milestone
     is {NavigationMilestone} on a main frame navigation for {ObservedNavigation}
     with {RTTBucketSuffix} connection, with potentially some suffixes detaling
-    the navigation {BackgroundSuffix}, {HiddenSuffix}, and/or {NonSRPSuffix}.
+    the navigation {BackgroundSuffix}, {HiddenSuffix}, {ResponseFromCacheSuffix}
+    and/or {NonSRPSuffix}.
 
     The metric is emitted when the navigation gets abandoned.
   </summary>
   <token key="BackgroundSuffix" variants="BackgroundSuffix"/>
   <token key="HiddenSuffix" variants="HiddenSuffix"/>
+  <token key="ResponseFromCacheSuffix" variants="ResponseFromCacheSuffix"/>
   <token key="NavigationMilestone" variants="NavigationMilestone"/>
   <token key="NonSRPSuffix" variants="NonSRPSuffix"/>
   <token key="ObservedNavigation" variants="ObservedNavigation"/>
@@ -2217,7 +2225,7 @@
 </histogram>
 
 <histogram
-    name="PageLoad.Clients{ObservedNavigation}.Leakage2.LastMilestoneBeforeAbandon{NavigationAbandonReason}{BackgroundSuffix}{HiddenSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
+    name="PageLoad.Clients{ObservedNavigation}.Leakage2.LastMilestoneBeforeAbandon{NavigationAbandonReason}{BackgroundSuffix}{HiddenSuffix}{ResponseFromCacheSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
     enum="NavigationMilestoneEnum2" expires_after="2025-02-10">
   <owner>rakina@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
@@ -2225,11 +2233,13 @@
     Logs the last navigation milestone reason that was reached when a main frame
     navigation for {ObservedNavigation} is abandoned due to
     {NavigationAbandonReason}, with potentially some suffixes detaling the
-    navigation {BackgroundSuffix}, {HiddenSuffix}, and/or {NonSRPSuffix}. The
-    metric is emitted when the navigation gets abandoned.
+    navigation {BackgroundSuffix}, {HiddenSuffix}, {ResponseFromCacheSuffix}
+    and/or {NonSRPSuffix}. The metric is emitted when the navigation gets
+    abandoned.
   </summary>
   <token key="BackgroundSuffix" variants="BackgroundSuffix"/>
   <token key="HiddenSuffix" variants="HiddenSuffix"/>
+  <token key="ResponseFromCacheSuffix" variants="ResponseFromCacheSuffix"/>
   <token key="NavigationAbandonReason" variants="NavigationAbandonReason"/>
   <token key="NonSRPSuffix" variants="NonSRPSuffix"/>
   <token key="ObservedNavigation" variants="ObservedNavigation"/>
@@ -2237,7 +2247,7 @@
 </histogram>
 
 <histogram
-    name="PageLoad.Clients{ObservedNavigation}.Leakage2.NavigationStartToRendererProcessInit{BackgroundSuffix}{HiddenSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
+    name="PageLoad.Clients{ObservedNavigation}.Leakage2.NavigationStartToRendererProcessInit{BackgroundSuffix}{HiddenSuffix}{ResponseFromCacheSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
     units="ms" expires_after="2024-12-08">
   <owner>rakina@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
@@ -2245,19 +2255,21 @@
     The time relative to the navigation start that the final renderer process is
     created for a main frame navigation for {ObservedNavigation} with
     potentially some suffixes detaling the navigation {BackgroundSuffix},
-    {HiddenSuffix}, {NonSRPSuffix}, and {RTTBucketSuffix}. The metric is emitted
-    when the navigation's commit IPC is just sent out, if the renderer process
-    is created after the navigation started.
+    {HiddenSuffix}, {ResponseFromCacheSuffix}, {NonSRPSuffix}, and
+    {RTTBucketSuffix}. The metric is emitted when the navigation's commit IPC is
+    just sent out, if the renderer process is created after the navigation
+    started.
   </summary>
   <token key="BackgroundSuffix" variants="BackgroundSuffix"/>
   <token key="HiddenSuffix" variants="HiddenSuffix"/>
+  <token key="ResponseFromCacheSuffix" variants="ResponseFromCacheSuffix"/>
   <token key="NonSRPSuffix" variants="NonSRPSuffix"/>
   <token key="ObservedNavigation" variants="ObservedNavigation"/>
   <token key="RTTBucketSuffix" variants="RTTBucketSuffix"/>
 </histogram>
 
 <histogram
-    name="PageLoad.Clients{ObservedNavigation}.Leakage2.NavigationStartTo{NavigationMilestone}{BackgroundSuffix}{HiddenSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
+    name="PageLoad.Clients{ObservedNavigation}.Leakage2.NavigationStartTo{NavigationMilestone}{BackgroundSuffix}{HiddenSuffix}{ResponseFromCacheSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
     units="ms" expires_after="2025-02-10">
   <owner>rakina@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
@@ -2265,17 +2277,18 @@
     The time relative to navigation start that a navigation reaches the the
     milestone {NavigationMilestone} on a main frame navigation for
     {ObservedNavigation}, with potentially some suffixes detaling the navigation
-    {BackgroundSuffix}, {HiddenSuffix}, and/or {NonSRPSuffix}. Only recorded for
-    Search navigations that didn't get terminally abandoned at least until the
-    relevant milestone. Note that abandonment from backgrounding or hiding are
-    non-terminal, so milestones after those types of abandonments will still be
-    logged. The metric is emitted when the navigation finishes committing or it
-    gets abandoned (which can happen multiple times during the navigation if
-    non-terminal, so only the milestones that haven't been logged will be
-    logged).
+    {BackgroundSuffix}, {HiddenSuffix},{ResponseFromCacheSuffix}, and/or
+    {NonSRPSuffix}. Only recorded for Search navigations that didn't get
+    terminally abandoned at least until the relevant milestone. Note that
+    abandonment from backgrounding or hiding are non-terminal, so milestones
+    after those types of abandonments will still be logged. The metric is
+    emitted when the navigation finishes committing or it gets abandoned (which
+    can happen multiple times during the navigation if non-terminal, so only the
+    milestones that haven't been logged will be logged).
   </summary>
   <token key="BackgroundSuffix" variants="BackgroundSuffix"/>
   <token key="HiddenSuffix" variants="HiddenSuffix"/>
+  <token key="ResponseFromCacheSuffix" variants="ResponseFromCacheSuffix"/>
   <token key="NavigationMilestone" variants="NavigationMilestone"/>
   <token key="NonSRPSuffix" variants="NonSRPSuffix"/>
   <token key="ObservedNavigation" variants="ObservedNavigation"/>
@@ -2301,7 +2314,7 @@
 </histogram>
 
 <histogram
-    name="PageLoad.Clients{ObservedNavigation}.Leakage2.RendererProcessCreatedBeforeNav{BackgroundSuffix}{HiddenSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
+    name="PageLoad.Clients{ObservedNavigation}.Leakage2.RendererProcessCreatedBeforeNav{BackgroundSuffix}{HiddenSuffix}{ResponseFromCacheSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
     enum="Boolean" expires_after="2024-12-08">
   <owner>rakina@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
@@ -2309,37 +2322,41 @@
     Whether the renderer process for the navigation is already created before
     the navigation start or not, for a main frame navigation for
     {ObservedNavigation} with potentially some suffixes detaling the navigation
-    {BackgroundSuffix}, {HiddenSuffix}, {NonSRPSuffix}, and {RTTBucketSuffix}.
-    The metric is emitted when the navigation's commit IPC is just sent out.
+    {BackgroundSuffix}, {HiddenSuffix}, {ResponseFromCacheSuffix},
+    {NonSRPSuffix}, and {RTTBucketSuffix}. The metric is emitted when the
+    navigation's commit IPC is just sent out.
   </summary>
   <token key="BackgroundSuffix" variants="BackgroundSuffix"/>
   <token key="HiddenSuffix" variants="HiddenSuffix"/>
+  <token key="ResponseFromCacheSuffix" variants="ResponseFromCacheSuffix"/>
   <token key="NonSRPSuffix" variants="NonSRPSuffix"/>
   <token key="ObservedNavigation" variants="ObservedNavigation"/>
   <token key="RTTBucketSuffix" variants="RTTBucketSuffix"/>
 </histogram>
 
 <histogram
-    name="PageLoad.Clients{ObservedNavigation}.Leakage2.TimeToAbandonFromNavigationStart.{NavigationMilestone}{BackgroundSuffix}{HiddenSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
+    name="PageLoad.Clients{ObservedNavigation}.Leakage2.TimeToAbandonFromNavigationStart.{NavigationMilestone}{BackgroundSuffix}{HiddenSuffix}{ResponseFromCacheSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
     units="ms" expires_after="2024-12-08">
   <owner>sisidovski@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
     The Duration from the navigation start time to the time that a navigation
     gets abandoned after the milestone {NavigationMilestone} on a main frame
-    navigation for {ObservedNavigation} with {RTTBucketSuffix} connection. Only
-    the abandonments due to backgrounding and hiding are non-terminal, and other
-    abandonments are terminal. This means this metric might be recorded multiple
-    times if the navigation is only abandoned non-terminally before, but only
-    once for each abandonment reason. &quot;This is logged after non-terminal
-    abandonment of pages which were {BackgroundSuffix} and {HiddenSuffix}&quot;
-    {NonSRPSuffix} is added if the navigation involves a non-SRP URL but then
-    redirected to SRP URL. The milestone recorded will be the latest milestone
-    (if there are multiple milestones reached before the abandonment). The
-    metric is emitted when the navigation gets abandoned.
+    navigation for {ObservedNavigation} with {RTTBucketSuffix} connection and
+    {ResponseFromCacheSuffix}. Only the abandonments due to backgrounding and
+    hiding are non-terminal, and other abandonments are terminal. This means
+    this metric might be recorded multiple times if the navigation is only
+    abandoned non-terminally before, but only once for each abandonment reason.
+    &quot;This is logged after non-terminal abandonment of pages which were
+    {BackgroundSuffix} and {HiddenSuffix}&quot; {NonSRPSuffix} is added if the
+    navigation involves a non-SRP URL but then redirected to SRP URL. The
+    milestone recorded will be the latest milestone (if there are multiple
+    milestones reached before the abandonment). The metric is emitted when the
+    navigation gets abandoned.
   </summary>
   <token key="BackgroundSuffix" variants="BackgroundSuffix"/>
   <token key="HiddenSuffix" variants="HiddenSuffix"/>
+  <token key="ResponseFromCacheSuffix" variants="ResponseFromCacheSuffix"/>
   <token key="NavigationMilestone" variants="NavigationMilestone"/>
   <token key="NonSRPSuffix" variants="NonSRPSuffix"/>
   <token key="ObservedNavigation" variants="ObservedNavigation"/>
@@ -2347,26 +2364,28 @@
 </histogram>
 
 <histogram
-    name="PageLoad.Clients{ObservedNavigation}.Leakage2.{NavigationMilestone}ToAbandon{NavigationAbandonReason}{BackgroundSuffix}{HiddenSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
+    name="PageLoad.Clients{ObservedNavigation}.Leakage2.{NavigationMilestone}ToAbandon{NavigationAbandonReason}{BackgroundSuffix}{HiddenSuffix}{ResponseFromCacheSuffix}{NonSRPSuffix}{RTTBucketSuffix}"
     units="ms" expires_after="2025-02-10">
   <owner>rakina@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
     The time relative to the milestone {NavigationMilestone} that a navigation
     gets abandoned due to {NavigationAbandonReason}, on a main frame navigation
-    for {ObservedNavigation}. Abandonment from backgrounding or hiding are
-    non-terminal, while other abandonment for other reasons are terminal. This
-    means this metric might be recorded multiple times if the navigation is only
-    abandoned non-terminally before, but only once for each abandonment reason.
-    Metrics logged after non-terminal abandonment will be marked with the
-    suffixes {BackgroundSuffix} and/or {HiddenSuffix}, and if the navigation
-    involves a non-SRP URL but then redirected to SRP URL, the logged metric
-    will be marked with {NonSRPSuffix}. The milestone recorded will be the
-    latest milestone (if there are multiple milestones reached before the
-    abandonment). The metric is emitted when the navigation gets abandoned.
+    for {ObservedNavigation} and {ResponseFromCacheSuffix}. Abandonment from
+    backgrounding or hiding are non-terminal, while other abandonment for other
+    reasons are terminal. This means this metric might be recorded multiple
+    times if the navigation is only abandoned non-terminally before, but only
+    once for each abandonment reason. Metrics logged after non-terminal
+    abandonment will be marked with the suffixes {BackgroundSuffix} and/or
+    {HiddenSuffix}, and if the navigation involves a non-SRP URL but then
+    redirected to SRP URL, the logged metric will be marked with {NonSRPSuffix}.
+    The milestone recorded will be the latest milestone (if there are multiple
+    milestones reached before the abandonment). The metric is emitted when the
+    navigation gets abandoned.
   </summary>
   <token key="BackgroundSuffix" variants="BackgroundSuffix"/>
   <token key="HiddenSuffix" variants="HiddenSuffix"/>
+  <token key="ResponseFromCacheSuffix" variants="ResponseFromCacheSuffix"/>
   <token key="NavigationAbandonReason" variants="NavigationAbandonReason"/>
   <token key="NavigationMilestone" variants="NavigationMilestone"/>
   <token key="NonSRPSuffix" variants="NonSRPSuffix"/>
diff --git a/tools/metrics/histograms/metadata/password/enums.xml b/tools/metrics/histograms/metadata/password/enums.xml
index db9cc84..6be3a57 100644
--- a/tools/metrics/histograms/metadata/password/enums.xml
+++ b/tools/metrics/histograms/metadata/password/enums.xml
@@ -496,7 +496,11 @@
   <int value="20" label="Omnibox Pedal Suggestion"/>
   <int value="21" label="Default store changed bubble"/>
   <int value="22" label="Manage Password Details Bubble"/>
-  <int value="23" label="Passkey Saved Confirmation Bubble"/>
+  <int value="23" label="Passkey saved confirmation bubble"/>
+  <int value="24" label="Passkey deleted confirmation bubble"/>
+  <int value="25" label="Passkey updated confirmation bubble"/>
+  <int value="26" label="Passkey not accepted confirmation bubble"/>
+  <int value="27" label="Password access loss warning"/>
 </enum>
 
 <enum name="OutdatedGMSDialogDismissalReason">
diff --git a/tools/metrics/histograms/metadata/permissions/histograms.xml b/tools/metrics/histograms/metadata/permissions/histograms.xml
index e1351420e..a6fc0dc 100644
--- a/tools/metrics/histograms/metadata/permissions/histograms.xml
+++ b/tools/metrics/histograms/metadata/permissions/histograms.xml
@@ -500,7 +500,7 @@
 
 <histogram
     name="Permissions.Experimental.PrimaryMainNavigationFinished.{PermissionType}.TopLevelHeaderPolicy"
-    enum="PermissionsPolicyConfiguration" expires_after="2024-09-01">
+    enum="PermissionsPolicyConfiguration" expires_after="2025-01-05">
   <owner>tungnh@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/prefetch/histograms.xml b/tools/metrics/histograms/metadata/prefetch/histograms.xml
index 2c2d65e6..bf81619 100644
--- a/tools/metrics/histograms/metadata/prefetch/histograms.xml
+++ b/tools/metrics/histograms/metadata/prefetch/histograms.xml
@@ -33,6 +33,11 @@
   <variant name="Moderate" summary="Moderate"/>
 </variants>
 
+<variants name="WasServed">
+  <variant name="NotServed" summary="not served"/>
+  <variant name="Served" summary="served"/>
+</variants>
+
 <histogram
     name="PrefetchProxy.AfterClick.BlockUntilHeadDuration.{WasServed}.{SpeculationEagerness}"
     units="ms" expires_after="2025-01-05">
@@ -48,11 +53,40 @@
     until the head of the prefetch response was received. This histogram is only
     recorded for prefetches that are {WasServed} and with an Eagerness of
     {SpeculationEagerness}.
+
+    Only recorded if `kPrefetchNewWaitLoop` is disabled.
+    `PrefetchProxy.AfterClick.BlockUntilHeadDuration2.{WasServed}.{SpeculationEagerness}`
+    is the corresponding metric for the enabled case. This metric will be
+    removed once `kPrefetchNewWaitLoop` is launched.
   </summary>
-  <token key="WasServed">
-    <variant name="NotServed" summary="not served"/>
-    <variant name="Served" summary="served"/>
-  </token>
+  <token key="WasServed" variants="WasServed"/>
+  <token key="SpeculationEagerness" variants="SpeculationEagerness"/>
+</histogram>
+
+<histogram
+    name="PrefetchProxy.AfterClick.BlockUntilHeadDuration2.{WasServed}.{SpeculationEagerness}"
+    units="ms" expires_after="2025-01-05">
+  <owner>kouhei@chromium.org</owner>
+  <owner>kenoss@chromium.org</owner>
+  <owner>chrome-loading@chromium.org</owner>
+  <summary>
+    When a navigation started, `PrefetchMatchResolver2` collects potentially
+    matching prefetches for it. Judging a prefetch accutally matches or not
+    needs non-redirect header. If a prefetch has non-redirect header and
+    actually matched, then this process ends synchronously. If not, it waits
+    non-redirect headers until it finds a matched and servable one or there is
+    no more candidates.
+
+    This histogram is recorded iff the matching process didn't end synchronously
+    and when the judge for a prefetch is determined. The value is the blocked
+    duration (for each tuple (navigation, candidate prefetch)).
+
+    This histogram is only recorded for prefetches that are {WasServed} and with
+    an Eagerness of {SpeculationEagerness}.
+
+    Only recorded if `kPrefetchNewWaitLoop` is enabled.
+  </summary>
+  <token key="WasServed" variants="WasServed"/>
   <token key="SpeculationEagerness" variants="SpeculationEagerness"/>
 </histogram>
 
@@ -141,6 +175,31 @@
   </summary>
 </histogram>
 
+<histogram
+    name="PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch.{SpeculationEagerness}"
+    enum="Boolean" expires_after="2025-01-05">
+  <owner>kouhei@chromium.org</owner>
+  <owner>kenoss@chromium.org</owner>
+  <owner>chrome-loading@chromium.org</owner>
+  <summary>
+    When a navigation started, `PrefetchMatchResolver2` collects potentially
+    matching prefetches for it. Judging a prefetch accutally matches or not
+    needs non-redirect header. If a prefetch has non-redirect header and
+    actually matched, then this process ends synchronously. If not, it waits
+    non-redirect headers until it finds a matched and servable one or there is
+    no more candidates.
+
+    This histogram is recorded when this process ends, for each tuple
+    (navigation, candidate prefetch), true iff didn't end synchronously.
+
+    This histogram is only for prefetches with an eagerness of
+    {SpeculationEagerness}.
+
+    Only recorded if `kPrefetchNewWaitLoop` is enabled.
+  </summary>
+  <token key="SpeculationEagerness" variants="SpeculationEagerness"/>
+</histogram>
+
 <histogram name="PrefetchProxy.AfterClick.RedirectChainSize" units="count"
     expires_after="2024-09-01">
   <owner>kouhei@chromium.org</owner>
@@ -157,14 +216,20 @@
   <owner>kouhei@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
   <summary>
-    If a prefetch request has started but the head of the prefetch response has
-    not been received yet when a navigation to a URL that matches the prefetch,
-    then the prefetch service will block until the head of the prefetch response
-    is received and then decide to serve or not serve the prefetch.
+    If a prefetch request has started but the non-redirect header of the
+    prefetch response has not been received yet when a navigation to a URL that
+    is expected to matche the prefetch, then the prefetch service will block
+    until the non-redirect header of the prefetch response is received and then
+    decide to serve or not serve the prefetch.
 
     This histogram records whether or not prefetch service will block or not for
     a prefetch. This histogram is only for prefetches with an Eagerness of
     {SpeculationEagerness}.
+
+    Only recorded if `kPrefetchNewWaitLoop` is disabled.
+    `PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch.{SpeculationEagerness}`
+    is the corresponding metric for the enabled case. This metric will be
+    removed once `kPrefetchNewWaitLoop` is launched.
   </summary>
   <token key="SpeculationEagerness" variants="SpeculationEagerness"/>
 </histogram>
diff --git a/tools/metrics/histograms/metadata/session/histograms.xml b/tools/metrics/histograms/metadata/session/histograms.xml
index 984325d6..04bf2dd 100644
--- a/tools/metrics/histograms/metadata/session/histograms.xml
+++ b/tools/metrics/histograms/metadata/session/histograms.xml
@@ -984,7 +984,7 @@
 </histogram>
 
 <histogram name="Session.WebState.CustomWebViewSerializedSize" units="KB"
-    expires_after="2024-10-08">
+    expires_after="2025-10-08">
   <owner>justincohen@chromium.org</owner>
   <owner>ajuma@chromium.org</owner>
   <summary>
@@ -995,7 +995,7 @@
 </histogram>
 
 <histogram name="Session.WebStates.AllSerializedCertPolicyCachesSize"
-    units="KB" expires_after="2024-10-08">
+    units="KB" expires_after="2025-10-08">
   <owner>ajuma@chromium.org</owner>
   <owner>justincohen@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/storage/histograms.xml b/tools/metrics/histograms/metadata/storage/histograms.xml
index 604b67c..8beeb03 100644
--- a/tools/metrics/histograms/metadata/storage/histograms.xml
+++ b/tools/metrics/histograms/metadata/storage/histograms.xml
@@ -545,20 +545,6 @@
   </summary>
 </histogram>
 
-<histogram name="Storage.FileSystemAccess.AttemptToObserveSymlinkOrJunction"
-    enum="Boolean" expires_after="2025-09-01">
-  <owner>memmott@chromium.org</owner>
-  <owner>dslee@chromium.org</owner>
-  <owner>christinesm@chromium.org</owner>
-  <owner>src/content/browser/file_system_access/OWNERS</owner>
-  <summary>
-    Records whether a site attempted to observe a symlink or a junction using
-    the File System Observer API. Recorded on every observe() call after
-    validating the handle and ensuring that read permissions on the handle are
-    granted.
-  </summary>
-</histogram>
-
 <histogram name="Storage.FileSystemAccess.FilePathWatcherCallbackError"
     enum="FileSystemAccessWatchWithChangeInfoResult" expires_after="2025-06-27">
   <owner>memmott@chromium.org</owner>
diff --git a/tools/metrics/structured/sync/structured.xml b/tools/metrics/structured/sync/structured.xml
index 79692280..24265b4 100644
--- a/tools/metrics/structured/sync/structured.xml
+++ b/tools/metrics/structured/sync/structured.xml
@@ -1007,6 +1007,7 @@
     <variant value="6">APP_INSTALL_URI_GETIT</variant>
     <variant value="7">APP_INSTALL_URI_LAUNCHER</variant>
     <variant value="8">APP_INSTALL_URI_PERIPHERALS</variant>
+    <variant value="9">APP_INSTALL_URI_MALL_V2</variant>
   </enum>
 
   <enum name="CameraAppLaunchType">
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 4790080d..5aadbca 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -16358,6 +16358,28 @@
   </metric>
 </event>
 
+<event name="PasswordManager.FirstCCTPageLoad" singular="True">
+  <owner>ioanap@chromium.org</owner>
+  <owner>kazinova@google.com</owner>
+  <summary>
+    Logged on Android for the first page that a Chrome Custom Tab loads.
+    Recorded when the primary page changes away from the first loaded one or
+    when the WebContents is destroyed.
+  </summary>
+  <metric name="HasPasswordForm" enum="Boolean">
+    <summary>
+      True if there was a password form detected on the page.
+    </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <enumeration/>
+        </statistics>
+      </history>
+    </aggregation>
+  </metric>
+</event>
+
 <event name="PasswordManager.LeakWarningDialog">
   <owner>jkeitel@google.com</owner>
   <owner>chrome-duplex@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 a02c279f..7d07ade 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v47.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "265b0c463b580bc38ece580036c6f978ac973a9b",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/4acdcdfaa037f456d793ba8e08c44f1756069971/trace_processor_shell.exe"
+            "hash": "8841f135ff04107027b3855e509aa65bbb72b763",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/f6c1662f8536f69d54ae91d75fe2a3c0a8ec3c10/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "b5e5fcaf0e8ae5af509255c50c116d2ad6308221",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v47.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "9b09644fbb459a3f79cdb04e042ce8ae99748c1b",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/4acdcdfaa037f456d793ba8e08c44f1756069971/trace_processor_shell"
+            "hash": "6f51b1fe12e8ac653a7e17117781ddfd1d8ba3f7",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/91ef2fc5882363981a66c12e496d7bc595df5b5f/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/results_processor/command_line.py b/tools/perf/core/results_processor/command_line.py
index 03cb646b..02f88ee 100644
--- a/tools/perf/core/results_processor/command_line.py
+++ b/tools/perf/core/results_processor/command_line.py
@@ -117,6 +117,11 @@
       action='store_true',
       help=('Argument to enable fetching data from a device.'))
   device_group.add_argument(
+      '--fetch-device-data-on-success',
+      action='store_true',
+      help=('When --fetch-device-data is enabled, this switch ensures that '
+            'data is only pulled after a successful run (exited with 0)'))
+  device_group.add_argument(
       '--fetch-device-data-platform',
       dest='fetch_data_platform',
       choices=['android', 'chromeos'],
@@ -207,6 +212,11 @@
       raise argparse.ArgumentError(options.device_data_path,
                                    ('--fetch-data-path-device must be set '
                                     'with --fetch-device-data'))
+  if options.fetch_device_data_on_success:
+    if not options.fetch_device_data:
+      raise argparse.ArgumentError(options.fetch_device_data_on_success,
+                                   ('--fetch-device-data must be set '
+                                    'with --fetch-device-data-on-success'))
 
 
 def _CreateTopLevelParser(standalone):
diff --git a/tools/perf/core/results_processor/processor.py b/tools/perf/core/results_processor/processor.py
index ac9e1be8..900c7c1 100644
--- a/tools/perf/core/results_processor/processor.py
+++ b/tools/perf/core/results_processor/processor.py
@@ -126,10 +126,15 @@
 
     print('View results at file://', output_file, sep='')
 
-  if options.fetch_device_data:
-    PullDeviceArtifacts(options)
+  exit_code = GenerateExitCode(test_results)
 
-  return GenerateExitCode(test_results)
+  if options.fetch_device_data:
+    if options.fetch_device_data_on_success and exit_code != 0:
+      logging.warning('Not fetching device data due to non zero exit code.')
+    else:
+      PullDeviceArtifacts(options)
+
+  return exit_code
 
 
 def _AmortizeProcessingDuration(processing_duration, test_results):
diff --git a/tools/typescript/definitions/autofill_private.d.ts b/tools/typescript/definitions/autofill_private.d.ts
index fd6031a..1950834 100644
--- a/tools/typescript/definitions/autofill_private.d.ts
+++ b/tools/typescript/definitions/autofill_private.d.ts
@@ -116,6 +116,7 @@
         ADDRESS_HOME_APT_TYPE,
         ADDRESS_HOME_HOUSE_NUMBER_AND_APT,
         SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES,
+        IMPROVED_PREDICTION,
       }
 
       export enum AddressRecordType {
diff --git a/tools/ubsan/ignorelist.txt b/tools/ubsan/ignorelist.txt
index 4dc90f7..ee5a7098 100644
--- a/tools/ubsan/ignorelist.txt
+++ b/tools/ubsan/ignorelist.txt
@@ -30,7 +30,3 @@
 # https://crbug.com/363225486
 [alignment]
 fun:*base*Pickle*
-
-# https://crbug.com/363264997
-[null]
-fun:*performance_manager*PerformanceManagerImpl*RunCallbackWithGraph*
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index fd56d21..59e948a6 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -417,7 +417,10 @@
     }
 
     if (is_mac) {
-      sources += [ "platform/browser_accessibility_mac_unittest.mm" ]
+      sources += [
+        "platform/browser_accessibility_mac_unittest.mm",
+        "platform/inspect/ax_element_wrapper_mac_unittest.mm",
+      ]
       deps += [ "//ui/base:test_support" ]
     }
 
diff --git a/ui/accessibility/platform/inspect/ax_element_wrapper_mac.mm b/ui/accessibility/platform/inspect/ax_element_wrapper_mac.mm
index 47f85868..85a0cef 100644
--- a/ui/accessibility/platform/inspect/ax_element_wrapper_mac.mm
+++ b/ui/accessibility/platform/inspect/ax_element_wrapper_mac.mm
@@ -138,6 +138,7 @@
         }
       }
     }
+    return NSMakePoint(0, 0);
   }
 
   NOTREACHED_IN_MIGRATION()
diff --git a/ui/accessibility/platform/inspect/ax_element_wrapper_mac_unittest.mm b/ui/accessibility/platform/inspect/ax_element_wrapper_mac_unittest.mm
new file mode 100644
index 0000000..0606901d1
--- /dev/null
+++ b/ui/accessibility/platform/inspect/ax_element_wrapper_mac_unittest.mm
@@ -0,0 +1,45 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/accessibility/platform/inspect/ax_element_wrapper_mac.h"
+
+// #include <string>
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+// Tests retrieving the position from an AXElementWrapper.
+TEST(AXElementWrapperMacTest, Position) {
+  // Test with an NSAccessibilityElement inside an AXElementWrapper.
+  // With no frame set, we expect (0, 0).
+  id node = [[NSAccessibilityElement alloc] init];
+  AXElementWrapper wrapper(node);
+  EXPECT_TRUE(NSEqualPoints(wrapper.Position(), NSZeroPoint));
+
+  // Create a node with a frame, and confirm picking up the position (origin).
+  NSRect frame = NSMakeRect(10, 20, 30, 40);
+  node = [NSAccessibilityElement
+      accessibilityElementWithRole:NSAccessibilityApplicationRole
+                             frame:frame
+                             label:@""
+                            parent:nil];
+  AXElementWrapper app_wrapper(node);
+  EXPECT_TRUE(NSEqualPoints(app_wrapper.Position(), frame.origin));
+
+  // Test with an AXUIElementRef inside an AXElementWrapper.
+  AXUIElementRef element = AXUIElementCreateApplication(getpid());
+  AXElementWrapper element_wrapper((__bridge id)element);
+  EXPECT_TRUE(NSEqualPoints(element_wrapper.Position(), NSZeroPoint));
+  CFRelease(element);
+
+  // Anything other than an NSAccessibilityElement or AXUIElementRef inside
+  // an AXElementWrapper should CHECK.
+  id object = [[NSObject alloc] init];
+  AXElementWrapper non_ax_wrapper(object);
+  EXPECT_DEATH({ non_ax_wrapper.Position(); }, "");
+}
+
+}  // namespace ui
diff --git a/ui/aura/client/drag_drop_client_observer.h b/ui/aura/client/drag_drop_client_observer.h
index a4d48ab..7b4b4ef 100644
--- a/ui/aura/client/drag_drop_client_observer.h
+++ b/ui/aura/client/drag_drop_client_observer.h
@@ -62,6 +62,11 @@
   // the operation returned from StartDragAndDrop.
   virtual void OnDragActionsChanged(int actions) {}
 #endif
+
+  // Called during destruction of the observed `DragDropClient`. Note that the
+  // client being destroyed does not necessarily imply the end of the drag
+  // session.
+  virtual void OnDragDropClientDestroying() {}
 };
 
 }  // namespace client
diff --git a/ui/aura/demo/demo_main.cc b/ui/aura/demo/demo_main.cc
index 3e973fc..c425d2f 100644
--- a/ui/aura/demo/demo_main.cc
+++ b/ui/aura/demo/demo_main.cc
@@ -191,7 +191,7 @@
   auto context_factory = std::make_unique<ui::InProcessContextFactory>(
       &host_frame_sink_manager, &frame_sink_manager, /*output_to_window=*/true);
 
-  base::PowerMonitor::Initialize(
+  base::PowerMonitor::GetInstance()->Initialize(
       std::make_unique<base::PowerMonitorDeviceSource>());
 
   std::unique_ptr<aura::Env> env = aura::Env::CreateInstance();
diff --git a/ui/aura/test/test_screen.cc b/ui/aura/test/test_screen.cc
index bfb05ec2..4cef1fb 100644
--- a/ui/aura/test/test_screen.cc
+++ b/ui/aura/test/test_screen.cc
@@ -111,6 +111,11 @@
   display_list().UpdateDisplay(display);
 }
 
+void TestScreen::SetPreferredScaleFactorForWindow(gfx::NativeWindow window,
+                                                  float scale_factor) {
+  preferred_scale_factors_[window] = scale_factor;
+}
+
 gfx::Transform TestScreen::GetRotationTransform() const {
   display::Display display = GetPrimaryDisplay();
   return display::CreateRotationTransform(display.rotation(),
@@ -197,6 +202,15 @@
   return {};
 }
 
+std::optional<float> TestScreen::GetPreferredScaleFactorForWindow(
+    gfx::NativeWindow window) const {
+  if (auto it = preferred_scale_factors_.find(window);
+      it != preferred_scale_factors_.end()) {
+    return it->second;
+  }
+  return Screen::GetPreferredScaleFactorForWindow(window);
+}
+
 TestScreen::TestScreen(const gfx::Rect& screen_bounds) {
   static int64_t synthesized_display_id = 2000;
   display::Display display(synthesized_display_id++);
diff --git a/ui/aura/test/test_screen.h b/ui/aura/test/test_screen.h
index 9c43d37..09f9957 100644
--- a/ui/aura/test/test_screen.h
+++ b/ui/aura/test/test_screen.h
@@ -5,6 +5,8 @@
 #ifndef UI_AURA_TEST_TEST_SCREEN_H_
 #define UI_AURA_TEST_TEST_SCREEN_H_
 
+#include <map>
+
 #include "base/memory/raw_ptr.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display.h"
@@ -43,6 +45,8 @@
   void SetDisplayRotation(display::Display::Rotation rotation);
   void SetUIScale(float ui_scale);
   void SetWorkAreaInsets(const gfx::Insets& insets);
+  void SetPreferredScaleFactorForWindow(gfx::NativeWindow window,
+                                        float scale_factor);
 
  protected:
   static gfx::NativeWindow GetWindowForPoint(Window* window,
@@ -67,11 +71,14 @@
   display::Display GetDisplayNearestWindow(
       gfx::NativeWindow window) const override;
   std::string GetCurrentWorkspace() override;
+  std::optional<float> GetPreferredScaleFactorForWindow(
+      gfx::NativeWindow window) const override;
 
  private:
   explicit TestScreen(const gfx::Rect& screen_bounds);
 
   raw_ptr<aura::WindowTreeHost> host_ = nullptr;
+  std::unordered_map<gfx::NativeWindow, float> preferred_scale_factors_;
 
   float ui_scale_ = 1.0f;
 };
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 5180a4eb..fa62b07 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -267,8 +267,10 @@
   // See: http://crbug.com/956264.
   host_->SetVisible(true);
 
-  if (base::PowerMonitor::IsInitialized())
-    base::PowerMonitor::AddPowerSuspendObserver(this);
+  if (auto* power_monitor = base::PowerMonitor::GetInstance();
+      power_monitor->IsInitialized()) {
+    power_monitor->AddPowerSuspendObserver(this);
+  }
 
   if (command_line->HasSwitch(switches::kUISlowAnimations)) {
     slow_animations_ = std::make_unique<ScopedAnimationDurationScaleMode>(
@@ -281,8 +283,10 @@
 
 Compositor::~Compositor() {
   TRACE_EVENT0("shutdown,viz", "Compositor::destructor");
-  if (base::PowerMonitor::IsInitialized())
-    base::PowerMonitor::RemovePowerSuspendObserver(this);
+  if (auto* power_monitor = base::PowerMonitor::GetInstance();
+      power_monitor->IsInitialized()) {
+    power_monitor->RemovePowerSuspendObserver(this);
+  }
 
   for (auto& observer : observer_list_)
     observer.OnCompositingShuttingDown(this);
diff --git a/ui/gfx/color_conversion_sk_filter_cache.cc b/ui/gfx/color_conversion_sk_filter_cache.cc
index ab837860..e4cbd348 100644
--- a/ui/gfx/color_conversion_sk_filter_cache.cc
+++ b/ui/gfx/color_conversion_sk_filter_cache.cc
@@ -16,7 +16,7 @@
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
 #include "third_party/skia/include/gpu/graphite/Recorder.h"
 #include "third_party/skia/include/gpu/graphite/Surface.h"
diff --git a/ui/gfx/vector_icon_types.h b/ui/gfx/vector_icon_types.h
index 065e21c..ebeabd6 100644
--- a/ui/gfx/vector_icon_types.h
+++ b/ui/gfx/vector_icon_types.h
@@ -6,6 +6,7 @@
 #define UI_GFX_VECTOR_ICON_TYPES_H_
 
 #include "base/containers/span.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "third_party/skia/include/core/SkScalar.h"
 #include "ui/gfx/animation/tween.h"
 
@@ -96,7 +97,11 @@
   VectorIconRep(const VectorIconRep&) = delete;
   VectorIconRep& operator=(const VectorIconRep&) = delete;
 
-  base::span<const PathElement> path;
+  // RAW_PTR_EXCLUSION: #global-scope
+  // TODO(crbug.com/363264995): Convert to base::raw_span once that no longer
+  // results in the type being marked as a complex type since that prevents
+  // initialization of the type in constexpr contexts.
+  RAW_PTR_EXCLUSION base::span<const PathElement> path;
 };
 
 // A vector icon that stores one or more representations to be used for various
@@ -113,7 +118,11 @@
 
   bool is_empty() const { return reps.empty(); }
 
-  base::span<const VectorIconRep> reps;
+  // RAW_PTR_EXCLUSION: #global-scope
+  // TODO(crbug.com/363264995): Convert to base::raw_span once that no longer
+  // results in the type being marked as a complex type since that prevents
+  // initialization of the type in constexpr contexts.
+  RAW_PTR_EXCLUSION base::span<const VectorIconRep> reps;
 
   // A human-readable name, useful for debugging, derived from the name of the
   // icon file. This can also be used as an identifier, but vector icon targets
diff --git a/ui/gl/init/create_gr_gl_interface.h b/ui/gl/init/create_gr_gl_interface.h
index 5e1a5aa..d39a9cc 100644
--- a/ui/gl/init/create_gr_gl_interface.h
+++ b/ui/gl/init/create_gr_gl_interface.h
@@ -5,8 +5,7 @@
 #ifndef UI_GL_INIT_CREATE_GR_GL_INTERFACE_H_
 #define UI_GL_INIT_CREATE_GR_GL_INTERFACE_H_
 
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
-
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 #include "ui/gl/init/gl_init_export.h"
 
 namespace gl {
diff --git a/ui/gl/swap_chain_presenter.cc b/ui/gl/swap_chain_presenter.cc
index 17788e5..b44e59e 100644
--- a/ui/gl/swap_chain_presenter.cc
+++ b/ui/gl/swap_chain_presenter.cc
@@ -504,11 +504,11 @@
       d3d11_device_(d3d11_device),
       dcomp_device_(dcomp_device),
       is_on_battery_power_(
-          base::PowerMonitor::AddPowerStateObserverAndReturnOnBatteryState(
-              this)) {}
+          base::PowerMonitor::GetInstance()
+              ->AddPowerStateObserverAndReturnOnBatteryState(this)) {}
 
 SwapChainPresenter::~SwapChainPresenter() {
-  base::PowerMonitor::RemovePowerStateObserver(this);
+  base::PowerMonitor::GetInstance()->RemovePowerStateObserver(this);
 }
 
 DXGI_FORMAT SwapChainPresenter::GetSwapChainFormat(
diff --git a/ui/gl/vsync_thread_win.cc b/ui/gl/vsync_thread_win.cc
index 3657b00..8031c4d 100644
--- a/ui/gl/vsync_thread_win.cc
+++ b/ui/gl/vsync_thread_win.cc
@@ -177,8 +177,8 @@
       vsync_provider_(gfx::kNullAcceleratedWidget),
       dxgi_adapter_(GetAdapter(dxgi_device.Get())),
       original_adapter_luid_(GetLuid(dxgi_adapter_.Get())) {
-  is_suspended_ =
-      base::PowerMonitor::AddPowerSuspendObserverAndReturnSuspendedState(this);
+  is_suspended_ = base::PowerMonitor::GetInstance()
+                      ->AddPowerSuspendObserverAndReturnSuspendedState(this);
   vsync_thread_.StartWithOptions(
       base::Thread::Options(base::ThreadType::kDisplayCritical));
 }
diff --git a/ui/ozone/demo/skia/skia_gl_renderer.cc b/ui/ozone/demo/skia/skia_gl_renderer.cc
index e807f39..bbb557f 100644
--- a/ui/ozone/demo/skia/skia_gl_renderer.cc
+++ b/ui/ozone/demo/skia/skia_gl_renderer.cc
@@ -17,13 +17,13 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkFont.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLAssembleInterface.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "third_party/skia/include/private/chromium/GrDeferredDisplayList.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/gpu_fence.h"
diff --git a/ui/ozone/demo/skia/skia_gl_renderer.h b/ui/ozone/demo/skia/skia_gl_renderer.h
index fb3d92d5..edbf21d1 100644
--- a/ui/ozone/demo/skia/skia_gl_renderer.h
+++ b/ui/ozone/demo/skia/skia_gl_renderer.h
@@ -14,7 +14,7 @@
 #include "base/synchronization/lock.h"
 #include "base/threading/simple_thread.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/private/chromium/GrDeferredDisplayListRecorder.h"
 #include "ui/gfx/swap_result.h"
 #include "ui/ozone/demo/renderer_base.h"
diff --git a/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc b/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
index 4dcd78c..5db1e212 100644
--- a/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
+++ b/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
@@ -20,12 +20,12 @@
 #include "base/trace_event/trace_event.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkTypeface.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLAssembleInterface.h"
 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
 #include "third_party/skia/include/private/chromium/GrDeferredDisplayList.h"
 #include "ui/display/types/display_snapshot.h"
 #include "ui/gfx/geometry/rect_conversions.h"
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
index dfab59a..35c02b3 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
@@ -213,6 +213,7 @@
   for (size_t i = 0; i < connector_and_crtc_count; ++i) {
     auto& connector_properties = drm_state.connector_properties.emplace_back();
     connector_properties.id = kConnectorIdBase + i;
+    connector_properties.connection = true;
     connector_properties.properties.push_back(
         {.id = kCrtcIdPropId, .value = 0});
   }
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index b19607f..bcb8d50e 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -358,10 +358,13 @@
   }
 
   const PlatformRuntimeProperties& GetPlatformRuntimeProperties() override {
-    using SupportsSsdForTest =
-        OzonePlatform::PlatformRuntimeProperties::SupportsSsdForTest;
+    using SupportsForTest =
+        OzonePlatform::PlatformRuntimeProperties::SupportsForTest;
     const auto& override_supports_ssd_for_test = OzonePlatform::
         PlatformRuntimeProperties::override_supports_ssd_for_test;
+    const auto& override_supports_per_window_scaling_for_test =
+        OzonePlatform::PlatformRuntimeProperties::
+            override_supports_per_window_scaling_for_test;
 
     static OzonePlatform::PlatformRuntimeProperties properties;
     if (connection_) {
@@ -370,8 +373,8 @@
       // the browser process side.
       properties.supports_server_side_window_decorations =
           (connection_->xdg_decoration_manager_v1() != nullptr &&
-          override_supports_ssd_for_test == SupportsSsdForTest::kNotSet) ||
-          override_supports_ssd_for_test == SupportsSsdForTest::kYes;
+           override_supports_ssd_for_test == SupportsForTest::kNotSet) ||
+          override_supports_ssd_for_test == SupportsForTest::kYes;
       properties.supports_overlays =
           connection_->ShouldUseOverlayDelegation() &&
           connection_->viewporter();
@@ -400,7 +403,11 @@
                         ->SupportsCompositingOnlySurface()
               : true;
       properties.supports_per_window_scaling =
-          connection_->UsePerSurfaceScaling();
+          (connection_->UsePerSurfaceScaling() &&
+           override_supports_per_window_scaling_for_test ==
+               SupportsForTest::kNotSet) ||
+          (override_supports_per_window_scaling_for_test ==
+           SupportsForTest::kYes);
 
       if (surface_factory_) {
         DCHECK(has_initialized_gpu());
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index 7cc3a6c..4ec3564 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -43,9 +43,13 @@
 
 }  // namespace
 
-OzonePlatform::PlatformRuntimeProperties::SupportsSsdForTest
+OzonePlatform::PlatformRuntimeProperties::SupportsForTest
     OzonePlatform::PlatformRuntimeProperties::override_supports_ssd_for_test =
-        OzonePlatform::PlatformRuntimeProperties::SupportsSsdForTest::kNotSet;
+        OzonePlatform::PlatformRuntimeProperties::SupportsForTest::kNotSet;
+
+OzonePlatform::PlatformRuntimeProperties::SupportsForTest OzonePlatform::
+    PlatformRuntimeProperties::override_supports_per_window_scaling_for_test =
+        OzonePlatform::PlatformRuntimeProperties::SupportsForTest::kNotSet;
 
 OzonePlatform::PlatformProperties::PlatformProperties() = default;
 OzonePlatform::PlatformProperties::~PlatformProperties() = default;
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index 6018130..b255090 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -160,9 +160,8 @@
   struct PlatformRuntimeProperties {
     PlatformRuntimeProperties();
 
-    // Values to override the value of the
-    // supports_server_side_window_decorations property in tests.
-    enum class SupportsSsdForTest {
+    // Values to override the value of a property in tests.
+    enum class SupportsForTest {
       kNotSet,  // The property is not overridden.
       kYes,     // The platform should return true.
       kNo,      // The plafrorm should return false.
@@ -179,7 +178,7 @@
     // this parameter allows setting the desired state in tests.  The platform
     // must have the appropriate logic in its GetPlatformRuntimeProperties()
     // method.
-    static SupportsSsdForTest override_supports_ssd_for_test;
+    static SupportsForTest override_supports_ssd_for_test;
 
     // Wayland only: determines whether solid color overlays can be delegated
     // without a backing image via a wayland protocol.
@@ -225,6 +224,9 @@
     // Indicates whether the platform supports system-controlled per-window
     // scaling.
     bool supports_per_window_scaling = false;
+
+    // Allows overriding whether per window scaling is enabled in tests.
+    static SupportsForTest override_supports_per_window_scaling_for_test;
   };
 
   // Corresponds to chrome_browser_main_extra_parts.h.
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
index f283905..57820f2 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
@@ -127,6 +127,9 @@
 
 DesktopDragDropClientOzone::~DesktopDragDropClientOzone() {
   ResetDragDropTarget();
+  for (aura::client::DragDropClientObserver& observer : observers_) {
+    observer.OnDragDropClientDestroying();
+  }
 }
 
 DragOperation DesktopDragDropClientOzone::StartDragAndDrop(
diff --git a/v8 b/v8
index 225c383..a9fc592 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 225c383245605b717d45c0debf45b8f6b4fabc5b
+Subproject commit a9fc5925179a7e303fc7f9ccb9a40e11c6405d0e