diff --git a/.eslintrc.js b/.eslintrc.js
index 0e5a6134..78007a5d 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -81,6 +81,18 @@
       // Prevent a common misuse of "!" operator.
       "selector": "TSNonNullExpression > CallExpression > MemberExpression[property.name=/^querySelectorAll$/]",
       "message": "Remove unnecessary \"!\" non-null operator after querySelectorAll(). It always returns a non-null result",
+    },
+    {
+      // https://google.github.io/styleguide/jsguide.html#es-module-imports
+      //  1) Matching only import URLs that have at least one '/' slash, to
+      //     avoid false positives for NodeJS imports like
+      //     `import fs from 'fs';`.
+      //     Using '\u002F' instead of '/' as the suggested workaround for
+      //     https://github.com/eslint/eslint/issues/16555
+      //  2) Allowing extensions that have a length between 2-4 characters
+      //     (for example js, css, json)
+      "selector": "ImportDeclaration[source.value=/^.*\\u002F.*(?<!\\.[a-z]{2}|\\.[a-z]{3}|\\.[a-z]{4})$/]",
+      "message": "Disallowed extensionless import. Explicitly specify the extension suffix."
     }],
     'no-throw-literal': 'error',
     'no-trailing-spaces': 'error',
diff --git a/.gitmodules b/.gitmodules
index 7f33e74..511346a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -617,10 +617,6 @@
 	path = chrome/browser/platform_experience/win
 	url = https://chrome-internal.googlesource.com/chrome/browser/platform_experience/win
 	gclient-condition = checkout_src_internal
-[submodule "chrome/browser/resources/chromeos/quickoffice"]
-	path = chrome/browser/resources/chromeos/quickoffice
-	url = https://chrome-internal.googlesource.com/quickoffice/crx
-	gclient-condition = (checkout_chromeos or checkout_linux) and checkout_src_internal
 [submodule "chrome/browser/resources/settings/internal"]
 	path = chrome/browser/resources/settings/internal
 	url = https://chrome-internal.googlesource.com/chrome/browser/resources/settings_internal
diff --git a/AUTHORS b/AUTHORS
index b3e5fe0..1e1799bf 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -154,6 +154,7 @@
 Arnaud Coomans <hello@acoomans.com>
 Arnaud Mandy <arnaud.mandy@intel.com>
 Arnaud Renevier <a.renevier@samsung.com>
+Arnaud Renevier <arnaud@switchboard.app>
 Arpita Bahuguna <a.bah@samsung.com>
 Arthur Lussos <developer0420@gmail.com>
 Artin Lindqvist <artin.lindqvist.chromium@gmail.com>
diff --git a/DEPS b/DEPS
index 46571d52..685d157 100644
--- a/DEPS
+++ b/DEPS
@@ -253,7 +253,7 @@
   # luci-go CIPD package version.
   # Make sure the revision is uploaded by infra-packagers builder.
   # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console
-  'luci_go': 'git_revision:623f8d17a069eaea6d0fca13147888284ec76ff1',
+  'luci_go': 'git_revision:7dd3e0506c6083aae7a0e413a30e0e11b76da08e',
 
   # This can be overridden, e.g. with custom_vars, to build clang from HEAD
   # instead of downloading the prebuilt pinned revision.
@@ -288,7 +288,7 @@
   'sysroots_json_path': 'build/linux/sysroot_scripts/sysroots.json',
 
   # siso CIPD package version.
-  'siso_version': 'git_revision:110b1d8c0528de153cef259f09f3dc5ee627e6cb',
+  'siso_version': 'git_revision:a3d591f9996c1b28fe5762913bb5bbd69960e4d5',
 
   # download libaom test data
   'download_libaom_testdata': False,
@@ -308,11 +308,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': 'b022b4a9b353aa1edd9fa7e4e752f719c6a39929',
+  'src_internal_revision': '1a7212d3a146d3b55a8d03acbe71ffb5f738b0cb',
   # 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': '9950dc8ec6fd376a48fb1e892ed4ef040bf07331',
+  'skia_revision': '2f07d8e1829ba5bcd0868e3d27e644b87b110598',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # 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 ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '8080a736f13854f806cb115e6ab78a8f4f33d08e',
+  'angle_revision': 'd2cef82a8fd31e1792d845c79b1c85f8c820566c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -347,7 +347,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': 'c231e6f5b152029dbd5fa4a9e0c04095035aec3f',
+  'googletest_revision': 'eff443c6ef5eb6ab598bfaae27f9427fdb4f6af7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -383,11 +383,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': '079202a7fd7b9a5016aba3cd8e4d8a35f12d5ed4',
+  'catapult_revision': '735ecd76d25b509aea2c4b5b04ca85655f2a974c',
   # 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': '2af5a482054fe7fdc7edc80e9ed2008704385ce4',
+  'chromium_variations_revision': 'e78e275e34f94fdf333245137878f0f6482db67d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -403,7 +403,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': 'a11a408bd5d81d06adac27ef13e74a24630d45c6',
+  'devtools_frontend_revision': '936bfad4657d91c0e3badb8eb5dbe9d51610d442',
   # 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.
@@ -427,7 +427,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '88bd94c569428eddacba38af0700d5d09fde3744',
+  'dawn_revision': 'c00db02cbbfedcc03ce23a2f237468d1405a5cda',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -447,7 +447,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libavif
   # and whatever else without interference from each other.
-  'libavif_revision': 'de32f53d28cc8915acba6bedc8ee738e7f7445f5',
+  'libavif_revision': '43ec9ace31c6ca11efddddb61b94b744450d46e2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libavifinfo
   # and whatever else without interference from each other.
@@ -827,7 +827,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '2ab9ac86f0c274f5df3781390b997f901bd4a958',
+    '11eafed3dc08cb3219299d340a5928280140e87a',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -836,7 +836,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '6acfcc10d3cbbd19b428cf5e8fa26d86b383a1ea',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '96b6332c4a2a8881231e3e77a28483dfc228102d',
       'condition': 'checkout_ios',
   },
 
@@ -982,7 +982,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'FOeJcZQ2mA3UXM0yKNQ0R4aeu-xXh3kUwWXV4zko8hoC',
+          'version': 'lF4BpWLcXmIo0uVu9bWTtoknnD3jp2xwe8L0cN-eskMC',
       },
     ],
     'condition': 'checkout_android',
@@ -1157,7 +1157,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '284a94feca39f9a9cdf09f5938312d48b6a17795',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c6c74fc7f352ccbe37d0a5c9d27c7cca37f40d6e',
       'condition': 'checkout_chromeos',
   },
 
@@ -1192,13 +1192,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '59c1900dd6408d4977b2f50b84ad08a475b92215',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ca4cfdaf00d28ceb9c801779a852ad017f6326c1',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '66b4b8e55ce57e007a9abaf12ec4aacc14fbdb47',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '7cb88b45f4b8a5eed9fc512a5c8dd65a082d3456',
     'condition': 'checkout_src_internal',
   },
 
@@ -1456,7 +1456,7 @@
     Var('chromium_git') + '/external/libaddressinput.git' + '@' + 'e8712e415627f22d0b00ebee8db99547077f39bd',
 
   'src/third_party/libaom/source/libaom':
-    Var('aomedia_git') + '/aom.git' + '@' +  '77317a77d321d093f2ac0ba0775c32d28bfeee80',
+    Var('aomedia_git') + '/aom.git' + '@' +  '158761dfb40e77f3a54c3b14a596112837baa24b',
 
   'src/third_party/libavif/src':
     Var('chromium_git') + '/external/github.com/AOMediaCodec/libavif.git' + '@' + Var('libavif_revision'),
@@ -1658,7 +1658,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '7e63f42d755ca255162f1f327126f90e34465581',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'dfcd22426a245ba1a1a5e1e47b17fd27d3409bbb',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151',
@@ -1806,7 +1806,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@074f27e83c5591c976f789a7f3bd4447124e9640',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@0d7ba9788b088d29e91a83f2ecc682aeee03542d',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21',
@@ -1843,10 +1843,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'f4bf599a8b575df685c31d9c4729a70a04e377ed',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '0f6a21daf476a8c60dd8bebed05ad94fc503b66a',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'c56e46958b2d7e2d47576f624ae1f66b2c04a849',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'ab9395924c18aed2c5cfc114c06d153a1c2c68ae',
+    Var('webrtc_git') + '/src.git' + '@' + '802552a8030d82ad07b72aa738f814f3a0030810',
 
   # 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.
@@ -1958,7 +1958,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/eche_app/app',
-        'version': 'PKVmY86ljSVV0JieuprhefluxjPnoOmShmDNe5m5V8AC',
+        'version': 'I7rGBmWd-PoIChUKylzcnFnkqPR7_L8TJ8r4qOrE2j8C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3906,12 +3906,6 @@
       'condition': 'checkout_src_internal',
   },
 
-  'src/chrome/browser/resources/chromeos/quickoffice': {
-      'url': Var('chrome_git') + '/quickoffice/crx.git' + '@' +
-        'cf37f7d861973f9f7f3e937358a452406b16093a',
-      'condition': '(checkout_chromeos or checkout_linux) and checkout_src_internal',
-  },
-
   'src/chrome/browser/resources/settings/internal': {
       'url': Var('chrome_git') + '/chrome/browser/resources/settings_internal.git' + '@' +
         'c8a62277e90b0e4cea284f3ba188d5e1cbfc07ab', # from svn revision 41419
diff --git a/WATCHLISTS b/WATCHLISTS
index 8155ed9..92bdf7a 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2481,6 +2481,7 @@
     'arc_kiosk': ['poromov+watch@chromium.org'],
     'arc_mojom': ['hashimoto+watch@chromium.org'],
     'arc_nearby_share': ['alanding+watch@chromium.org',
+                         'jackshira+watch-nearby-arc@google.com',
                          'phshah+watch@chromium.org'],
     'arc_net': ['abhishekbh@chromium.org',
                 'cernekee@chromium.org',
@@ -2711,6 +2712,7 @@
                           'gordonseto+watch-bluetooth@google.com',
                           'hansberry+watch-bluetooth@chromium.org',
                           'hsuregan+watch-bluetooth@chromium.org',
+                          'jackshira+watch-bluetooth@google.com',
                           'jiajunz+watch-bluetooth@google.com',
                           'khorimoto+watch-bluetooth@chromium.org',
                           'nikhilcn+watch-bluetooth@google.com',
@@ -2737,6 +2739,7 @@
                      'ejcaruso+watch-network@chromium.org',
                      'gordonseto+watch-network@google.com',
                      'hsuregan+watch-network@chromium.org',
+                     'jackshira+watch-network@google.com',
                      'jiajunz+watch-network@google.com',
                      'jonmann+watch-network@chromium.org',
                      'khorimoto+watch-network@chromium.org',
@@ -2749,6 +2752,7 @@
     'chromeos_webui': ['alemate+watch@chromium.org',
                        'rrsilva+watch-chromium@google.com'],
     'chromeos_wifi_sync': ['ajayramamurthy+watch@google.com',
+                           'jackshira+watch-wifi-sync@google.com',
                            'jonmann+watch@chromium.org',
                            'stevenjb+watch@chromium.org'],
     'chrometto_proto_extensions': ['ddrone+watch@google.com'],
@@ -2869,6 +2873,7 @@
     'drive_resource_metadata': ['hashimoto+watch@chromium.org'],
     'eche': ['ajayramamurthy+watch-eche@google.com',
              'crisrael+watch-eche@google.com',
+             'jackshira+watch-eche@google.com',
              'jonmann+watch-eche@chromium.org',
              'pushi+watch-eche@google.com'],
     'eme': ['eme-reviews@chromium.org'],
@@ -3008,6 +3013,7 @@
                    'phweiss+watch@chromium.org'],
     'multidevice': ['ajayramamurthy+watch-multidevice@google.com',
                     'hansberry+watch-multidevice@chromium.org',
+                    'jackshira+watch-multidevice@google.com',
                     'joeantonetti+watch-multidevice@google.com',
                     'jonmann+watch-multidevice@chromium.org'],
     'multipaste': ['andrewxu+watch-multipaste@google.com',
@@ -3027,6 +3033,7 @@
                'hais+watch-nearby@google.com',
                'hansberry+watch-nearby@chromium.org',
                'hansenmichael+watch-nearby@google.com',
+               'jackshira+watch-nearby@google.com',
                'pushi+watch-nearby@google.com',
                'suetfei+watch-nearby@google.com',
                'xlythe+watch-nearby@google.com'],
@@ -3034,6 +3041,7 @@
                         'akingsb+watch-presence@google.com',
                         'crisrael+watch-presence@google.com',
                         'hansberry+watch-presence@chromium.org',
+                        'jackshira+watch-presence@google.com',
                         'julietlevesque+watch-presence@google.com',
                         'polner+watch-presence@google.com'],
     'net': ['net-reviews@chromium.org'],
@@ -3107,6 +3115,7 @@
     'phonehub': ['ajayramamurthy+watch-phonehub@google.com',
                  'crisrael+watch-phonehub@google.com',
                  'hansberry+watch-phonehub@chromium.org',
+                 'jackshira+watch-phonehub@google.com',
                  'jonmann+watch-phonehub@chromium.org',
                  'joeantonetti+watch-phonehub@google.com',
                  'pushi+watch-phonehub@google.com'],
@@ -3202,6 +3211,7 @@
                   'cclem+watch-smartlock@google.com',
                   'ajayramamurthy+watch-smartlock@google.com',
                   'hansberry+watch-smartlock@chromium.org',
+                  'jackshira+watch-smartlock@google.com',
                   'jasonrhee+watch-smartlock@google.com'],
     'smb': ['cros-enterprise-lax+smbwatch@chromium.org'],
     'source_idls': ['jmedley+watch@chromium.org'],
@@ -3256,6 +3266,7 @@
                'cclem+watch-tether@google.com',
                'ajayramamurthy+watch-tether@google.com',
                'hansberry+watch-tether@chromium.org',
+               'jackshira+watch-tether@google.com',
                'jasonrhee+watch-tether@google.com'],
     'text_to_speech': ['dtseng+watch@chromium.org'],
     'textinput': ['keithlee+watch@chromium.org',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 6077ee39..58734f8 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -524,6 +524,7 @@
     "java/src/org/chromium/android_webview/AwContentsOriginMatcher.java",
     "java/src/org/chromium/android_webview/AwContentsStatics.java",
     "java/src/org/chromium/android_webview/AwCookieManager.java",
+    "java/src/org/chromium/android_webview/AwCrashyClassUtils.java",
     "java/src/org/chromium/android_webview/AwDarkMode.java",
     "java/src/org/chromium/android_webview/AwDevToolsServer.java",
     "java/src/org/chromium/android_webview/AwFeatureMap.java",
@@ -579,6 +580,7 @@
     "java/src/org/chromium/android_webview/AwContentsOriginMatcher.java",
     "java/src/org/chromium/android_webview/AwContentsStatics.java",
     "java/src/org/chromium/android_webview/AwCookieManager.java",
+    "java/src/org/chromium/android_webview/AwCrashyClassUtils.java",
     "java/src/org/chromium/android_webview/AwDarkMode.java",
     "java/src/org/chromium/android_webview/AwDataDirLock.java",
     "java/src/org/chromium/android_webview/AwDebug.java",
@@ -660,6 +662,8 @@
     "java/src/org/chromium/android_webview/gfx/RectUtils.java",
     "java/src/org/chromium/android_webview/gfx/RootBeginFrameSourceWebView.java",
     "java/src/org/chromium/android_webview/ip_protection/ExclusionUtilities.java",
+    "java/src/org/chromium/android_webview/media_integrity/AwMediaIntegrityServiceFactory.java",
+    "java/src/org/chromium/android_webview/media_integrity/AwMediaIntegrityServiceImpl.java",
     "java/src/org/chromium/android_webview/metrics/AwMetricsLogUploader.java",
     "java/src/org/chromium/android_webview/metrics/AwMetricsServiceClient.java",
     "java/src/org/chromium/android_webview/metrics/AwMetricsUtils.java",
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn
index a619835..5ef60da 100644
--- a/android_webview/browser/BUILD.gn
+++ b/android_webview/browser/BUILD.gn
@@ -55,6 +55,7 @@
     "aw_cookie_access_policy.h",
     "aw_crash_keys.cc",
     "aw_crash_keys.h",
+    "aw_crashy_class_utils.cc",
     "aw_dark_mode.cc",
     "aw_dark_mode.h",
     "aw_devtools_manager_delegate.cc",
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index c9a238bd4..fd1f569 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -155,4 +155,6 @@
   "+third_party/blink/public/mojom/input",
   # For background tracing setup.
   "+third_party/metrics_proto",
+  # For Android WebView Media Integrity
+  "+third_party/blink/public/mojom/webview/webview_media_integrity.mojom.h",
 ]
diff --git a/android_webview/browser/aw_content_browser_client_receiver_bindings.cc b/android_webview/browser/aw_content_browser_client_receiver_bindings.cc
index 3a74acd..5555a453 100644
--- a/android_webview/browser/aw_content_browser_client_receiver_bindings.cc
+++ b/android_webview/browser/aw_content_browser_client_receiver_bindings.cc
@@ -28,7 +28,9 @@
 #include "mojo/public/cpp/bindings/binder_map.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/mojom/webview/webview_media_integrity.mojom.h"
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
 #include "components/spellcheck/browser/spell_check_host_impl.h"
@@ -142,6 +144,12 @@
                               std::move(receiver));
 }
 
+template <typename Interface>
+void ForwardToJavaFrame(content::RenderFrameHost* render_frame_host,
+                        mojo::PendingReceiver<Interface> receiver) {
+  render_frame_host->GetJavaInterfaces()->GetInterface(std::move(receiver));
+}
+
 }  // anonymous namespace
 
 void AwContentBrowserClient::BindMediaServiceReceiver(
@@ -247,6 +255,13 @@
       base::BindRepeating(create_spellcheck_host),
       content::GetUIThreadTaskRunner({}));
 #endif
+
+  if (base::FeatureList::IsEnabled(
+          features::kWebViewMediaIntegrityApiBlinkExtension) &&
+      !base::FeatureList::IsEnabled(features::kWebViewMediaIntegrityApi)) {
+    map->Add<blink::mojom::WebViewMediaIntegrityService>(base::BindRepeating(
+        &ForwardToJavaFrame<blink::mojom::WebViewMediaIntegrityService>));
+  }
 }
 
 void AwContentBrowserClient::
diff --git a/android_webview/browser/aw_crashy_class_utils.cc b/android_webview/browser/aw_crashy_class_utils.cc
new file mode 100644
index 0000000..57f5e4a
--- /dev/null
+++ b/android_webview/browser/aw_crashy_class_utils.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 <ostream>
+
+#include "android_webview/browser_jni_headers/AwCrashyClassUtils_jni.h"
+#include "base/check.h"
+
+namespace android_webview {
+
+static void JNI_AwCrashyClassUtils_CrashInNative(JNIEnv* env) {
+  CHECK(false) << "WebView Forced Native Crash for WebView Browser Process";
+}
+
+}  // namespace android_webview
diff --git a/android_webview/browser/aw_feature_map.cc b/android_webview/browser/aw_feature_map.cc
index 36df34c..eba1eb8 100644
--- a/android_webview/browser/aw_feature_map.cc
+++ b/android_webview/browser/aw_feature_map.cc
@@ -45,6 +45,7 @@
     &features::kWebViewMuteAudio,
     &features::kWebViewUseInitialNetworkStateAtStartup,
     &features::kWebViewReduceUAAndroidVersionDeviceModel,
+    &features::kWebViewEnableCrash,
 };
 
 // static
diff --git a/android_webview/browser/aw_settings.cc b/android_webview/browser/aw_settings.cc
index 8165cc4..30dc7d2 100644
--- a/android_webview/browser/aw_settings.cc
+++ b/android_webview/browser/aw_settings.cc
@@ -667,6 +667,11 @@
   return false;
 }
 
+base::android::ScopedJavaLocalRef<jobject> AwSettings::GetJavaObject() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return aw_settings_.get(env);
+}
+
 void AwSettings::SetEnterpriseAuthenticationAppLinkPolicyEnabled(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
@@ -712,6 +717,21 @@
   return reinterpret_cast<intptr_t>(settings);
 }
 
+static ScopedJavaLocalRef<jobject> JNI_AwSettings_FromWebContents(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jweb_contents) {
+  base::android::ScopedJavaLocalRef<jobject> jaw_settings;
+
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(jweb_contents);
+  AwSettings* aw_settings =
+      web_contents ? AwSettings::FromWebContents(web_contents) : nullptr;
+  if (aw_settings) {
+    jaw_settings = aw_settings->GetJavaObject();
+  }
+  return jaw_settings;
+}
+
 static ScopedJavaLocalRef<jstring> JNI_AwSettings_GetDefaultUserAgent(
     JNIEnv* env) {
   return base::android::ConvertUTF8ToJavaString(env, GetUserAgent());
diff --git a/android_webview/browser/aw_settings.h b/android_webview/browser/aw_settings.h
index dcc991c..1a75af0 100644
--- a/android_webview/browser/aw_settings.h
+++ b/android_webview/browser/aw_settings.h
@@ -129,6 +129,7 @@
                           const base::android::JavaParamRef<jobject>& obj);
   bool PrefersDarkFromTheme(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& obj);
+  base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
 
   void SetEnterpriseAuthenticationAppLinkPolicyEnabled(
       JNIEnv* env,
diff --git a/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.cc b/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.cc
index 625e976..bc9fd0e 100644
--- a/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.cc
+++ b/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.cc
@@ -65,10 +65,10 @@
     : database_manager_(std::move(database_manager)),
       ui_manager_(std::move(ui_manager)),
       threat_types_(safe_browsing::CreateSBThreatTypeSet(
-          {safe_browsing::SB_THREAT_TYPE_URL_MALWARE,
-           safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
-           safe_browsing::SB_THREAT_TYPE_URL_UNWANTED,
-           safe_browsing::SB_THREAT_TYPE_BILLING})),
+          {safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_MALWARE,
+           safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_PHISHING,
+           safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_UNWANTED,
+           safe_browsing::SBThreatType::SB_THREAT_TYPE_BILLING})),
       allowlist_manager_(allowlist_manager) {}
 
 AwUrlCheckerDelegateImpl::~AwUrlCheckerDelegateImpl() = default;
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc
index 42e69e878..d66734b 100644
--- a/android_webview/common/aw_features.cc
+++ b/android_webview/common/aw_features.cc
@@ -248,5 +248,10 @@
              "WebViewReduceUAAndroidVersionDeviceModel",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// This enables WebView crashes.
+BASE_FEATURE(kWebViewEnableCrash,
+             "WebViewEnableCrash",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 }  // namespace features
 }  // namespace android_webview
diff --git a/android_webview/common/aw_features.h b/android_webview/common/aw_features.h
index 588a7ef9..968f0187 100644
--- a/android_webview/common/aw_features.h
+++ b/android_webview/common/aw_features.h
@@ -57,6 +57,7 @@
 BASE_DECLARE_FEATURE(kWebViewUseInitialNetworkStateAtStartup);
 BASE_DECLARE_FEATURE(kWebViewZoomKeyboardShortcuts);
 BASE_DECLARE_FEATURE(kWebViewReduceUAAndroidVersionDeviceModel);
+BASE_DECLARE_FEATURE(kWebViewEnableCrash);
 
 }  // namespace features
 }  // namespace android_webview
diff --git a/android_webview/common/aw_switches.cc b/android_webview/common/aw_switches.cc
index 24eceecb..3eaa0b3b 100644
--- a/android_webview/common/aw_switches.cc
+++ b/android_webview/common/aw_switches.cc
@@ -91,4 +91,10 @@
 // Force disables 3rd party cookie for all apps.
 const char kWebViewForceDisable3pcs[] = "webview-force-disable-3pcs";
 
+// Enables crashes during WebView startup in the Java layer
+const char kWebViewForceCrashJava[] = "webview-force-crash-java";
+
+// Enables crashes during WebView startup in the Native layer
+const char kWebViewForceCrashNative[] = "webview-force-crash-native";
+
 }  // namespace switches
diff --git a/android_webview/common/aw_switches.h b/android_webview/common/aw_switches.h
index 79c041bf..33aadf7 100644
--- a/android_webview/common/aw_switches.h
+++ b/android_webview/common/aw_switches.h
@@ -27,6 +27,8 @@
 extern const char kWebViewTpcdMetadaComponent[];
 extern const char kWebViewFpsComponent[];
 extern const char kWebViewForceDisable3pcs[];
+extern const char kWebViewForceCrashJava[];
+extern const char kWebViewForceCrashNative[];
 
 }  // namespace switches
 
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index e66ad98..c4c9b606 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -25,6 +25,7 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwContentsStatics;
 import org.chromium.android_webview.AwCookieManager;
+import org.chromium.android_webview.AwCrashyClassUtils;
 import org.chromium.android_webview.AwDarkMode;
 import org.chromium.android_webview.AwFeatureMap;
 import org.chromium.android_webview.AwLocaleConfig;
@@ -270,6 +271,8 @@
             // This runs all the pending tasks queued for after Chromium init is finished,
             // so should be the last thing that happens in startChromiumLocked.
             mFactory.getRunQueue().drainQueue();
+
+            AwCrashyClassUtils.maybeCrashIfEnabled();
         }
         RecordHistogram.recordTimesHistogram(
                 "Android.WebView.Startup.CreationTime.StartChromiumLocked",
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 0fd4375..e64ef28d 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -1107,15 +1107,20 @@
 
     // A Webview class that implements the listener part of the JankTracker requirement. It mirrors
     // JankActivityTracker in starting and stopping the listener and collection.
-    private class AwFrameMetricsListener {
+    private static class AwFrameMetricsListener {
+        private static final WeakHashMap<Window, AwFrameMetricsListener> sWindowMap =
+                new WeakHashMap<>();
+
         private boolean mAttached;
         private JankTrackerStateController mController;
         private JankTracker mJankTracker;
         private WeakReference<Window> mWindow;
+        private int mAttachedWebviews;
+        private int mVisibleWebviews;
 
         private static final WeakHashMap<Window, Integer> sNumActiveScrolls = new WeakHashMap<>();
 
-        public AwFrameMetricsListener() {
+        private AwFrameMetricsListener() {
             FrameMetricsStore metricsStore = new FrameMetricsStore();
             mController =
                     new JankTrackerStateController(
@@ -1125,29 +1130,68 @@
             mAttached = false;
         }
 
-        public void attachListener(Window window) {
+        private void attachListener(Window window) {
             if (mAttached) return;
             mWindow = new WeakReference<Window>(window);
             mController.startMetricCollection(window);
             mAttached = true;
         }
 
-        public void detachListener(Window window) {
+        private void detachListener(Window window) {
             if (!mAttached || window != mWindow.get()) return;
             mController.stopMetricCollection(window);
             mAttached = false;
         }
 
-        public void startListening() {
+        private void incrementAttachedWebviews() {
+            mAttachedWebviews++;
+        }
+
+        private void decrementAttachedWebviews() {
+            mAttachedWebviews--;
+            assert mAttachedWebviews >= 0;
+        }
+
+        private int getAttachedWebviews() {
+            return mAttachedWebviews;
+        }
+
+        public static AwFrameMetricsListener onAttachedToWindow(
+                Window window, AwContents awContents) {
+            AwFrameMetricsListener listener = sWindowMap.get(window);
+            if (listener == null) {
+                listener = new AwFrameMetricsListener();
+                listener.attachListener(window);
+                sWindowMap.put(window, listener);
+            }
+            listener.incrementAttachedWebviews();
+            return listener;
+        }
+
+        public static void onDetachedFromWindow(Window window, AwContents awContents) {
+            AwFrameMetricsListener listener = sWindowMap.get(window);
+            listener.decrementAttachedWebviews();
+            if (listener.getAttachedWebviews() >= 1) return;
+            listener.detachListener(window);
+            sWindowMap.remove(window);
+        }
+
+        public void onWebviewVisible() {
             if (!mAttached) return;
+            mVisibleWebviews++;
+            if (mVisibleWebviews > 1) return;
             mController.startPeriodicReporting();
             mController.startMetricCollection(null);
         }
 
-        public void stopListening() {
+        public void onWebviewHidden() {
             if (!mAttached) return;
-            mController.stopMetricCollection(null);
-            mController.stopPeriodicReporting();
+            mVisibleWebviews--;
+            assert mVisibleWebviews >= 0;
+            if (mVisibleWebviews == 0) {
+                mController.stopMetricCollection(null);
+                mController.stopPeriodicReporting();
+            }
         }
 
         public void onWebContentsScrollStateUpdate(boolean isScrolling, long scrollId) {
@@ -1348,10 +1392,6 @@
 
             onContainerViewChanged();
         }
-
-        if (AwFeatureMap.isEnabled(BaseFeatures.COLLECT_ANDROID_FRAME_TIMELINE_METRICS)) {
-            mAwFrameMetricsListener = new AwFrameMetricsListener();
-        }
     }
 
     private void initWebContents(
@@ -3485,10 +3525,10 @@
             if (mDisplayCutoutController != null) mDisplayCutoutController.onAttachedToWindow();
         }
 
-        if (mAwFrameMetricsListener != null) {
-            Activity activity = getActivity();
-            if (activity != null && mContainerView.isHardwareAccelerated()) {
-                mAwFrameMetricsListener.attachListener(activity.getWindow());
+        if (AwFeatureMap.isEnabled(BaseFeatures.COLLECT_ANDROID_FRAME_TIMELINE_METRICS)) {
+            Window window = mWindowAndroid.getWindowAndroid().getWindow();
+            if (window != null && mContainerView.isHardwareAccelerated()) {
+                mAwFrameMetricsListener = AwFrameMetricsListener.onAttachedToWindow(window, this);
             }
         }
     }
@@ -3508,9 +3548,10 @@
         mAwViewMethods.onDetachedFromWindow();
 
         if (mAwFrameMetricsListener != null) {
-            Activity activity = getActivity();
-            if (activity != null && mContainerView.isHardwareAccelerated()) {
-                mAwFrameMetricsListener.detachListener(activity.getWindow());
+            Window window = mWindowAndroid.getWindowAndroid().getWindow();
+            if (window != null && mContainerView.isHardwareAccelerated()) {
+                AwFrameMetricsListener.onDetachedFromWindow(window, this);
+                mAwFrameMetricsListener = null;
             }
         }
     }
@@ -3575,9 +3616,9 @@
 
             if (mAwFrameMetricsListener != null) {
                 if (mIsWindowVisible) {
-                    mAwFrameMetricsListener.startListening();
+                    mAwFrameMetricsListener.onWebviewVisible();
                 } else {
-                    mAwFrameMetricsListener.stopListening();
+                    mAwFrameMetricsListener.onWebviewHidden();
                 }
             }
         }
@@ -4370,11 +4411,6 @@
         }
     }
 
-    private Activity getActivity() {
-        Context context = mWindowAndroid.getWindowAndroid().getContext().get();
-        return ContextUtils.activityFromContext(context);
-    }
-
     private void maybeRecordMemory() {
         // Note: there is a corner case here: if there are no visible WebViews, but the last one
         // was removed too recently to have had its functor reclaimed, we still collect data.
diff --git a/android_webview/java/src/org/chromium/android_webview/AwCrashyClassUtils.java b/android_webview/java/src/org/chromium/android_webview/AwCrashyClassUtils.java
new file mode 100644
index 0000000..8619b96
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/AwCrashyClassUtils.java
@@ -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.
+
+package org.chromium.android_webview;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.jni_zero.JNINamespace;
+import org.jni_zero.NativeMethods;
+
+import org.chromium.android_webview.common.AwFeatures;
+import org.chromium.android_webview.common.AwSwitches;
+import org.chromium.base.CommandLine;
+
+/** A helper class for testing related to Java and Native crashes. */
+@JNINamespace("android_webview")
+public final class AwCrashyClassUtils {
+
+    public static void maybeCrashIfEnabled() {
+        if (shouldCrashJava()) {
+            throw new RuntimeException("WebView Forced Java Crash for WebView Browser Process");
+        } else if (shouldCrashNative()) {
+            AwCrashyClassUtilsJni.get().crashInNative();
+        }
+    }
+
+    @VisibleForTesting
+    public static boolean shouldCrashJava() {
+        return AwFeatureMap.isEnabled(AwFeatures.WEBVIEW_ENABLE_CRASH)
+                && CommandLine.getInstance().hasSwitch(AwSwitches.WEBVIEW_FORCE_CRASH_JAVA);
+    }
+
+    public static boolean shouldCrashNative() {
+        return AwFeatureMap.isEnabled(AwFeatures.WEBVIEW_ENABLE_CRASH)
+                && CommandLine.getInstance().hasSwitch(AwSwitches.WEBVIEW_FORCE_CRASH_NATIVE);
+    }
+
+    // Do not instantiate this class.
+    private AwCrashyClassUtils() {}
+
+    @NativeMethods
+    interface Natives {
+        void crashInNative();
+    }
+}
diff --git a/android_webview/java/src/org/chromium/android_webview/AwInterfaceRegistrar.java b/android_webview/java/src/org/chromium/android_webview/AwInterfaceRegistrar.java
index 6515e050..f2ec03a9 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwInterfaceRegistrar.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwInterfaceRegistrar.java
@@ -6,7 +6,9 @@
 
 import org.jni_zero.CalledByNative;
 
+import org.chromium.android_webview.media_integrity.AwMediaIntegrityServiceFactory;
 import org.chromium.blink.mojom.Authenticator;
+import org.chromium.blink.mojom.WebViewMediaIntegrityService;
 import org.chromium.components.webauthn.AuthenticatorFactory;
 import org.chromium.content_public.browser.InterfaceRegistrar;
 import org.chromium.content_public.browser.RenderFrameHost;
@@ -28,6 +30,9 @@
             registry.addInterface(
                     Authenticator.MANAGER,
                     new AuthenticatorFactory(renderFrameHost, /* confirmationFactory= */ null));
+            registry.addInterface(
+                    WebViewMediaIntegrityService.MANAGER,
+                    new AwMediaIntegrityServiceFactory(renderFrameHost));
         }
     }
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
index 2fe675e..0aa0e3f 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -17,6 +17,8 @@
 import android.webkit.WebSettings;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
@@ -378,6 +380,12 @@
         // Defer initializing the native side until a native WebContents instance is set.
     }
 
+    /** Get the AwSettings for the WebView with the given WebContents */
+    @Nullable
+    public static AwSettings fromWebContents(@NonNull WebContents webContents) {
+        return AwSettingsJni.get().fromWebContents(webContents);
+    }
+
     public int getUiModeNight() {
         return mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
     }
@@ -2040,6 +2048,8 @@
 
         void destroy(long nativeAwSettings, AwSettings caller);
 
+        AwSettings fromWebContents(WebContents webContents);
+
         void populateWebPreferencesLocked(
                 long nativeAwSettings, AwSettings caller, long webPrefsPtr);
 
diff --git a/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java b/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java
index 68cf79d..c9216872 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java
@@ -163,7 +163,7 @@
     /**
      * Asynchronously obtain a MediaIntegrityProvider implementation.
      *
-     * @param cloudProjectNumber Long representing the cloud project number passed by caller
+     * @param cloudProjectNumber cloud project number passed by caller
      * @param apiStatus Enablement status of the api for given origin
      * @param callback Callback to call with the result containing either a non-null
      *     MediaIntegrityProvider implementation or an appropriate exception.
diff --git a/android_webview/java/src/org/chromium/android_webview/media_integrity/AwMediaIntegrityServiceFactory.java b/android_webview/java/src/org/chromium/android_webview/media_integrity/AwMediaIntegrityServiceFactory.java
new file mode 100644
index 0000000..21a28248
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/media_integrity/AwMediaIntegrityServiceFactory.java
@@ -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.
+
+package org.chromium.android_webview.media_integrity;
+
+import org.chromium.blink.mojom.WebViewMediaIntegrityService;
+import org.chromium.content_public.browser.RenderFrameHost;
+import org.chromium.services.service_manager.InterfaceFactory;
+
+public class AwMediaIntegrityServiceFactory implements InterfaceFactory {
+    private final RenderFrameHost mRenderFrameHost;
+
+    public AwMediaIntegrityServiceFactory(RenderFrameHost renderFrameHost) {
+        mRenderFrameHost = renderFrameHost;
+    }
+
+    @Override
+    public WebViewMediaIntegrityService createImpl() {
+        return new AwMediaIntegrityServiceImpl(mRenderFrameHost);
+    }
+}
diff --git a/android_webview/java/src/org/chromium/android_webview/media_integrity/AwMediaIntegrityServiceImpl.java b/android_webview/java/src/org/chromium/android_webview/media_integrity/AwMediaIntegrityServiceImpl.java
new file mode 100644
index 0000000..d23075c
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/media_integrity/AwMediaIntegrityServiceImpl.java
@@ -0,0 +1,190 @@
+// 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.android_webview.media_integrity;
+
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.chromium.android_webview.AwSettings;
+import org.chromium.android_webview.common.Lifetime;
+import org.chromium.android_webview.common.MediaIntegrityApiStatus;
+import org.chromium.android_webview.common.MediaIntegrityErrorCode;
+import org.chromium.android_webview.common.MediaIntegrityProvider;
+import org.chromium.android_webview.common.PlatformServiceBridge;
+import org.chromium.android_webview.common.ValueOrErrorCallback;
+import org.chromium.base.ThreadUtils;
+import org.chromium.blink.mojom.WebViewMediaIntegrityErrorCode;
+import org.chromium.blink.mojom.WebViewMediaIntegrityProvider;
+import org.chromium.blink.mojom.WebViewMediaIntegrityService;
+import org.chromium.blink.mojom.WebViewMediaIntegrityTokenResponse;
+import org.chromium.content_public.browser.RenderFrameHost;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.browser.WebContentsStatics;
+import org.chromium.mojo.bindings.InterfaceRequest;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.url.GURL;
+
+import java.util.Objects;
+
+/**
+ * Implements the Android WebView Media Integrity API.
+ *
+ * <p>This class delegates token provider implementation via PlatformServiceBridge.
+ *
+ * <p>Instances of this class are bound to individual RenderFrameHosts.
+ */
+@Lifetime.WebView
+public class AwMediaIntegrityServiceImpl implements WebViewMediaIntegrityService {
+    @NonNull private final RenderFrameHost mRenderFrameHost;
+
+    private static @WebViewMediaIntegrityErrorCode.EnumType int errorCodeToMojomErrorCode(
+            @MediaIntegrityErrorCode int code) {
+        switch (code) {
+            case MediaIntegrityErrorCode.INTERNAL_ERROR:
+                return WebViewMediaIntegrityErrorCode.INTERNAL_ERROR;
+            case MediaIntegrityErrorCode.NON_RECOVERABLE_ERROR:
+                return WebViewMediaIntegrityErrorCode.NON_RECOVERABLE_ERROR;
+            case MediaIntegrityErrorCode.API_DISABLED_BY_APPLICATION:
+                return WebViewMediaIntegrityErrorCode.API_DISABLED_BY_APPLICATION;
+            case MediaIntegrityErrorCode.INVALID_ARGUMENT:
+                return WebViewMediaIntegrityErrorCode.INVALID_ARGUMENT;
+            case MediaIntegrityErrorCode.TOKEN_PROVIDER_INVALID:
+                return WebViewMediaIntegrityErrorCode.TOKEN_PROVIDER_INVALID;
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown MediaIntegrityException.ErrorCode " + code);
+        }
+    }
+
+    public AwMediaIntegrityServiceImpl(RenderFrameHost renderFrameHost) {
+        mRenderFrameHost = renderFrameHost;
+    }
+
+    @Override
+    public void close() {}
+
+    @Override
+    public void onConnectionError(MojoException e) {
+        // Close will also be called in case of connection errors.
+    }
+
+    @Override
+    public void getIntegrityProvider(
+            @NonNull InterfaceRequest<WebViewMediaIntegrityProvider> providerRequest,
+            long cloudProjectNumber,
+            @NonNull GetIntegrityProvider_Response callback) {
+        ThreadUtils.assertOnUiThread();
+        // In practice, < 0 means cloudProjectNumber (which is unsigned in the IPC) was >= 2 ** 63.
+        // In theory, we should never be called with a number greater than 2 ** 53 - 1 (JavaScript's
+        // Number.MAX_SAFE_INTEGER), which the renderer SHOULD reject before passing along the IPC.
+        if (cloudProjectNumber < 0
+                || cloudProjectNumber > WebViewMediaIntegrityService.MAX_CLOUD_PROJECT_NUMBER) {
+            // No Java binding equivalent of ReportBadMessage? Ignore is second-best. Note that this
+            // may (on some future garbage collection) result in the Mojo connection being
+            // disconnected.
+            return;
+        }
+
+        final WebContents webContents = WebContentsStatics.fromRenderFrameHost(mRenderFrameHost);
+        if (webContents == null) {
+            callback.call(WebViewMediaIntegrityErrorCode.INTERNAL_ERROR);
+            return;
+        }
+        final AwSettings awSettings = AwSettings.fromWebContents(webContents);
+        if (awSettings == null) {
+            callback.call(WebViewMediaIntegrityErrorCode.INTERNAL_ERROR);
+            return;
+        }
+        final GURL gurl = mRenderFrameHost.getLastCommittedURL();
+        if (gurl == null) {
+            callback.call(WebViewMediaIntegrityErrorCode.INTERNAL_ERROR);
+            return;
+        }
+        final String origin = gurl.getOrigin().getValidSpecOrEmpty();
+        @MediaIntegrityApiStatus final int apiStatus;
+        if ("".equals(origin)) {
+            // An empty origin will be produced for non-http/https URLs.
+            apiStatus = awSettings.getWebViewIntegrityApiDefaultStatus();
+        } else {
+            apiStatus = awSettings.getWebViewIntegrityApiStatusForUri(Uri.parse(origin));
+        }
+        if (apiStatus == MediaIntegrityApiStatus.DISABLED) {
+            callback.call(WebViewMediaIntegrityErrorCode.API_DISABLED_BY_APPLICATION);
+            return;
+        }
+
+        PlatformServiceBridge.getInstance()
+                .getMediaIntegrityProvider(
+                        cloudProjectNumber,
+                        apiStatus,
+                        new ValueOrErrorCallback<MediaIntegrityProvider, Integer>() {
+                            @Override
+                            public void onResult(MediaIntegrityProvider provider) {
+                                ThreadUtils.assertOnUiThread();
+                                Objects.requireNonNull(provider);
+                                WebViewMediaIntegrityProvider.MANAGER.bind(
+                                        new AwMediaIntegrityProviderImpl(provider),
+                                        providerRequest);
+                                callback.call(null);
+                            }
+
+                            @Override
+                            public void onError(Integer error) {
+                                ThreadUtils.assertOnUiThread();
+                                Objects.requireNonNull(error);
+                                callback.call(errorCodeToMojomErrorCode(error));
+                            }
+                        });
+    }
+
+    @Lifetime.WebView
+    private static class AwMediaIntegrityProviderImpl implements WebViewMediaIntegrityProvider {
+        @NonNull private final MediaIntegrityProvider mProvider;
+
+        public AwMediaIntegrityProviderImpl(@NonNull MediaIntegrityProvider provider) {
+            mProvider = provider;
+        }
+
+        @Override
+        public void close() {}
+
+        @Override
+        public void onConnectionError(MojoException e) {
+            // Close will also be called in case of connection errors.
+        }
+
+        @Override
+        public void requestToken(
+                @Nullable String contentBinding, @NonNull RequestToken_Response callback) {
+            ThreadUtils.assertOnUiThread();
+            // The provider is responsible for any contentBinding validation.
+            mProvider.requestToken(
+                    contentBinding,
+                    new ValueOrErrorCallback<String, Integer>() {
+                        @Override
+                        public void onResult(String token) {
+                            ThreadUtils.assertOnUiThread();
+                            Objects.requireNonNull(token);
+                            final WebViewMediaIntegrityTokenResponse response =
+                                    new WebViewMediaIntegrityTokenResponse();
+                            response.setToken(token);
+                            callback.call(response);
+                        }
+
+                        @Override
+                        public void onError(Integer error) {
+                            ThreadUtils.assertOnUiThread();
+                            Objects.requireNonNull(error);
+                            final WebViewMediaIntegrityTokenResponse response =
+                                    new WebViewMediaIntegrityTokenResponse();
+                            response.setErrorCode(errorCodeToMojomErrorCode(error));
+                            callback.call(response);
+                        }
+                    });
+        }
+    }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
index 429de12..cf8ebaa 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
@@ -25,6 +25,7 @@
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.UseParametersRunnerFactory;
@@ -32,11 +33,13 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.gfx.AwGLFunctor;
 import org.chromium.android_webview.test.AwActivityTestRule.TestDependencyFactory;
+import org.chromium.base.BaseFeatures;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.CriteriaNotSatisfiedException;
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Features;
 import org.chromium.content_public.browser.ImeAdapter;
 import org.chromium.content_public.browser.WebContentsAccessibility;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -49,16 +52,16 @@
 import java.util.concurrent.Callable;
 
 /**
- * AwContents garbage collection tests. Most apps relies on WebView being
- * garbage collected to release memory. These tests ensure that nothing
- * accidentally prevents AwContents from garbage collected, leading to leaks.
- * See crbug.com/544098 for why @DisableHardwareAcceleration is needed.
+ * AwContents garbage collection tests. Most apps relies on WebView being garbage collected to
+ * release memory. These tests ensure that nothing accidentally prevents AwContents from garbage
+ * collected, leading to leaks. See crbug.com/544098 for why @DisableHardwareAcceleration is needed.
  */
 @RunWith(Parameterized.class)
 @UseParametersRunnerFactory(AwJUnit4ClassRunnerWithParameters.Factory.class)
 @DoNotBatch(reason = "GC tests require full restarts")
 public class AwContentsGarbageCollectionTest extends AwParameterizedTest {
     @Rule public AwActivityTestRule mActivityTestRule;
+    @Rule public TestRule mProcessor = new Features.InstrumentationProcessor();
 
     public AwContentsGarbageCollectionTest(AwSettingsMutation param) {
         mActivityTestRule =
@@ -120,6 +123,15 @@
     @DisableHardwareAcceleration
     @SmallTest
     @Feature({"AndroidWebView"})
+    @Features.EnableFeatures({BaseFeatures.COLLECT_ANDROID_FRAME_TIMELINE_METRICS})
+    public void testCreateWithMetricsCollectionAndGcOneTime() throws Throwable {
+        testCreateAndGcOneTime();
+    }
+
+    @Test
+    @DisableHardwareAcceleration
+    @SmallTest
+    @Feature({"AndroidWebView"})
     public void testCreateAndGcOneTime() throws Throwable {
         runAwContentsGcTest(
                 () -> {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwCrashyClassUtilsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwCrashyClassUtilsTest.java
new file mode 100644
index 0000000..d5054de7
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwCrashyClassUtilsTest.java
@@ -0,0 +1,102 @@
+// 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.android_webview.test;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+import org.chromium.android_webview.AwCrashyClassUtils;
+import org.chromium.android_webview.common.AwFeatures;
+import org.chromium.android_webview.common.AwSwitches;
+import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Features;
+
+/**
+ * Tests that WebView only enables test crashes under the right conditions when the correct flags
+ * are flipped.
+ */
+@Batch(Batch.PER_CLASS)
+@RunWith(Parameterized.class)
+@UseParametersRunnerFactory(AwJUnit4ClassRunnerWithParameters.Factory.class)
+public class AwCrashyClassUtilsTest extends AwParameterizedTest {
+
+    @Rule public AwActivityTestRule mRule;
+    @Rule public TestRule mProcessor = new Features.InstrumentationProcessor();
+
+    public AwCrashyClassUtilsTest(AwSettingsMutation param) {
+        this.mRule =
+                new AwActivityTestRule(param.getMutation()) {
+                    @Override
+                    public boolean needsBrowserProcessStarted() {
+                        return false;
+                    }
+                };
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mRule.startBrowserProcess();
+    }
+
+    @Test(expected = RuntimeException.class)
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @CommandLineFlags.Add({AwSwitches.WEBVIEW_FORCE_CRASH_JAVA})
+    public void testJavaCrashWhenEnabled() {
+        Assert.assertTrue(AwCrashyClassUtils.shouldCrashJava());
+        AwCrashyClassUtils.maybeCrashIfEnabled();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @Features.DisableFeatures({AwFeatures.WEBVIEW_ENABLE_CRASH})
+    @CommandLineFlags.Add(AwSwitches.WEBVIEW_FORCE_CRASH_JAVA)
+    public void testNoJavaCrashWhenEnabledAndExperimentDisabled() {
+        Assert.assertFalse(AwCrashyClassUtils.shouldCrashJava());
+        Assert.assertFalse(AwCrashyClassUtils.shouldCrashNative());
+        AwCrashyClassUtils.maybeCrashIfEnabled();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @Features.DisableFeatures({AwFeatures.WEBVIEW_ENABLE_CRASH})
+    @CommandLineFlags.Add(AwSwitches.WEBVIEW_FORCE_CRASH_NATIVE)
+    public void testNoNativeCrashWhenEnabledAndExperimentDisabled() {
+        Assert.assertFalse(AwCrashyClassUtils.shouldCrashJava());
+        Assert.assertFalse(AwCrashyClassUtils.shouldCrashNative());
+        AwCrashyClassUtils.maybeCrashIfEnabled();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @Features.DisableFeatures({AwFeatures.WEBVIEW_ENABLE_CRASH})
+    public void testNoCrashWhenCompletelyDisabled() {
+        Assert.assertFalse(AwCrashyClassUtils.shouldCrashJava());
+        Assert.assertFalse(AwCrashyClassUtils.shouldCrashNative());
+        AwCrashyClassUtils.maybeCrashIfEnabled();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testNoCrashWhenDisabledAndTestExperimentEnabled() {
+        Assert.assertFalse(AwCrashyClassUtils.shouldCrashJava());
+        Assert.assertFalse(AwCrashyClassUtils.shouldCrashNative());
+        AwCrashyClassUtils.maybeCrashIfEnabled();
+    }
+}
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index 76ff3c3..58937692 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -326,6 +326,7 @@
     "../javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwContentsStaticsTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwContentsTest.java",
+    "../javatests/src/org/chromium/android_webview/test/AwCrashyClassUtilsTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwDarkModeTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwDisplayCutoutTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwFileChooserTest.java",
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 5a9beaa2..55aa8a9 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -4353,6 +4353,7 @@
     "//ui/message_center",
     "//ui/message_center:test_support",
     "//ui/message_center/public/cpp",
+    "//ui/native_theme",
     "//ui/ozone",
     "//ui/ozone/testhelpers:mock_gesture_properties_service",
     "//ui/platform_window/stub",
@@ -4826,6 +4827,8 @@
     "wm/window_preview_view_test_api.h",
     "wm/window_restore/pine_test_api.cc",
     "wm/window_restore/pine_test_api.h",
+    "wm/window_restore/pine_test_base.cc",
+    "wm/window_restore/pine_test_base.h",
     "wm/workspace_controller_test_api.cc",
     "wm/workspace_controller_test_api.h",
   ]
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index d450cc3..be87399 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -2488,9 +2488,17 @@
   if (!::features::IsAccessibilityCaretBlinkIntervalSettingEnabled()) {
     return;
   }
-  // int caret_blink_interval =
-  //     active_user_prefs_->GetInteger(prefs::kAccessibilityCaretBlinkInterval);
-  // TODO(b:259374492): Update the native theme with the caret_blink_interval.
+  base::TimeDelta caret_blink_interval = base::Milliseconds(
+      active_user_prefs_->GetInteger(prefs::kAccessibilityCaretBlinkInterval));
+  auto* native_theme_dark = ui::NativeTheme::GetInstanceForDarkUI();
+  native_theme_dark->set_caret_blink_interval(caret_blink_interval);
+  native_theme_dark->NotifyOnNativeThemeUpdated();
+  auto* native_theme_web = ui::NativeTheme::GetInstanceForWeb();
+  native_theme_web->set_caret_blink_interval(caret_blink_interval);
+  native_theme_web->NotifyOnNativeThemeUpdated();
+  auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+  native_theme->set_caret_blink_interval(caret_blink_interval);
+  native_theme->NotifyOnNativeThemeUpdated();
 }
 
 void AccessibilityController::UpdateAccessibilityHighlightingFromPrefs() {
diff --git a/ash/accessibility/accessibility_controller_unittest.cc b/ash/accessibility/accessibility_controller_unittest.cc
index 813a6656..cfa4395 100644
--- a/ash/accessibility/accessibility_controller_unittest.cc
+++ b/ash/accessibility/accessibility_controller_unittest.cc
@@ -25,6 +25,7 @@
 #include "base/functional/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "components/live_caption/pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -53,6 +54,43 @@
   int status_changed_count_ = 0;
 };
 
+class AccessibilityControllerDefaultCaretIntervalTest : public AshTestBase {
+ protected:
+  AccessibilityControllerDefaultCaretIntervalTest() = default;
+  AccessibilityControllerDefaultCaretIntervalTest(
+      const AccessibilityControllerDefaultCaretIntervalTest&) = delete;
+  AccessibilityControllerDefaultCaretIntervalTest& operator=(
+      const AccessibilityControllerDefaultCaretIntervalTest&) = delete;
+  ~AccessibilityControllerDefaultCaretIntervalTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndDisableFeature(
+        ::features::kAccessibilityCaretBlinkIntervalSetting);
+    AshTestBase::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(AccessibilityControllerDefaultCaretIntervalTest,
+       DefaultCaretBlinkInterval) {
+  PrefService* prefs =
+      Shell::Get()->session_controller()->GetLastActiveUserPrefService();
+  // The pref is not registered.
+  EXPECT_FALSE(prefs->FindPreference(prefs::kAccessibilityCaretBlinkInterval));
+
+  auto* native_theme_dark = ui::NativeTheme::GetInstanceForDarkUI();
+  auto* native_theme_web = ui::NativeTheme::GetInstanceForWeb();
+  auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+
+  // All NativeThemes use the default value.
+  base::TimeDelta default_interval = base::Milliseconds(500);
+  EXPECT_EQ(default_interval, native_theme_dark->GetCaretBlinkInterval());
+  EXPECT_EQ(default_interval, native_theme_web->GetCaretBlinkInterval());
+  EXPECT_EQ(default_interval, native_theme->GetCaretBlinkInterval());
+}
+
 class AccessibilityControllerTest : public AshTestBase {
  protected:
   AccessibilityControllerTest() = default;
@@ -1566,6 +1604,30 @@
   ASSERT_FALSE(controller->IsDictationKeyboardDialogShowingForTesting());
 }
 
+TEST_F(AccessibilityControllerTest, ChangingPrefChangesCaretBlinkInterval) {
+  PrefService* prefs =
+      Shell::Get()->session_controller()->GetLastActiveUserPrefService();
+
+  // Starts with default value.
+  EXPECT_EQ(prefs->GetInteger(prefs::kAccessibilityCaretBlinkInterval), 500);
+
+  auto* native_theme_dark = ui::NativeTheme::GetInstanceForDarkUI();
+  auto* native_theme_web = ui::NativeTheme::GetInstanceForWeb();
+  auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+
+  base::TimeDelta expected_interval = base::Milliseconds(500);
+  EXPECT_EQ(expected_interval, native_theme_dark->GetCaretBlinkInterval());
+  EXPECT_EQ(expected_interval, native_theme_web->GetCaretBlinkInterval());
+  EXPECT_EQ(expected_interval, native_theme->GetCaretBlinkInterval());
+
+  // Native Themes should be updated.
+  prefs->SetInteger(prefs::kAccessibilityCaretBlinkInterval, 42);
+  expected_interval = base::Milliseconds(42);
+  EXPECT_EQ(expected_interval, native_theme_dark->GetCaretBlinkInterval());
+  EXPECT_EQ(expected_interval, native_theme_web->GetCaretBlinkInterval());
+  EXPECT_EQ(expected_interval, native_theme->GetCaretBlinkInterval());
+}
+
 class AccessibilityControllerDictationKeyboardImprovementsTest
     : public AshTestBase {
  protected:
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 31198290..1e39fe34 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -3292,13 +3292,13 @@
         Keep on
       </message>
       <message name="IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_ONE_DEVICE" desc="Bluetooth pairing message shown to indicate a Bluetooth device will disconnect when Bluetooth is turned off.">
-        When you turn off Bluetooth, "<ph name="DEVICE_NAME">$1<ex>Dell RGB Keyboard</ex></ph>" will disconnect from your Chromebook.
+        When you turn off Bluetooth, this external device will disconnect from your <ph name="DEVICE_TYPE">$1<ex>Chromebox</ex></ph>:
       </message>
       <message name="IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_TWO_DEVICES" desc="Bluetooth pairing message shown to indicate that two Bluetooth devices will disconnect when Bluetooth is turned off.">
-        When you turn off Bluetooth, "<ph name="DEVICE_NAME_1">$1<ex>Logitech SmartMouse</ex></ph>" and "<ph name="DEVICE_NAME_2">$2<ex>Dell RGB Keyboard</ex></ph>" will disconnect from your Chromebook.
+        When you turn off Bluetooth, these external devices will disconnect from your <ph name="DEVICE_TYPE">$1<ex>Chromebox</ex></ph>:
       </message>
       <message name="IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_MULTIPLE_DEVICES" desc="Bluetooth pairing message shown to indicate that multiple Bluetooth devices will disconnect when Bluetooth is turned off.">
-        When you turn off Bluetooth, "<ph name="DEVICE_NAME_1">$1<ex>Logitech SmartMouse</ex></ph>", "<ph name="DEVICE_NAME_2">$2<ex>Dell RGB Keyboard</ex></ph>" and <ph name="DEVICE_COUNT">$3<ex>3</ex></ph> other devices will disconnect from your Chromebook.
+        When you turn off Bluetooth, <ph name="DEVICE_COUNT">$1<ex>2</ex></ph> external devices will disconnect from your <ph name="DEVICE_TYPE">$2<ex>Chromebox</ex></ph>, including:
       </message>
 
       <!-- Ash multi-user warning dialog -->
@@ -7882,6 +7882,9 @@
       <message name="IDS_ASH_BIRCH_CALENDAR_JOIN_BUTTON" translateable="false" desc="Button label for joining a meeting associated with a calendar event">
         Join
       </message>
+      <message name="IDS_ASH_BIRCH_RECENT_TAB_SUBTITLE" translateable="false" desc="Subtitle for the suggestion chip with recent tabs from other devices">
+        From <ph name="SESSION_NAME">$1<ex>Chromebook</ex></ph>
+      </message>
      </messages>
     </release>
 </grit>
diff --git a/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_MULTIPLE_DEVICES.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_MULTIPLE_DEVICES.png.sha1
index 9efcf23..74fdf1fe 100644
--- a/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_MULTIPLE_DEVICES.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_MULTIPLE_DEVICES.png.sha1
@@ -1 +1 @@
-533b9663aa5f0e762b7d5d669352fadd6e3554c8
\ No newline at end of file
+a01afd1a94bdf20c0410c8d55d60d894d4669f02
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_ONE_DEVICE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_ONE_DEVICE.png.sha1
index 9efcf23..38842a07 100644
--- a/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_ONE_DEVICE.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_ONE_DEVICE.png.sha1
@@ -1 +1 @@
-533b9663aa5f0e762b7d5d669352fadd6e3554c8
\ No newline at end of file
+639cd38d8617e79435f3c3fab7c0eac8ccc1ca98
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_TWO_DEVICES.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_TWO_DEVICES.png.sha1
index 9efcf23..e61588d 100644
--- a/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_TWO_DEVICES.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_TWO_DEVICES.png.sha1
@@ -1 +1 @@
-533b9663aa5f0e762b7d5d669352fadd6e3554c8
\ No newline at end of file
+2fe6e9fae2f5dffe4ad05c517d0ffd64c356aca7
\ No newline at end of file
diff --git a/ash/birch/birch_item.cc b/ash/birch/birch_item.cc
index 6ac9bf3..27b362c2 100644
--- a/ash/birch/birch_item.cc
+++ b/ash/birch/birch_item.cc
@@ -10,6 +10,7 @@
 
 #include "ash/public/cpp/image_downloader.h"
 #include "ash/public/cpp/new_window_delegate.h"
+#include "ash/public/cpp/style/dark_light_mode_controller.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -19,6 +20,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/ui/base/file_icon_util.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
 
@@ -253,8 +255,10 @@
 }
 
 void BirchFileItem::LoadIcon(LoadIconCallback callback) const {
-  std::move(callback).Run(
-      ui::ImageModel::FromVectorIcon(chromeos::GetIconForPath(file_path_)));
+  const gfx::VectorIcon& icon = chromeos::GetIconForPath(file_path_);
+  bool dark_mode = DarkLightModeController::Get()->IsDarkModeEnabled();
+  SkColor color = chromeos::GetIconColorForPath(file_path_, dark_mode);
+  std::move(callback).Run(ui::ImageModel::FromVectorIcon(icon, color));
 }
 
 // static
@@ -320,7 +324,7 @@
                            const GURL& favicon_url,
                            const std::string& session_name,
                            const DeviceFormFactor& form_factor)
-    : BirchItem(title, base::UTF8ToUTF16(session_name)),
+    : BirchItem(title, GetSubtitle(session_name)),
       url_(url),
       timestamp_(timestamp),
       favicon_url_(favicon_url),
@@ -369,6 +373,15 @@
   DownloadImageFromUrl(favicon_url_, std::move(callback));
 }
 
+// static
+std::u16string BirchTabItem::GetSubtitle(const std::string& session_name) {
+  // Builds a string like "From Chromebook".
+  return l10n_util::GetStringFUTF16(IDS_ASH_BIRCH_RECENT_TAB_SUBTITLE,
+                                    base::UTF8ToUTF16(session_name));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 BirchReleaseNotesItem::BirchReleaseNotesItem(
     const std::u16string& release_notes_title,
     const std::u16string& release_notes_text,
diff --git a/ash/birch/birch_item.h b/ash/birch/birch_item.h
index f17d881..0c3a98c 100644
--- a/ash/birch/birch_item.h
+++ b/ash/birch/birch_item.h
@@ -214,6 +214,8 @@
   DeviceFormFactor form_factor() const { return form_factor_; }
 
  private:
+  static std::u16string GetSubtitle(const std::string& session_name);
+
   GURL url_;
   base::Time timestamp_;
   GURL favicon_url_;
diff --git a/ash/birch/birch_item_unittest.cc b/ash/birch/birch_item_unittest.cc
index 1e0c650..faeaadea 100644
--- a/ash/birch/birch_item_unittest.cc
+++ b/ash/birch/birch_item_unittest.cc
@@ -14,7 +14,11 @@
 #include "base/memory/raw_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/models/image_model.h"
+#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
+#include "ui/color/color_provider.h"
+#include "ui/color/color_provider_manager.h"
 #include "ui/gfx/image/image_unittest_util.h"
+#include "ui/native_theme/native_theme.h"
 
 namespace ash {
 namespace {
@@ -136,6 +140,14 @@
             GURL("https://google.com/search?q=weather"));
 }
 
+TEST_F(BirchItemTest, Tab_SubtitleHasSessionName) {
+  BirchTabItem item(u"item", /*url=*/GURL("http://example.com/"),
+                    /*timestamp=*/base::Time(),
+                    /*favicon_url=*/GURL(), /*session_name=*/"Chromebook",
+                    BirchTabItem::DeviceFormFactor::kDesktop);
+  EXPECT_EQ(item.subtitle(), u"From Chromebook");
+}
+
 TEST_F(BirchItemTest, Tab_PerformAction_ValidUrl) {
   BirchTabItem item(u"item", /*url=*/GURL("http://example.com/"),
                     /*timestamp=*/base::Time(),
@@ -235,8 +247,17 @@
   const base::FilePath excel_path("/my/test/mySheet.xlsx");
   BirchFileItem item(excel_path, u"suggested", base::Time());
 
-  item.LoadIcon(base::BindOnce(
-      [](const ui::ImageModel& icon) { EXPECT_FALSE(icon.IsEmpty()); }));
+  item.LoadIcon(base::BindOnce([](const ui::ImageModel& icon) {
+    // Icon was set.
+    EXPECT_FALSE(icon.IsEmpty());
+
+    // Color is the one used for MS Excel documents.
+    auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+    auto* color_provider = ui::ColorProviderManager::Get().GetColorProviderFor(
+        native_theme->GetColorProviderKey(nullptr));
+    EXPECT_EQ(icon.GetVectorIcon().color(),
+              color_provider->GetColor(cros_tokens::kCrosSysFileMsExcel));
+  }));
 }
 
 }  // namespace
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 43f5fcfb..8dbeaba 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -989,12 +989,6 @@
              "ExtendedUpdatesOptInFeature",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Requires user opt-in to receive extended updates support.
-// This is only enabled for applicable devices.
-BASE_FEATURE(kExtendedUpdatesRequireOptIn,
-             "ExtendedUpdatesRequireOptIn",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Enables policy that controls feature to allow Family Link accounts on school
 // owned devices.
 BASE_FEATURE(kFamilyLinkOnSchoolDevice,
@@ -3399,10 +3393,8 @@
   return base::FeatureList::IsEnabled(kExperimentalRgbKeyboardPatterns);
 }
 
-bool IsExtendedUpdatesRequireOptInEnabled() {
-  // Boolean order matters due to how finch experiments are recorded.
-  return base::FeatureList::IsEnabled(kExtendedUpdatesRequireOptIn) &&
-         base::FeatureList::IsEnabled(kExtendedUpdatesOptInFeature);
+bool IsExtendedUpdatesOptInFeatureEnabled() {
+  return base::FeatureList::IsEnabled(kExtendedUpdatesOptInFeature);
 }
 
 bool IsExternalKeyboardInDiagnosticsAppEnabled() {
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index a5135228..1be8e81 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -297,8 +297,6 @@
 BASE_DECLARE_FEATURE(kExperimentalRgbKeyboardPatterns);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kExtendedUpdatesOptInFeature);
-COMPONENT_EXPORT(ASH_CONSTANTS)
-BASE_DECLARE_FEATURE(kExtendedUpdatesRequireOptIn);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kFamilyLinkOnSchoolDevice);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kFastPair);
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -987,7 +985,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsExperimentalRgbKeyboardPatternsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
-bool IsExtendedUpdatesRequireOptInEnabled();
+bool IsExtendedUpdatesOptInFeatureEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsExternalKeyboardInDiagnosticsAppEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFamilyLinkOnSchoolDeviceEnabled();
diff --git a/ash/constants/devicetype.cc b/ash/constants/devicetype.cc
index 18a7af5b..0d64ddb 100644
--- a/ash/constants/devicetype.cc
+++ b/ash/constants/devicetype.cc
@@ -14,30 +14,35 @@
 #include "chromeos/constants/devicetype.h"
 
 namespace ash {
+namespace {
+constexpr char kDefaultDeviceTypeName[] = "Chromebook";
+}  // namespace
 
 std::string GetDeviceBluetoothName(const std::string& bluetooth_address) {
-  const char* name = "Chromebook";
-  switch (chromeos::GetDeviceType()) {
-    case chromeos::DeviceType::kChromebase:
-      name = "Chromebase";
-      break;
-    case chromeos::DeviceType::kChromebit:
-      name = "Chromebit";
-      break;
-    case chromeos::DeviceType::kChromebook:
-      name = "Chromebook";
-      break;
-    case chromeos::DeviceType::kChromebox:
-      name = "Chromebox";
-      break;
-    case chromeos::DeviceType::kUnknown:
-    default:
-      break;
-  }
+  const std::string device_name = DeviceTypeToString(chromeos::GetDeviceType());
+
   // Take the lower 2 bytes of hashed |bluetooth_address| and combine it with
   // the device type to create a more identifiable device name.
-  return base::StringPrintf("%s_%04X", name,
-                            base::PersistentHash(bluetooth_address) & 0xFFFF);
+  return base::StringPrintf(
+      "%s_%04X",
+      device_name.empty() ? kDefaultDeviceTypeName : device_name.c_str(),
+      base::PersistentHash(bluetooth_address) & 0xFFFF);
+}
+
+std::string DeviceTypeToString(chromeos::DeviceType device_type) {
+  switch (device_type) {
+    case chromeos::DeviceType::kChromebase:
+      return "Chromebase";
+    case chromeos::DeviceType::kChromebit:
+      return "Chromebit";
+    case chromeos::DeviceType::kChromebook:
+      return "Chromebook";
+    case chromeos::DeviceType::kChromebox:
+      return "Chromebox";
+    case chromeos::DeviceType::kUnknown:
+    default:
+      return "";
+  }
 }
 
 bool IsGoogleBrandedDevice() {
diff --git a/ash/constants/devicetype.h b/ash/constants/devicetype.h
index 7dc5d8d..6db326c 100644
--- a/ash/constants/devicetype.h
+++ b/ash/constants/devicetype.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/component_export.h"
+#include "chromeos/constants/devicetype.h"
 
 namespace ash {
 
@@ -17,6 +18,10 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 std::string GetDeviceBluetoothName(const std::string& bluetooth_address);
 
+// Returns the name of the provided Chrome device type.
+COMPONENT_EXPORT(ASH_CONSTANTS)
+std::string DeviceTypeToString(chromeos::DeviceType device_type);
+
 // Returns true if the device is Google branded.
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsGoogleBrandedDevice();
 
diff --git a/ash/game_dashboard/game_dashboard_button_reveal_controller.cc b/ash/game_dashboard/game_dashboard_button_reveal_controller.cc
index a6ab639..1eaa3e0 100644
--- a/ash/game_dashboard/game_dashboard_button_reveal_controller.cc
+++ b/ash/game_dashboard/game_dashboard_button_reveal_controller.cc
@@ -7,10 +7,13 @@
 #include "ash/game_dashboard/game_dashboard_context.h"
 #include "ash/game_dashboard/game_dashboard_utils.h"
 #include "ash/wm/window_state.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_forward.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "ui/aura/window.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/geometry/point.h"
+#include "ui/views/animation/animation_builder.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
@@ -21,6 +24,9 @@
 // game dashboard button revealing.
 constexpr base::TimeDelta kMouseRevealDelay = base::Milliseconds(200);
 
+constexpr base::TimeDelta kSlideAnimationDuration = base::Milliseconds(200);
+constexpr base::TimeDelta kNoSlideAnimationDuration = base::Milliseconds(0);
+
 }  // namespace
 
 GameDashboardButtonRevealController::GameDashboardButtonRevealController(
@@ -29,12 +35,38 @@
   DCHECK(context_);
   context_->game_window()->AddPreTargetHandler(
       this, ui::EventTarget::Priority::kSystem);
+  UpdateVisibility(/*target_visibility=*/false, /*animate=*/false);
 }
 
 GameDashboardButtonRevealController::~GameDashboardButtonRevealController() {
+  UpdateVisibility(/*target_visibility=*/true, /*animate=*/false);
   context_->game_window()->RemovePreTargetHandler(this);
 }
 
+void GameDashboardButtonRevealController::UpdateVisibility(
+    bool target_visibility,
+    bool animate) {
+  context_->SetGameDashboardButtonVisibility(/*visible=*/true);
+  views::AnimationBuilder()
+      .SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
+                                 IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
+      .OnEnded(
+          base::BindOnce(&GameDashboardButtonRevealController::OnAnimationEnd,
+                         weak_ptr_factory_.GetWeakPtr(), target_visibility))
+      .Once()
+      .SetDuration(animate ? kSlideAnimationDuration
+                           : kNoSlideAnimationDuration)
+      .SetTransform(
+          context_->game_dashboard_button_widget()->GetLayer(),
+          target_visibility
+              ? gfx::Transform()
+              : gfx::Transform::MakeTranslation(
+                    /*tx=*/0,
+                    /*ty=*/-game_dashboard_utils::GetFrameHeaderHeight(
+                        context_->game_window())),
+          gfx::Tween::EASE_OUT);
+}
+
 void GameDashboardButtonRevealController::OnMouseEvent(ui::MouseEvent* event) {
   const auto event_type = event->type();
   if (event_type != ui::ET_MOUSE_MOVED && event_type != ui::ET_MOUSE_RELEASED &&
@@ -59,7 +91,7 @@
   top_edge_hover_timer_.Stop();
   // If the main menu is closed, try to hide the game dashboard button.
   if (CanHideGameDashboardButton(mouse_screen_location)) {
-    context_->SetGameDashboardButtonVisibility(/*visible=*/false);
+    UpdateVisibility(/*target_visibility=*/false, /*animate=*/true);
   }
 }
 
@@ -103,7 +135,16 @@
 void GameDashboardButtonRevealController::OnTopEdgeHoverTimeout() {
   if (CanShowGameDashboardButton(
           display::Screen::GetScreen()->GetCursorScreenPoint())) {
-    context_->SetGameDashboardButtonVisibility(/*visible=*/true);
+    UpdateVisibility(/*target_visibility=*/true, /*animate=*/true);
+  }
+}
+
+void GameDashboardButtonRevealController::OnAnimationEnd(
+    bool target_visibility) {
+  if (!target_visibility) {
+    // The slide up animation has ended. Make the Game Dashboard button
+    // widget not visible.
+    context_->SetGameDashboardButtonVisibility(/*visible=*/false);
   }
 }
 
diff --git a/ash/game_dashboard/game_dashboard_button_reveal_controller.h b/ash/game_dashboard/game_dashboard_button_reveal_controller.h
index 4e7f8a44..6721eb7 100644
--- a/ash/game_dashboard/game_dashboard_button_reveal_controller.h
+++ b/ash/game_dashboard/game_dashboard_button_reveal_controller.h
@@ -30,6 +30,13 @@
 
   void StopTopEdgeTimer() { top_edge_hover_timer_.Stop(); }
 
+  // Updates the visibility of the Game Dashboard button widget. If
+  // `target_visibility` is true, then the widget will be visible, otherwise it
+  // will not be visible. If `animate` is true, the widget will slide up/down
+  // with a short animation to `target_visibility`, otherwise, the widget will
+  // move without any animation.
+  void UpdateVisibility(bool target_visibility, bool animate);
+
   // ui::EventHandler:
   void OnMouseEvent(ui::MouseEvent* event) override;
 
@@ -59,10 +66,16 @@
   // Called when the `top_edge_hover_timer_` is fired.
   void OnTopEdgeHoverTimeout();
 
+  // Callbacks when the animation has ended in `UpdateVisibility()`.
+  void OnAnimationEnd(bool target_visibility);
+
   const raw_ptr<GameDashboardContext> context_;
 
   // Timer to track cursor being held at the top edge of the screen.
   base::OneShotTimer top_edge_hover_timer_;
+
+  base::WeakPtrFactory<GameDashboardButtonRevealController> weak_ptr_factory_{
+      this};
 };
 
 }  // namespace ash
diff --git a/ash/game_dashboard/game_dashboard_context.cc b/ash/game_dashboard/game_dashboard_context.cc
index ed198ac..f6619077 100644
--- a/ash/game_dashboard/game_dashboard_context.cc
+++ b/ash/game_dashboard/game_dashboard_context.cc
@@ -205,7 +205,14 @@
 }
 
 void GameDashboardContext::ToggleMainMenuByAccelerator() {
-  SetGameDashboardButtonVisibility(/*visible=*/true);
+  if (game_dashboard_button_reveal_controller_) {
+    // Window is in fullscreen, and `game_dashboard_button_widget_` may not be
+    // visible. Reset its position and make it visible. Don't animate the button
+    // so it and the main menu show up at the same time.
+    game_dashboard_button_reveal_controller_->UpdateVisibility(
+        /*target_visibility=*/true, /*animate=*/false);
+  }
+
   ToggleMainMenu(GameDashboardMainMenuToggleMethod::kSearchPlusG);
 }
 
@@ -411,7 +418,9 @@
     chromeos::WindowStateType old_type) {
   // Hide the Game Dashboard button before the window switches to fullscreen.
   if (window_state->IsFullscreen()) {
-    SetGameDashboardButtonVisibility(/*visible=*/false);
+    DCHECK(!game_dashboard_button_reveal_controller_);
+    // The `GameDashboardButtonRevealController`'s ctor will hide
+    // `game_dashboard_button_widget_`.
     game_dashboard_button_reveal_controller_ =
         std::make_unique<GameDashboardButtonRevealController>(this);
   }
@@ -420,9 +429,11 @@
 void GameDashboardContext::OnPostWindowStateTypeChange(
     WindowState* window_state,
     chromeos::WindowStateType old_type) {
-  if (!window_state->IsFullscreen()) {
+  if (!window_state->IsFullscreen() &&
+      game_dashboard_button_reveal_controller_) {
+    // When exiting fullscreen, GameDashboardButtonRevealController dtor will
+    // make `game_dashboard_button_widget_` visible and reset its position.
     game_dashboard_button_reveal_controller_.reset();
-    SetGameDashboardButtonVisibility(/*visible=*/true);
   }
 }
 
@@ -636,12 +647,12 @@
   if (welcome_dialog_widget_) {
     welcome_dialog_widget_->RemoveObserver(this);
     welcome_dialog_widget_.reset();
+    MaybeShowToolbar();
   }
 }
 
 void GameDashboardContext::OnWelcomeDialogTimerCompleted() {
   CloseWelcomeDialogIfAny();
-  MaybeShowToolbar();
 }
 
 void GameDashboardContext::UpdateOnMainMenuClosed() {
diff --git a/ash/game_dashboard/game_dashboard_context_unittest.cc b/ash/game_dashboard/game_dashboard_context_unittest.cc
index c571b2a..81e683c 100644
--- a/ash/game_dashboard/game_dashboard_context_unittest.cc
+++ b/ash/game_dashboard/game_dashboard_context_unittest.cc
@@ -1306,6 +1306,7 @@
   auto* event_generator = GetEventGenerator();
   auto app_bounds = game_window_->GetBoundsInScreen();
   auto* window_state = WindowState::Get(game_window_.get());
+  ASSERT_TRUE(window_state->IsNormalStateType());
   views::Widget* button_widget = test_api_->GetGameDashboardButtonWidget();
   CHECK(button_widget);
 
@@ -1316,6 +1317,7 @@
   ASSERT_TRUE(window_state->IsFullscreen());
   ASSERT_FALSE(button_widget->IsVisible());
   ASSERT_TRUE(test_api_->GetGameDashboardButtonRevealController());
+  ASSERT_FALSE(test_api_->GetGameDashboardButtonWidget()->IsVisible());
 
   // Move mouse to top edge of window.
   event_generator->MoveMouseTo(app_bounds.top_center());
@@ -1324,13 +1326,40 @@
   ASSERT_TRUE(top_edge_hover_timer.IsRunning());
   top_edge_hover_timer.FireNow();
   ASSERT_TRUE(button_widget->IsVisible());
+  ASSERT_TRUE(test_api_->GetGameDashboardButtonWidget()->IsVisible());
 
   // Move mouse to the center of the app, and verify Game Dashboard button
   // widget is not visible.
   event_generator->MoveMouseTo(app_bounds.CenterPoint());
+  ASSERT_FALSE(test_api_->GetGameDashboardButtonWidget()->IsVisible());
   ASSERT_FALSE(button_widget->IsVisible());
 }
 
+// Verifies that at startup and with the welcome dialog is visible, opening
+// the main menu dismisses the welcome dialog and shows the toolbar.
+TEST_F(GameDashboardContextTest, MainMenuAndToolbarAndWelcomeDialogStartup) {
+  SetShowWelcomeDialog(true);
+  SetShowToolbar(true);
+  CreateGameWindow(/*is_arc_window=*/true);
+
+  // Verify the welcome dialog is visible and the toolbar is not visible.
+  ASSERT_TRUE(test_api_->GetWelcomeDialogWidget());
+  ASSERT_FALSE(test_api_->GetToolbarWidget());
+
+  // Advance by 1 second and verify the widgets visibility has not changed.
+  task_environment()->FastForwardBy(base::Seconds(1));
+  ASSERT_TRUE(test_api_->GetWelcomeDialogWidget());
+  ASSERT_FALSE(test_api_->GetToolbarWidget());
+
+  // Click on the Game Dashboard button to open the main menu.
+  test_api_->OpenTheMainMenu();
+
+  // Verify the welcome dialog is no longer visible, and the toolbar is now
+  // visible.
+  ASSERT_FALSE(test_api_->GetWelcomeDialogWidget());
+  ASSERT_TRUE(test_api_->GetToolbarWidget());
+}
+
 // -----------------------------------------------------------------------------
 // GameTypeGameDashboardContextTest:
 // Test fixture to test both ARC and GeForceNow game window depending on the
diff --git a/ash/glanceables/glanceables_metrics.cc b/ash/glanceables/glanceables_metrics.cc
index f814fd6..e2ba4277 100644
--- a/ash/glanceables/glanceables_metrics.cc
+++ b/ash/glanceables/glanceables_metrics.cc
@@ -174,6 +174,18 @@
   base::UmaHistogramMediumTimes(kTotalShowTimeHistogram, total_show_time);
 }
 
+void RecordTotalShowTimeForClassroom(base::TimeDelta total_show_time) {
+  base::UmaHistogramMediumTimes(
+      base::StrCat({kTimeManagementClassroomPrefix, ".TotalShowTime"}),
+      total_show_time);
+}
+
+void RecordTotalShowTimeForTasks(base::TimeDelta total_show_time) {
+  base::UmaHistogramMediumTimes(
+      base::StrCat({kTimeManagementTaskPrefix, ".TotalShowTime"}),
+      total_show_time);
+}
+
 void RecordClassromInitialLoadTime(bool first_occurrence,
                                    base::TimeDelta load_time) {
   std::string histogram_name =
diff --git a/ash/glanceables/glanceables_metrics.h b/ash/glanceables/glanceables_metrics.h
index 3a544ab..ffd2220 100644
--- a/ash/glanceables/glanceables_metrics.h
+++ b/ash/glanceables/glanceables_metrics.h
@@ -80,6 +80,10 @@
 
 void RecordTotalShowTime(base::TimeDelta total_show_time);
 
+void RecordTotalShowTimeForClassroom(base::TimeDelta total_show_time);
+
+void RecordTotalShowTimeForTasks(base::TimeDelta total_show_time);
+
 void RecordClassromInitialLoadTime(bool first_occurrence,
                                    base::TimeDelta load_time);
 
diff --git a/ash/glanceables/tasks/glanceables_tasks_view.cc b/ash/glanceables/tasks/glanceables_tasks_view.cc
index 2f3628c..a48b296 100644
--- a/ash/glanceables/tasks/glanceables_tasks_view.cc
+++ b/ash/glanceables/tasks/glanceables_tasks_view.cc
@@ -171,7 +171,12 @@
 
 GlanceablesTasksViewBase::GlanceablesTasksViewBase(
     bool use_glanceables_container_style)
-    : GlanceableTrayChildBubble(use_glanceables_container_style) {}
+    : GlanceableTrayChildBubble(use_glanceables_container_style),
+      shown_time_(base::Time::Now()) {}
+
+GlanceablesTasksViewBase::~GlanceablesTasksViewBase() {
+  RecordTotalShowTimeForTasks(base::Time::Now() - shown_time_);
+}
 
 BEGIN_METADATA(GlanceablesTasksViewBase)
 END_METADATA
diff --git a/ash/glanceables/tasks/glanceables_tasks_view.h b/ash/glanceables/tasks/glanceables_tasks_view.h
index b84ab5a..1062e21 100644
--- a/ash/glanceables/tasks/glanceables_tasks_view.h
+++ b/ash/glanceables/tasks/glanceables_tasks_view.h
@@ -45,11 +45,15 @@
   explicit GlanceablesTasksViewBase(bool use_glanceables_container_style);
   GlanceablesTasksViewBase(const GlanceablesTasksViewBase&) = delete;
   GlanceablesTasksViewBase& operator=(const GlanceablesTasksViewBase&) = delete;
-  ~GlanceablesTasksViewBase() override = default;
+  ~GlanceablesTasksViewBase() override;
 
   // Invalidates any pending tasks, or tasks lists requests. Called when the
   // glanceables bubble widget starts closing to avoid unnecessary UI updates.
   virtual void CancelUpdates() = 0;
+
+ private:
+  // Time stamp of when the view was created.
+  const base::Time shown_time_;
 };
 
 // Glanceables view responsible for interacting with Google Tasks.
diff --git a/ash/glanceables/tasks/glanceables_tasks_view_unittest.cc b/ash/glanceables/tasks/glanceables_tasks_view_unittest.cc
index 722b962..ab447fe9a 100644
--- a/ash/glanceables/tasks/glanceables_tasks_view_unittest.cc
+++ b/ash/glanceables/tasks/glanceables_tasks_view_unittest.cc
@@ -72,8 +72,7 @@
   void TearDown() override {
     // Destroy `widget_` first, before destroying `LayoutProvider` (needed in
     // the `views::Combobox`'s destruction chain).
-    view_ = nullptr;
-    widget_.reset();
+    CloseWidget();
     AshTestBase::TearDown();
   }
 
@@ -94,6 +93,11 @@
         fake_glanceables_tasks_client_->task_lists()));
   }
 
+  void CloseWidget() {
+    view_ = nullptr;
+    widget_.reset();
+  }
+
   Combobox* GetComboBoxView() const {
     return views::AsViewClass<Combobox>(view_->GetViewByID(
         base::to_underlying(GlanceablesViewId::kTasksBubbleComboBox)));
@@ -162,6 +166,17 @@
   const GlanceablesTestNewWindowDelegate new_window_delegate_;
 };
 
+TEST_F(GlanceablesTasksViewTest, RecordShowTimeHistogramOnClose) {
+  base::HistogramTester histogram_tester;
+  histogram_tester.ExpectTotalCount(
+      "Ash.Glanceables.TimeManagement.Tasks.TotalShowTime", 0);
+
+  CloseWidget();
+
+  histogram_tester.ExpectTotalCount(
+      "Ash.Glanceables.TimeManagement.Tasks.TotalShowTime", 1);
+}
+
 TEST_F(GlanceablesTasksViewTest, ShowsProgressBarWhileLoadingTasks) {
   tasks_client()->set_paused(true);
 
diff --git a/ash/hud_display/hud_display.cc b/ash/hud_display/hud_display.cc
index 85b7208e..f8260586 100644
--- a/ash/hud_display/hud_display.cc
+++ b/ash/hud_display/hud_display.cc
@@ -142,6 +142,7 @@
 
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
   params.delegate = delegate.release();
+  params.name = "HUDDisplay";
   params.parent = Shell::GetContainer(Shell::GetPrimaryRootWindow(),
                                       kShellWindowId_OverlayContainer);
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
diff --git a/ash/picker/search/picker_search_controller.cc b/ash/picker/search/picker_search_controller.cc
index 7ff4d7d..1537f942 100644
--- a/ash/picker/search/picker_search_controller.cc
+++ b/ash/picker/search/picker_search_controller.cc
@@ -146,10 +146,13 @@
 }
 
 void PickerSearchController::ResetResults() {
+  category_results_.clear();
+  suggested_results_.clear();
   omnibox_results_.clear();
   gif_results_.clear();
   emoji_results_.clear();
-  suggested_results_.clear();
+  local_file_results_.clear();
+  drive_file_results_.clear();
 }
 
 void PickerSearchController::PublishBurnInResults() {
diff --git a/ash/public/cpp/resources/ash_public_unscaled_resources.grd b/ash/public/cpp/resources/ash_public_unscaled_resources.grd
index 0cd4a53..222baa1 100644
--- a/ash/public/cpp/resources/ash_public_unscaled_resources.grd
+++ b/ash/public/cpp/resources/ash_public_unscaled_resources.grd
@@ -54,6 +54,9 @@
       <!-- Clipboard Nudge -->
       <structure type="lottie" name="IDR_CLIPBOARD_NUDGE_COPIED_IMAGE" file="unscaled_resources/clipboard_nudge_copied.json" compress="gzip" />
       <structure type="lottie" name="IDR_CLIPBOARD_NUDGE_SELECT_IMAGE" file="unscaled_resources/clipboard_nudge_select.json" compress="gzip" />
+       <!-- Pine Nudge -->
+       <structure type="lottie" name="IDR_PINE_NUDGE_IMAGE_DM" file="unscaled_resources/pine_nudge_dm.json" compress="gzip" />
+       <structure type="lottie" name="IDR_PINE_NUDGE_IMAGE_LM" file="unscaled_resources/pine_nudge_lm.json" compress="gzip" />
     </structures>
   </release>
 </grit>
diff --git a/ash/public/cpp/resources/unscaled_resources/pine_nudge_dm.json b/ash/public/cpp/resources/unscaled_resources/pine_nudge_dm.json
new file mode 100644
index 0000000..59b982ac
--- /dev/null
+++ b/ash/public/cpp/resources/unscaled_resources/pine_nudge_dm.json
@@ -0,0 +1 @@
+{"layers":[{"nm":"surface7934","ty":4,"ind":1211,"ip":0,"op":60,"st":0,"ks":{"a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[133.33,133.33],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}},"ao":0,"shapes":[{"nm":"surface7934","hd":false,"ty":"gr","it":[{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[12.75,33],[32.25,33],[36.75,37.5],[36.75,46.5],[32.25,51],[12.75,51],[8.25,46.5],[8.25,37.5],[12.75,33]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.2549019607843137,0.27450980392156865,0.34901960784313724,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[-18,33],[1.5,33],[6,37.5],[6,46.5],[1.5,51],[-18,51],[-22.5,46.5],[-22.5,37.5],[-18,33]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.2549019607843137,0.27450980392156865,0.34901960784313724,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[43.5,33],[63,33],[67.5,37.5],[67.5,46.5],[63,51],[43.5,51],[39,46.5],[39,37.5],[43.5,33]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.2549019607843137,0.27450980392156865,0.34901960784313724,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[-18,12.75],[1.5,12.75],[6,17.25],[6,26.25],[1.5,30.75],[-18,30.75],[-22.5,26.25],[-22.5,17.25],[-18,12.75]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.2549019607843137,0.27450980392156865,0.34901960784313724,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[43.5,12.75],[63,12.75],[67.5,17.25],[67.5,26.25],[63,30.75],[43.5,30.75],[39,26.25],[39,17.25],[43.5,12.75]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.2549019607843137,0.27450980392156865,0.34901960784313724,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,17.25],[28.5,17.25],[28.5,26.25],[30,26.25]]},"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0,0,0,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[27,17.25],[25.5,17.25],[25.5,26.25],[27,26.25]]},"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0,0,0,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[15,17.25],[24,17.25],[24,26.25],[15,26.25]]},"a":0}},{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[22.5,18.75],[16.5,18.75],[16.5,24.75],[22.5,24.75]]},"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0,0,0,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.69],[0,0],[3.31,0],[0,0],[0,2.69],[0,0],[-3.31,0]],"o":[[0,0],[3.31,0],[0,0],[0,3.31],[0,0],[-3.31,0],[0,0],[0,-3.31],[0,0]],"v":[[17,17],[43,17],[49,23],[49,35],[43,41],[17,41],[11,35],[11,23],[17,17]]},"a":0}},{"nm":"cros.sys.illo.color1","hd":false,"ty":"st","lc":1,"lj":1,"ml":4,"o":{"k":100,"a":0},"w":{"k":1,"a":0},"c":{"k":[0.7058823529411765,0.7725490196078432,1,1],"a":0}},{"nm":"cros.sys.illo.color1","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.7058823529411765,0.7725490196078432,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[75,75],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"rc","p":{"k":[22.5,22.5],"a":0},"s":{"k":[45,45],"a":0},"r":{"k":0,"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0,0,0,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"rc","p":{"k":[22.5,22.5],"a":0},"s":{"k":[45,45],"a":0},"r":{"k":0,"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0,0,0,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]}]}],"v":"5.6.6","fr":60,"ip":0,"op":1,"w":60,"h":60,"meta":{"g":"LF SVG to Lottie"}}
\ No newline at end of file
diff --git a/ash/public/cpp/resources/unscaled_resources/pine_nudge_lm.json b/ash/public/cpp/resources/unscaled_resources/pine_nudge_lm.json
new file mode 100644
index 0000000..93878b4
--- /dev/null
+++ b/ash/public/cpp/resources/unscaled_resources/pine_nudge_lm.json
@@ -0,0 +1 @@
+{"layers":[{"nm":"surface7934","ty":4,"ind":1211,"ip":0,"op":60,"st":0,"ks":{"a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[133.33,133.33],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}},"ao":0,"shapes":[{"nm":"surface7934","hd":false,"ty":"gr","it":[{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[12.75,33],[32.25,33],[36.75,37.5],[36.75,46.5],[32.25,51],[12.75,51],[8.25,46.5],[8.25,37.5],[12.75,33]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.8588235294117647,0.8823529411764706,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[-18,33],[1.5,33],[6,37.5],[6,46.5],[1.5,51],[-18,51],[-22.5,46.5],[-22.5,37.5],[-18,33]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.8588235294117647,0.8823529411764706,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[43.5,33],[63,33],[67.5,37.5],[67.5,46.5],[63,51],[43.5,51],[39,46.5],[39,37.5],[43.5,33]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.8588235294117647,0.8823529411764706,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[-18,12.75],[1.5,12.75],[6,17.25],[6,26.25],[1.5,30.75],[-18,30.75],[-22.5,26.25],[-22.5,17.25],[-18,12.75]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.8588235294117647,0.8823529411764706,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.02],[0,0],[2.48,0],[0,0],[0,2.02],[0,0],[-2.48,0]],"o":[[0,0],[2.48,0],[0,0],[0,2.48],[0,0],[-2.48,0],[0,0],[0,-2.48],[0,0]],"v":[[43.5,12.75],[63,12.75],[67.5,17.25],[67.5,26.25],[63,30.75],[43.5,30.75],[39,26.25],[39,17.25],[43.5,12.75]]},"a":0}},{"nm":"cros.sys.illo.color1.2","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.8588235294117647,0.8823529411764706,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,17.25],[28.5,17.25],[28.5,26.25],[30,26.25]]},"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[1,1,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[27,17.25],[25.5,17.25],[25.5,26.25],[27,26.25]]},"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[1,1,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[15,17.25],[24,17.25],[24,26.25],[15,26.25]]},"a":0}},{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[22.5,18.75],[16.5,18.75],[16.5,24.75],[22.5,24.75]]},"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[1,1,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"sh","ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,-2.69],[0,0],[3.31,0],[0,0],[0,2.69],[0,0],[-3.31,0]],"o":[[0,0],[3.31,0],[0,0],[0,3.31],[0,0],[-3.31,0],[0,0],[0,-3.31],[0,0]],"v":[[17,17],[43,17],[49,23],[49,35],[43,41],[17,41],[11,35],[11,23],[17,17]]},"a":0}},{"nm":"cros.sys.illo.color1","hd":false,"ty":"st","lc":1,"lj":1,"ml":4,"o":{"k":100,"a":0},"w":{"k":1,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"a":0}},{"nm":"cros.sys.illo.color1","ty":"fl","o":{"k":100,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[75,75],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"rc","p":{"k":[22.5,22.5],"a":0},"s":{"k":[45,45],"a":0},"r":{"k":0,"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[1,1,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"gr","it":[{"ty":"rc","p":{"k":[22.5,22.5],"a":0},"s":{"k":[45,45],"a":0},"r":{"k":0,"a":0}},{"nm":"cros.sys.illo.base","ty":"fl","o":{"k":100,"a":0},"c":{"k":[1,1,1,1],"a":0}},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]},{"hd":false,"ty":"tr","a":{"k":[0,0],"a":0},"p":{"k":[0,0],"a":0},"s":{"k":[100,100],"a":0},"r":{"k":0,"a":0},"o":{"k":100,"a":0},"sk":{"k":0,"a":0},"sa":{"k":0,"a":0}}]}]}],"v":"5.6.6","fr":60,"ip":0,"op":1,"w":60,"h":60,"meta":{"g":"LF SVG to Lottie"}}
\ No newline at end of file
diff --git a/ash/public/cpp/test/test_saved_desk_delegate.cc b/ash/public/cpp/test/test_saved_desk_delegate.cc
index d5ee139..83558c6 100644
--- a/ash/public/cpp/test/test_saved_desk_delegate.cc
+++ b/ash/public/cpp/test/test_saved_desk_delegate.cc
@@ -7,7 +7,6 @@
 #include "ash/public/cpp/desk_template.h"
 #include "base/containers/contains.h"
 #include "components/app_restore/app_launch_info.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/color/color_provider.h"
@@ -50,13 +49,7 @@
     uint64_t lacros_profile_id,
     base::OnceCallback<void(const gfx::ImageSkia&)> callback,
     base::CancelableTaskTracker* tracker) const {
-  // TODO(b/329454790): Replace default icon when one is added for Pine, or
-  // revert this to no-op.
-  // Create a placeholder `gfx::ImageSkia` so the image data is not empty.
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(1, 1);
-  bitmap.eraseColor(SK_ColorCYAN);
-  std::move(callback).Run(gfx::ImageSkia::CreateFrom1xBitmap(bitmap));
+  std::move(callback).Run(gfx::ImageSkia());
 }
 
 void TestSavedDeskDelegate::GetIconForAppId(
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index f56be407..f315220 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -100,6 +100,7 @@
     "color_correction.icon",
     "combine_desks.icon",
     "copy.icon",
+    "default_app.icon",
     "delete.icon",
     "desk_bar_shift.icon",
     "desk_button_visibility_off.icon",
diff --git a/ash/resources/vector_icons/default_app.icon b/ash/resources/vector_icons/default_app.icon
new file mode 100644
index 0000000..6afa311
--- /dev/null
+++ b/ash/resources/vector_icons/default_app.icon
@@ -0,0 +1,40 @@
+// 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, 32,
+MOVE_TO, 15.86f, 7.31f,
+R_CUBIC_TO, 0.09f, -0.04f, 0.19f, -0.04f, 0.28f, 0,
+R_LINE_TO, 7.26f, 3.35f,
+R_LINE_TO, -7.4f, 3.7f,
+R_LINE_TO, -7.4f, -3.7f,
+R_LINE_TO, 7.26f, -3.35f,
+CLOSE,
+R_MOVE_TO, 1.14f, 16.92f,
+R_LINE_TO, 7.15f, -3.57f,
+R_ARC_TO, 0.33f, 0.33f, 0, 0, 0, 0.18f, -0.3f,
+R_V_LINE_TO, -7.93f,
+LINE_TO, 17, 16.1f,
+R_V_LINE_TO, 8.14f,
+CLOSE,
+R_MOVE_TO, -2, -8.13f,
+R_V_LINE_TO, 8.14f,
+R_LINE_TO, -7.15f, -3.58f,
+R_ARC_TO, 0.33f, 0.33f, 0, 0, 1, -0.18f, -0.3f,
+R_V_LINE_TO, -7.93f,
+R_LINE_TO, 7.33f, 3.67f,
+CLOSE,
+R_MOVE_TO, 1.98f, -10.6f,
+R_ARC_TO, 2.33f, 2.33f, 0, 0, 0, -1.96f, 0,
+R_LINE_TO, -8, 3.69f,
+R_ARC_TO, 2.33f, 2.33f, 0, 0, 0, -1.36f, 2.12f,
+R_V_LINE_TO, 9.05f,
+R_CUBIC_TO, 0, 0.88f, 0.5f, 1.69f, 1.29f, 2.09f,
+R_LINE_TO, 8, 4,
+R_CUBIC_TO, 0.66f, 0.33f, 1.43f, 0.33f, 2.09f, 0,
+R_LINE_TO, 8, -4,
+R_ARC_TO, 2.33f, 2.33f, 0, 0, 0, 1.29f, -2.09f,
+V_LINE_TO, 11.31f,
+R_CUBIC_TO, 0, -0.91f, -0.53f, -1.74f, -1.35f, -2.12f,
+R_LINE_TO, -8, -3.69f,
+CLOSE
\ No newline at end of file
diff --git a/ash/system/bluetooth/hid_preserving_controller/disable_bluetooth_dialog_controller_impl.cc b/ash/system/bluetooth/hid_preserving_controller/disable_bluetooth_dialog_controller_impl.cc
index acbd90ac..6674dfe 100644
--- a/ash/system/bluetooth/hid_preserving_controller/disable_bluetooth_dialog_controller_impl.cc
+++ b/ash/system/bluetooth/hid_preserving_controller/disable_bluetooth_dialog_controller_impl.cc
@@ -4,17 +4,27 @@
 
 #include "ash/system/bluetooth/hid_preserving_controller/disable_bluetooth_dialog_controller_impl.h"
 
+#include <algorithm>
+
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
+#include "ash/constants/devicetype.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/bluetooth/hid_preserving_controller/disable_bluetooth_dialog_controller.h"
+#include "chromeos/constants/devicetype.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/views/controls/bulleted_label_list/bulleted_label_list_view.h"
 #include "ui/views/controls/image_view.h"
+#include "ui/views/layout/layout_provider.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
 
+namespace {
+constexpr char kUnknownDeviceTypeName[] = "ChromeOS device";
+}  // namespace
+
 DisableBluetoothDialogControllerImpl::DisableBluetoothDialogControllerImpl() {
   CHECK(features::IsBluetoothDisconnectWarningEnabled());
 }
@@ -34,21 +44,25 @@
   DCHECK_EQ(dialog_widget_, nullptr);
   CHECK(!devices.empty());
 
+  std::string device_type = DeviceTypeToString(chromeos::GetDeviceType());
+  if (device_type.empty()) {
+    device_type = kUnknownDeviceTypeName;
+  }
+
   std::u16string dialog_description;
 
   if (devices.size() == 1) {
     dialog_description = l10n_util::GetStringFUTF16(
         IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_ONE_DEVICE,
-        base::UTF8ToUTF16(devices[0]));
+        base::UTF8ToUTF16(device_type));
   } else if (devices.size() == 2) {
     dialog_description = l10n_util::GetStringFUTF16(
         IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_TWO_DEVICES,
-        base::UTF8ToUTF16(devices[0]), base::UTF8ToUTF16(devices[1]));
+        base::UTF8ToUTF16(device_type));
   } else {
     dialog_description = l10n_util::GetStringFUTF16(
         IDS_ASH_DISCONNECT_BLUETOOTH_WARNING_DIALOG_DESCRIPTION_MULTIPLE_DEVICES,
-        base::UTF8ToUTF16(devices[0]), base::UTF8ToUTF16(devices[1]),
-        base::NumberToString16(devices.size() - 2));
+        base::NumberToString16(devices.size()), base::UTF8ToUTF16(device_type));
   }
 
   auto dialog = views::Builder<SystemDialogDelegateView>()
@@ -67,8 +81,20 @@
                         weak_ptr_factory_.GetWeakPtr()))
                     .Build();
 
+  // TODO(b/330161794): Fix bulleted list text color to match mocks.
+  std::unique_ptr<views::BulletedLabelListView> list_view =
+      std::make_unique<views::BulletedLabelListView>();
+
+  int count = std::min((int)devices.size(), 3);
+  // Intentionally limit the number of devices shown in the UI.
+  for (int i = 0; i < count; i++) {
+    list_view->AddLabel(base::UTF8ToUTF16(devices[i]));
+  }
+
   dialog->SetModalType(ui::MODAL_TYPE_SYSTEM);
   dialog->SetShowCloseButton(false);
+  dialog->SetMiddleContentView(std::move(list_view));
+  dialog->SetMiddleContentAlignment(views::LayoutAlignment::kStart);
 
   views::Widget::InitParams params;
   params.context = Shell::GetPrimaryRootWindow();
diff --git a/ash/system/holding_space/holding_space_tray_unittest.cc b/ash/system/holding_space/holding_space_tray_unittest.cc
index 7b2c365..df5a833 100644
--- a/ash/system/holding_space/holding_space_tray_unittest.cc
+++ b/ash/system/holding_space/holding_space_tray_unittest.cc
@@ -42,6 +42,7 @@
 #include "ash/system/tray/tray_constants.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
+#include "ash/test/ash_test_util.h"
 #include "ash/test/view_drawn_waiter.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_item.h"
@@ -109,45 +110,6 @@
   return view && view->GetVisible();
 }
 
-// Returns a pointer to the `ui::Layer` in the layer tree associated with the
-// specified `layer` which has the specified `name`. In the event that no such
-// layer is found, `nullptr` is returned.
-ui::Layer* FindLayerWithName(ui::Layer* layer, const char* name) {
-  if (!layer)
-    return nullptr;
-
-  if (strcmp(layer->name().c_str(), name) == 0)
-    return layer;
-
-  for (ui::Layer* child : layer->children()) {
-    layer = FindLayerWithName(child, name);
-    if (layer)
-      return layer;
-  }
-
-  return nullptr;
-}
-
-// Returns a pointer to the `ui::Layer` in the layer tree associated with the
-// specified `view` which has the specified `name`. In the event that no such
-// layer is found, `nullptr` is returned.
-ui::Layer* FindLayerWithName(views::View* view, const char* name) {
-  if (!view)
-    return nullptr;
-
-  ui::Layer* layer = FindLayerWithName(view->layer(), name);
-  if (layer)
-    return layer;
-
-  for (views::View* child : view->children()) {
-    layer = FindLayerWithName(child, name);
-    if (layer)
-      return layer;
-  }
-
-  return nullptr;
-}
-
 void Click(const views::View* view, int flags = ui::EF_NONE) {
   auto* root_window = view->GetWidget()->GetNativeWindow()->GetRootWindow();
   ui::test::EventGenerator event_generator(root_window);
diff --git a/ash/system/notification_center/notification_center_bubble_unittest.cc b/ash/system/notification_center/notification_center_bubble_unittest.cc
index 8d395f1..235a6dd 100644
--- a/ash/system/notification_center/notification_center_bubble_unittest.cc
+++ b/ash/system/notification_center/notification_center_bubble_unittest.cc
@@ -275,6 +275,42 @@
   EXPECT_TRUE(test_api()->GetNotificationViewForId(system_id)->GetVisible());
 }
 
+TEST_P(NotificationCenterBubbleTest, BubbleActivationWithGestureTap) {
+  test_api()->AddNotification();
+
+  test_api()->ToggleBubble();
+  auto* widget = test_api()->GetWidget();
+  EXPECT_FALSE(widget->IsActive());
+
+  GestureTapOn(test_api()->GetNotificationCenterView());
+  EXPECT_TRUE(widget->IsActive());
+}
+
+TEST_P(NotificationCenterBubbleTest, BubbleActivationWithTouchPress) {
+  test_api()->AddNotification();
+
+  test_api()->ToggleBubble();
+  auto* widget = test_api()->GetWidget();
+  EXPECT_FALSE(widget->IsActive());
+
+  GetEventGenerator()->PressTouch(test_api()
+                                      ->GetNotificationCenterView()
+                                      ->GetBoundsInScreen()
+                                      .CenterPoint());
+  EXPECT_TRUE(widget->IsActive());
+}
+
+TEST_P(NotificationCenterBubbleTest, BubbleActivationWithMouseClick) {
+  test_api()->AddNotification();
+
+  test_api()->ToggleBubble();
+  auto* widget = test_api()->GetWidget();
+  EXPECT_FALSE(widget->IsActive());
+
+  LeftClickOn(test_api()->GetNotificationCenterView());
+  EXPECT_TRUE(widget->IsActive());
+}
+
 // Tests that unlocking the device automatically closes the notification bubble.
 // See b/287622547.
 TEST_P(NotificationCenterBubbleTest, UnlockClosesBubble) {
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
index 3003269..d9766dd 100644
--- a/ash/system/tray/tray_bubble_view.cc
+++ b/ash/system/tray/tray_bubble_view.cc
@@ -252,24 +252,19 @@
   }
 }
 
-void TrayBubbleView::RerouteEventHandler::OnMouseEvent(
-    ui::MouseEvent* mouse_event) {
-  if (tray_bubble_view_->set_can_activate_on_click_or_tap_ &&
-      mouse_event->type() == ui::ET_MOUSE_PRESSED) {
-    tray_bubble_view_->SetCanActivate(true);
-  }
-}
-
-void TrayBubbleView::RerouteEventHandler::OnTouchEvent(
-    ui::TouchEvent* mouse_event) {
+void TrayBubbleView::RerouteEventHandler::OnEvent(ui::Event* event) {
   if (!tray_bubble_view_->set_can_activate_on_click_or_tap_) {
+    EventHandler::OnEvent(event);
     return;
   }
 
-  if (tray_bubble_view_->set_can_activate_on_click_or_tap_ &&
-      mouse_event->type() == ui::ET_MOUSE_PRESSED) {
+  if (event->type() == ui::ET_MOUSE_PRESSED ||
+      event->type() == ui::ET_TOUCH_PRESSED ||
+      event->type() == ui::ET_GESTURE_TAP) {
     tray_bubble_view_->SetCanActivate(true);
   }
+
+  EventHandler::OnEvent(event);
 }
 
 TrayBubbleView::TrayBubbleView(const InitParams& init_params)
diff --git a/ash/system/tray/tray_bubble_view.h b/ash/system/tray/tray_bubble_view.h
index 6510100..45c58e7 100644
--- a/ash/system/tray/tray_bubble_view.h
+++ b/ash/system/tray/tray_bubble_view.h
@@ -309,8 +309,7 @@
 
     // Overridden from ui::EventHandler
     void OnKeyEvent(ui::KeyEvent* event) override;
-    void OnMouseEvent(ui::MouseEvent* event) override;
-    void OnTouchEvent(ui::TouchEvent* event) override;
+    void OnEvent(ui::Event* event) override;
 
    private:
     // TrayBubbleView to which key events are going to be rerouted. Not owned.
diff --git a/ash/system/unified/classroom_bubble_student_view.cc b/ash/system/unified/classroom_bubble_student_view.cc
index 07e5430..a567ccc 100644
--- a/ash/system/unified/classroom_bubble_student_view.cc
+++ b/ash/system/unified/classroom_bubble_student_view.cc
@@ -105,7 +105,8 @@
 
 ClassroomBubbleStudentView::ClassroomBubbleStudentView()
     : ClassroomBubbleBaseView(
-          std::make_unique<ClassroomStudentComboboxModel>()) {
+          std::make_unique<ClassroomStudentComboboxModel>()),
+      shown_time_(base::Time::Now()) {
   combo_box_view_->SetSelectionChangedCallback(base::BindRepeating(
       &ClassroomBubbleStudentView::SelectedAssignmentListChanged,
       base::Unretained(this),
@@ -123,6 +124,8 @@
   if (first_assignment_list_shown_) {
     RecordStudentSelectedListChangeCount(selected_list_change_count_);
   }
+
+  RecordTotalShowTimeForClassroom(base::Time::Now() - shown_time_);
 }
 
 // static
diff --git a/ash/system/unified/classroom_bubble_student_view.h b/ash/system/unified/classroom_bubble_student_view.h
index 922e069..0f4f396 100644
--- a/ash/system/unified/classroom_bubble_student_view.h
+++ b/ash/system/unified/classroom_bubble_student_view.h
@@ -47,6 +47,9 @@
   // Handle switching between assignment lists.
   void SelectedAssignmentListChanged(bool initial_update);
 
+  // Time stamp of when the view was created.
+  const base::Time shown_time_;
+
   // The number of times that the selected list has changed during the lifetime
   // of this view.
   int selected_list_change_count_ = 0;
diff --git a/ash/system/unified/classroom_bubble_view_unittest.cc b/ash/system/unified/classroom_bubble_view_unittest.cc
index 2ab6e13f..9632d34 100644
--- a/ash/system/unified/classroom_bubble_view_unittest.cc
+++ b/ash/system/unified/classroom_bubble_view_unittest.cc
@@ -241,6 +241,15 @@
   EXPECT_EQ(3u, *combobox_view->GetSelectedIndex());
 }
 
+TEST_F(ClassroomBubbleStudentViewTest, RecordShowTimeHistogramOnClose) {
+  base::HistogramTester histogram_tester;
+  histogram_tester.ExpectTotalCount(
+      "Ash.Glanceables.TimeManagement.Classroom.TotalShowTime", 0);
+  widget_.reset();
+  histogram_tester.ExpectTotalCount(
+      "Ash.Glanceables.TimeManagement.Classroom.TotalShowTime", 1);
+}
+
 TEST_F(ClassroomBubbleStudentViewTest, ReadsInitialComboBoxViewValueFromPrefs) {
   EXPECT_CALL(classroom_client_, GetCompletedStudentAssignments(_))
       .Times(2)
diff --git a/ash/test/ash_test_util.cc b/ash/test/ash_test_util.cc
index 230e9400..0ffde0f 100644
--- a/ash/test/ash_test_util.cc
+++ b/ash/test/ash_test_util.cc
@@ -27,6 +27,7 @@
 #include "chromeos/ui/frame/multitask_menu/multitask_menu_metrics.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window_observer.h"
+#include "ui/compositor/layer.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/geometry/size.h"
@@ -35,6 +36,7 @@
 #include "ui/gfx/image/image_util.h"
 #include "ui/snapshot/snapshot_aura.h"
 #include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/view.h"
 #include "ui/views/view_utils.h"
 #include "ui/views/widget/any_widget_observer.h"
 #include "ui/views/widget/widget.h"
@@ -295,4 +297,40 @@
   }
 }
 
+ui::Layer* FindLayerWithName(ui::Layer* layer, std::string_view name) {
+  if (!layer) {
+    return nullptr;
+  }
+
+  if (layer->name() == name) {
+    return layer;
+  }
+
+  for (ui::Layer* child : layer->children()) {
+    if (ui::Layer* result = FindLayerWithName(child, name)) {
+      return result;
+    }
+  }
+
+  return nullptr;
+}
+
+ui::Layer* FindLayerWithName(views::View* view, std::string_view name) {
+  if (!view) {
+    return nullptr;
+  }
+
+  if (ui::Layer* layer = FindLayerWithName(view->layer(), name)) {
+    return layer;
+  }
+
+  for (views::View* child : view->children()) {
+    if (ui::Layer* layer = FindLayerWithName(child, name)) {
+      return layer;
+    }
+  }
+
+  return nullptr;
+}
+
 }  // namespace ash
diff --git a/ash/test/ash_test_util.h b/ash/test/ash_test_util.h
index 7792fb5f..bd52ac4 100644
--- a/ash/test/ash_test_util.h
+++ b/ash/test/ash_test_util.h
@@ -6,6 +6,7 @@
 #define ASH_TEST_ASH_TEST_UTIL_H_
 
 #include <cstddef>
+#include <string_view>
 
 #include "chromeos/ui/frame/caption_buttons/frame_size_button.h"
 #include "chromeos/ui/frame/multitask_menu/multitask_menu_metrics.h"
@@ -28,12 +29,16 @@
 class Size;
 }  // namespace gfx
 
-namespace ui::test {
+namespace ui {
+class Layer;
+namespace test {
 class EventGenerator;
-}  // namespace ui::test
+}  // namespace test
+}  // namespace ui
 
 namespace views {
 class MenuItemView;
+class View;
 }  // namespace views
 
 namespace ash {
@@ -97,6 +102,16 @@
              int flags = ui::EF_NONE,
              int count = 1);
 
+// Returns a pointer to the `ui::Layer` in the layer tree associated with the
+// specified `layer` which has the specified `name`. In the event that no such
+// layer is found, `nullptr` is returned.
+ui::Layer* FindLayerWithName(ui::Layer* layer, std::string_view name);
+
+// Returns a pointer to the `ui::Layer` in the layer tree associated with the
+// specified `view` which has the specified `name`. In the event that no such
+// layer is found, `nullptr` is returned.
+ui::Layer* FindLayerWithName(views::View* view, std::string_view name);
+
 }  // namespace ash
 
 #endif
diff --git a/ash/wallpaper/views/wallpaper_view.cc b/ash/wallpaper/views/wallpaper_view.cc
index c38385a..2821d850 100644
--- a/ash/wallpaper/views/wallpaper_view.cc
+++ b/ash/wallpaper/views/wallpaper_view.cc
@@ -60,7 +60,7 @@
   WallpaperWidgetDelegate(const WallpaperWidgetDelegate&) = delete;
   WallpaperWidgetDelegate& operator=(const WallpaperWidgetDelegate&) = delete;
 
-  // Overrides views::View.
+  // views::View:
   void Layout(PassKey) override {
     aura::Window* window = GetWidget()->GetNativeWindow();
     // Keep |this| at the bottom since there may be other windows on top of the
diff --git a/ash/wallpaper/views/wallpaper_widget_controller.cc b/ash/wallpaper/views/wallpaper_widget_controller.cc
index b7f434c8..dc06905 100644
--- a/ash/wallpaper/views/wallpaper_widget_controller.cc
+++ b/ash/wallpaper/views/wallpaper_widget_controller.cc
@@ -126,7 +126,9 @@
     return;
   }
 
-  wallpaper_underlay_layer_->SetBounds(root_window_->GetBoundsInScreen());
+  // Bounds have to be in parent. Since these are set on the layer directly, and
+  // layer bounds are relative to the layer's parent.
+  wallpaper_underlay_layer_->SetBounds(root_window_->bounds());
 }
 
 void WallpaperWidgetController::OnColorProviderChanged() {
@@ -150,7 +152,8 @@
   wallpaper_view_layer_parent->Add(wallpaper_underlay_layer_.get());
   wallpaper_view_layer_parent->StackBelow(wallpaper_underlay_layer_.get(),
                                           wallpaper_view_layer);
-  wallpaper_underlay_layer_->SetBounds(root_window_->GetBoundsInScreen());
+  wallpaper_underlay_layer_->SetBounds(root_window_->bounds());
+
   OnColorProviderChanged();
 
   // The `wallpaper_underlay_layer_` should be invisible by default. This
diff --git a/ash/webui/common/mojom/sea_pen.mojom b/ash/webui/common/mojom/sea_pen.mojom
index 740d661..3548e66b2 100644
--- a/ash/webui/common/mojom/sea_pen.mojom
+++ b/ash/webui/common/mojom/sea_pen.mojom
@@ -53,6 +53,10 @@
 struct SeaPenFeedbackMetadata {
   bool is_positive;
   string log_id;
+
+  // This is the generation seed from the Manta Service to identify a
+  // SeaPenThumbnail.
+  uint32 generation_seed;
 };
 
 // A text or template query for constructing a Manta request.
diff --git a/ash/webui/common/resources/cellular_setup/esim_flow_ui.ts b/ash/webui/common/resources/cellular_setup/esim_flow_ui.ts
index 945814d..a7b633a 100644
--- a/ash/webui/common/resources/cellular_setup/esim_flow_ui.ts
+++ b/ash/webui/common/resources/cellular_setup/esim_flow_ui.ts
@@ -29,7 +29,7 @@
 import {getTemplate} from './esim_flow_ui.html.js';
 import {getEuicc, getPendingESimProfiles} from './esim_manager_utils.js';
 import {getESimManagerRemote} from './mojo_interface_provider.js';
-import {ProfileDiscoveryListPageElement} from './profile_discovery_list_page';
+import {ProfileDiscoveryListPageElement} from './profile_discovery_list_page.js';
 import {SubflowMixin} from './subflow_mixin.js';
 
 export enum EsimPageName {
diff --git a/ash/webui/common/resources/sea_pen/constants_generated.ts b/ash/webui/common/resources/sea_pen/constants_generated.ts
index 61347d4..5ae77fab 100644
--- a/ash/webui/common/resources/sea_pen/constants_generated.ts
+++ b/ash/webui/common/resources/sea_pen/constants_generated.ts
@@ -431,6 +431,8 @@
               value: SeaPenTemplateOption.kArtFeatureBeach,
               translation:
                   loadTimeData.getString('seaPenOptionArtFeatureBeach'),
+              previewUrl:
+                  'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/beach.jpg',
             },
             {
               value: SeaPenTemplateOption.kArtFeatureMeadow,
@@ -1203,6 +1205,8 @@
               value: SeaPenTemplateOption.kSurrealLandscapeBeach,
               translation:
                   loadTimeData.getString('seaPenOptionSurrealLandscapeBeach'),
+              previewUrl:
+                  'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/beach.jpg',
             },
             {
               value: SeaPenTemplateOption.kSurrealLandscapeCliff,
@@ -2272,6 +2276,8 @@
             {
               value: SeaPenTemplateOption.kVcBackgroundCafeStyleRomantic,
               translation: 'romantic',
+              previewUrl:
+                  'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/romantic.jpg',
             },
           ],
         ],
@@ -2342,6 +2348,8 @@
             {
               value: SeaPenTemplateOption.kVcBackgroundArtFeatureBeach,
               translation: 'beach',
+              previewUrl:
+                  'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/beach.jpg',
             },
             {
               value: SeaPenTemplateOption.kVcBackgroundArtFeatureMeadow,
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_feedback_element.ts b/ash/webui/common/resources/sea_pen/sea_pen_feedback_element.ts
index dedc1ad..1077101 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_feedback_element.ts
+++ b/ash/webui/common/resources/sea_pen/sea_pen_feedback_element.ts
@@ -8,6 +8,7 @@
 import 'chrome://resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
 import 'chrome://resources/ash/common/cr_elements/icons.html.js';
 
+import {SeaPenThumbnail} from './sea_pen.mojom-webui.js';
 import {getTemplate} from './sea_pen_feedback_element.html.js';
 import {WithSeaPenStore} from './sea_pen_store.js';
 
@@ -32,16 +33,20 @@
         type: String,
         value: FeedbackOption.UNSPECIFIED,
       },
+      thumbnail: {
+        type: Object,
+      },
     };
   }
 
   selectedFeedbackOption: FeedbackOption;
+  thumbnail: SeaPenThumbnail;
 
   private notifySelectedOptionChanged_(isThumbsUp: boolean) {
     this.dispatchEvent(new CustomEvent('selected-feedback-changed', {
       bubbles: true,
       composed: true,
-      detail: {isThumbsUp},
+      detail: {isThumbsUp, thumbnailId: this.thumbnail.id},
     }));
   }
 
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_images_element.html b/ash/webui/common/resources/sea_pen/sea_pen_images_element.html
index 6661f01..1616335 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_images_element.html
+++ b/ash/webui/common/resources/sea_pen/sea_pen_images_element.html
@@ -203,6 +203,7 @@
           <template is="dom-if" if="[[!thumbnailsLoading_]]">
             <div class="feedback-icon-container">
               <sea-pen-feedback
+                  thumbnail="[[item]]"
                   on-selected-feedback-changed="onSelectedFeedbackChanged_">
               </sea-pen-feedback>
               <div class="feedback-icon-container-shadow-left"></div>
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_images_element.ts b/ash/webui/common/resources/sea_pen/sea_pen_images_element.ts
index b1ffed0..aeb4901 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_images_element.ts
+++ b/ash/webui/common/resources/sea_pen/sea_pen_images_element.ts
@@ -364,14 +364,15 @@
   }
   // END AUTOGENERATED - DO NOT EDIT!
 
-  private onSelectedFeedbackChanged_(event:
-                                         CustomEvent<{isThumbsUp: boolean}>) {
+  private onSelectedFeedbackChanged_(
+      event: CustomEvent<{isThumbsUp: boolean, thumbnailId: number}>) {
     const isThumbsUp = event.detail.isThumbsUp;
     const templateName = this.getTemplateNameFromId_(this.templateId);
     logSeaPenTemplateFeedback(templateName, isThumbsUp);
     const metadata = {
       isPositive: isThumbsUp,
       logId: templateName,
+      generationSeed: event.detail.thumbnailId,
     };
     openFeedbackDialog(metadata, getSeaPenProvider());
   }
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.html b/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.html
index 0d7ff674..5e580ad 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.html
+++ b/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.html
@@ -95,6 +95,40 @@
   .more-like-this-option {
     display: none;
   }
+
+  cr-dialog::part(dialog) {
+    border-radius: 20px;
+  }
+
+  @media (max-width: 671px) {
+    cr-dialog::part(dialog) {
+      width: 440px;
+    }
+  }
+
+  #wallpaperInfoBody {
+    padding: 0 32px;
+  }
+
+  #wallpaperInfoBody h2{
+    color: var(--cros-sys-on_surface);
+    font: var(--cros-display-7-font);
+    margin: 0 0 16px;
+  }
+
+  #wallpaperInfoBody p {
+    color: var(--cros-sys-on_surface_variant);
+    font: var(--cros-body-1-font);
+    margin: 0 0 16px;
+  }
+
+  #wallpaperInfoButtonContainer {
+    padding: 16px 32px 28px;
+  }
+
+  #wallpaperInfoCloseButton {
+    border-radius: 16px;
+  }
 </style>
 <template is="dom-if" if="[[shouldShowRecentlyUsedWallpapers_(recentImages_)]]">
   <h2 class="wallpaper-collections-heading">[[getRecentPoweredByGoogleMessage_()]]</h2>
@@ -147,7 +181,7 @@
         </cr-action-menu>
         <template is="dom-if" if="[[shouldShowWallpaperInfoDialog_(index, currentShowWallpaperInfoDialog_)]]" restamp>
           <cr-dialog id="wallpaperInfoDialog" data-id$="[[index]]" on-close="onCloseDialog_" show-on-attach>
-            <div slot="body">
+            <div id="wallpaperInfoBody" slot="body">
               <h2>[[i18n('seaPenAboutDialogTitle')]]</h2>
               <p class="about-prompt-info">
                 [[getWallpaperInfoPromptMessage_(image, recentImageData_, recentImageDataLoading_)]]
@@ -156,7 +190,7 @@
                 [[getWallpaperInfoDateMessage_(image, recentImageData_, recentImageDataLoading_)]]
               </p>
             </div>
-            <div slot="button-container">
+            <div id="wallpaperInfoButtonContainer" slot="button-container">
               <cr-button id="wallpaperInfoCloseButton" class="action-button primary" on-click="onCloseDialog_">
                 [[i18n('seaPenAboutDialogClose')]]
               </cr-button>
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_template_query_element.html b/ash/webui/common/resources/sea_pen/sea_pen_template_query_element.html
index b88b056..d71615cb 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_template_query_element.html
+++ b/ash/webui/common/resources/sea_pen/sea_pen_template_query_element.html
@@ -13,7 +13,6 @@
 
   #template {
     align-items: start;
-    border-bottom-color: var(--cros-text-color-primary);
     color: var(--cros-text-color-primary);
     display: flex;
     font: var(--cros-display-6_regular-font);
@@ -56,9 +55,9 @@
     background-color: var(--cros-sys-hover_on_subtle);
   }
 
-  .chip-text:focus {
+  .chip-text:focus-visible {
     background-color: var(--cros-sys-ripple_primary);
-    border: 2px solid var(--cros-sys-focus_ring);
+    outline: 2px solid var(--cros-sys-focus_ring);
   }
 
   .underline {
diff --git a/ash/webui/network_ui/traffic_counters_resource_provider.cc b/ash/webui/network_ui/traffic_counters_resource_provider.cc
index a91e3f16..1193861 100644
--- a/ash/webui/network_ui/traffic_counters_resource_provider.cc
+++ b/ash/webui/network_ui/traffic_counters_resource_provider.cc
@@ -52,6 +52,8 @@
      IDS_TRAFFIC_COUNTERS_DATA_USAGE_AUTO_RESET_DAY_OF_MONTH_SUBLABEL},
     {"TrafficCountersDataUsageDifferentFromProviderLabel",
      IDS_TRAFFIC_COUNTERS_DATA_USAGE_DIFFERENT_FROM_PROVIDER_LABEL},
+    {"TrafficCountersDataUsageResetDayTooltipText",
+     IDS_TRAFFIC_COUNTERS_DATA_USAGE_RESET_DAY_TOOLTIP_TEXT},
 };
 
 }  // namespace
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_reducers.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_reducers.ts
index 1514bc6..64f811f 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_reducers.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_reducers.ts
@@ -4,7 +4,7 @@
 
 import {SeaPenActionName, SeaPenActions} from 'chrome://resources/ash/common/sea_pen/sea_pen_actions.js';
 import {seaPenReducer} from 'chrome://resources/ash/common/sea_pen/sea_pen_reducer.js';
-import {SeaPenState} from 'chrome://resources/ash/common/sea_pen/sea_pen_state';
+import {SeaPenState} from 'chrome://resources/ash/common/sea_pen/sea_pen_state.js';
 import {isImageDataUrl, isNonEmptyArray, isNonEmptyFilePath} from 'chrome://resources/ash/common/sea_pen/sea_pen_utils.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
diff --git a/ash/webui/shortcut_customization_ui/resources/js/search/fake_shortcut_search_handler.ts b/ash/webui/shortcut_customization_ui/resources/js/search/fake_shortcut_search_handler.ts
index e8a8896..a94b9df3 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/search/fake_shortcut_search_handler.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/search/fake_shortcut_search_handler.ts
@@ -6,8 +6,7 @@
 import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
 
 import {SearchResultsAvailabilityObserverRemote} from '../../mojom-webui/search.mojom-webui.js';
-import {MojoSearchResult} from '../shortcut_types';
-import {ShortcutSearchHandlerInterface} from '../shortcut_types.js';
+import {MojoSearchResult, ShortcutSearchHandlerInterface} from '../shortcut_types.js';
 
 /**
  * @fileoverview
diff --git a/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.ts b/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.ts
index dcb4102..75362363 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.ts
@@ -15,7 +15,7 @@
 import {AcceleratorSubsectionElement} from './accelerator_subsection.js';
 import {getShortcutProvider} from './mojo_interface_provider.js';
 import {RouteObserver, Router} from './router.js';
-import {AcceleratorCategory, AcceleratorSubcategory} from './shortcut_types';
+import {AcceleratorCategory, AcceleratorSubcategory} from './shortcut_types.js';
 import {getTemplate} from './shortcuts_page.html.js';
 
 /**
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 33659e7..069d6a2 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -115,13 +115,6 @@
 // interval.
 constexpr base::TimeDelta kDeskTraversalsTimeout = base::Seconds(5);
 
-constexpr char kCloseAllZombieWindowsFoundHistogramName[] =
-    "Ash.Desks.CloseAllZombieWindowsFound";
-
-// The amount of time we wait after `CleanUpClosedAppWindowsTask` runs before
-// we check how many of those windows are still in memory.
-constexpr base::TimeDelta kCloseAllWindowsZombieCheckTimeout = base::Minutes(1);
-
 constexpr int kDeskDefaultNameIds[] = {
     IDS_ASH_DESKS_DESK_1_MINI_VIEW_TITLE,
     IDS_ASH_DESKS_DESK_2_MINI_VIEW_TITLE,
@@ -270,13 +263,6 @@
   ToastManager::Get()->Show(std::move(undo_toast_data));
 }
 
-// Reports the number of windows that still exist in `window_tracker`.
-void ReportNumberOfZombieWindows(
-    std::unique_ptr<aura::WindowTracker> window_tracker) {
-  base::UmaHistogramCounts100(kCloseAllZombieWindowsFoundHistogramName,
-                              window_tracker->windows().size());
-}
-
 AccountId GetPrimaryUserAccountId() {
   return Shell::Get()
       ->session_controller()
@@ -2246,8 +2232,6 @@
 
 void DesksController::CleanUpClosedAppWindowsTask(
     std::unique_ptr<aura::WindowTracker> closing_window_tracker) {
-  auto widgetless_windows = std::make_unique<aura::WindowTracker>();
-
   // We have waited long enough for these app windows to close cleanly.
   // If there is any app windows still around, we will close them forcefully.
   // These window's desk has already been removed. We should not let these
@@ -2262,20 +2246,8 @@
     // close cleanly before this.
     if (widget) {
       widget->CloseNow();
-    } else {
-      // If the window does not have a widget, we add it to the
-      // `widgetless_windows` tracker to check back on later.
-      widgetless_windows->Add(window);
     }
   }
-
-  // We post a delayed task to check that all of the windows in
-  // `widgetless_windows eventually end up closing.
-  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&ReportNumberOfZombieWindows,
-                     std::move(widgetless_windows)),
-      kCloseAllWindowsZombieCheckTimeout);
 }
 
 void DesksController::MoveVisibleOnAllDesksWindowsFromActiveDeskTo(
diff --git a/ash/wm/desks/templates/restore_data_collector.cc b/ash/wm/desks/templates/restore_data_collector.cc
index cb3bb21..67d4cf0 100644
--- a/ash/wm/desks/templates/restore_data_collector.cc
+++ b/ash/wm/desks/templates/restore_data_collector.cc
@@ -116,8 +116,7 @@
     has_supported_apps = true;
 
     std::unique_ptr<app_restore::WindowInfo> window_info =
-        BuildWindowInfo(window, /*activation_index=*/std::nullopt,
-                        /*for_saved_desks=*/true, mru_windows);
+        BuildWindowInfo(window, /*activation_index=*/std::nullopt, mru_windows);
 
     // Clear the desk ID and uuid in the WindowInfo that is to be stored in
     // the template. They will be set to the newly created desk when
diff --git a/ash/wm/desks/templates/saved_desk_icon_container.cc b/ash/wm/desks/templates/saved_desk_icon_container.cc
index 33003257..a106d4f 100644
--- a/ash/wm/desks/templates/saved_desk_icon_container.cc
+++ b/ash/wm/desks/templates/saved_desk_icon_container.cc
@@ -13,11 +13,11 @@
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/templates/saved_desk_constants.h"
+#include "ash/wm/window_restore/window_restore_util.h"
 #include "base/check.h"
 #include "base/containers/contains.h"
 #include "base/memory/raw_ptr.h"
 #include "base/ranges/algorithm.h"
-#include "components/app_constants/constants.h"
 #include "components/app_restore/app_restore_utils.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -28,11 +28,6 @@
 
 namespace {
 
-bool IsBrowserAppId(const std::string& app_id) {
-  return app_id == app_constants::kChromeAppId ||
-         app_id == app_constants::kLacrosAppId;
-}
-
 // Given a map of unique icon identifiers to icon info, returns a vector of the
 // same key, value pair ordered by icons' activation index.
 std::vector<SavedDeskIconContainer::IconIdentifierAndIconInfo>
diff --git a/ash/wm/lock_state_controller.cc b/ash/wm/lock_state_controller.cc
index a4fa3a1a..3d74a522 100644
--- a/ash/wm/lock_state_controller.cc
+++ b/ash/wm/lock_state_controller.cc
@@ -27,6 +27,7 @@
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/session_state_animator_impl.h"
+#include "ash/wm/window_restore/pine_constants.h"
 #include "ash/wm/window_restore/window_restore_util.h"
 #include "base/command_line.h"
 #include "base/debug/crash_logging.h"
@@ -97,14 +98,6 @@
 // before actually requesting shutdown, to give the animation time to finish.
 constexpr base::TimeDelta kShutdownRequestDelay = base::Milliseconds(50);
 
-// The resized width of the pine image in landscape or portrait orientation. The
-// width will be fixed and then the height of the resized image will be
-// calculated based on the aspect ratio of the original pine image. The resized
-// pine image will be saved to disk, decoded and shown with this size directly
-// inside the pine dialog later as well.
-constexpr int kResizedPineImageWidthInLandscape = 344;
-constexpr int kResizedPineImageWidthInPortrait = 384;
-
 // Amount of time to wait after starting to take the pine screenshot. The task
 // will be stopped if it takes longer than this time duration.
 constexpr base::TimeDelta kTakeScreenshotFailTimeout = base::Milliseconds(800);
@@ -129,15 +122,14 @@
     return;
   }
 
-  const int image_width = image.Width();
-  const int image_height = image.Height();
-  const float aspect_ratio = static_cast<float>(image_height) / image_width;
-  const int resized_image_width = image_width > image_height
-                                      ? kResizedPineImageWidthInLandscape
-                                      : kResizedPineImageWidthInPortrait;
-  const int resized_image_height = aspect_ratio * resized_image_width;
+  // The width of the resized pine image will be fixed and then the height of it
+  // will be calculated based on the aspect ratio of the original pine image.
+  // The resized pine image will be saved to disk, decoded and shown with this
+  // size directly inside the pine dialog later as well.
+  const float aspect_ratio = static_cast<float>(image.Height()) / image.Width();
+  const int resized_image_height = aspect_ratio * pine::kPreviewContainerWidth;
   const auto resized_image = gfx::ResizedImage(
-      image, gfx::Size(resized_image_width, resized_image_height));
+      image, gfx::Size(pine::kPreviewContainerWidth, resized_image_height));
   auto png_bytes = resized_image.As1xPNGBytes();
   auto raw_data = base::make_span(png_bytes->data(), png_bytes->size());
   if (!base::WriteFile(file_path, raw_data)) {
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index 82760aa..825fffb 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -27,6 +27,7 @@
 #include "ash/shell_delegate.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_id.h"
+#include "ash/style/rounded_label_widget.h"
 #include "ash/style/typography.h"
 #include "ash/system/toast/toast_manager_impl.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
@@ -531,7 +532,7 @@
 bool ShouldShowPineDialog(aura::Window* root_window) {
   return root_window == Shell::GetPrimaryRootWindow() &&
          features::IsForestFeatureEnabled() &&
-         !!Shell::Get()->pine_controller()->pine_contents_data();
+         Shell::Get()->pine_controller()->ShouldShowPineDialog();
 }
 
 }  // namespace
@@ -672,6 +673,8 @@
         std::make_unique<ScopedOverviewWallpaperClipper>(this);
   }
 
+  // TODO(b/326434696): Currently this will return false if there is no restore
+  // data in the pine contents data. Show the zero-state dialog.
   if (ShouldShowPineDialog(root_window_)) {
     pine_widget_ = PineContentsView::Create(GetGridEffectiveBounds());
     pine_widget_->ShowInactive();
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index 8455466..ddc7407 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -5,15 +5,11 @@
 #ifndef ASH_WM_OVERVIEW_OVERVIEW_GRID_H_
 #define ASH_WM_OVERVIEW_OVERVIEW_GRID_H_
 
-#include <stddef.h>
-
 #include <memory>
 #include <vector>
 
 #include "ash/public/cpp/wallpaper/wallpaper_controller_observer.h"
 #include "ash/rotator/screen_rotation_animator_observer.h"
-#include "ash/style/icon_button.h"
-#include "ash/style/rounded_label_widget.h"
 #include "ash/wm/desks/templates/saved_desk_save_desk_button_container.h"
 #include "ash/wm/overview/birch/birch_bar_view.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -51,6 +47,7 @@
 class OverviewGridEventHandler;
 class OverviewItemBase;
 class OverviewSession;
+class RoundedLabelWidget;
 class SavedDeskSaveDeskButton;
 class SavedDeskLibraryView;
 class ScopedOverviewWallpaperClipper;
@@ -496,6 +493,8 @@
     return faster_splitview_widget_.get();
   }
 
+  const views::Widget* pine_widget() const { return pine_widget_.get(); }
+
   int num_incognito_windows() const { return num_incognito_windows_; }
 
   int num_unsupported_windows() const { return num_unsupported_windows_; }
@@ -671,9 +670,6 @@
   // if split view is unsupported (see |ShouldAllowSplitView|).
   std::unique_ptr<SplitViewDragIndicators> split_view_drag_indicators_;
 
-  // A solid-color layer stacked below the clipped wallpaper.
-  std::unique_ptr<ui::Layer> wallpaper_underlay_layer_;
-
   // Widget that contains the DeskBarView contents when the Virtual Desks
   // feature is enabled.
   std::unique_ptr<views::Widget> desks_widget_;
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index 3cc60c7..41fd803 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -4,7 +4,6 @@
 
 #include "ash/wm/overview/overview_item.h"
 
-#include <sstream>
 #include <utility>
 #include <vector>
 
@@ -32,6 +31,7 @@
 #include "ash/wm/overview/scoped_overview_animation_settings.h"
 #include "ash/wm/overview/scoped_overview_hide_windows.h"
 #include "ash/wm/raster_scale/raster_scale_controller.h"
+#include "ash/wm/snap_group/snap_group_controller.h"
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/window_mini_view_header_view.h"
@@ -1021,6 +1021,22 @@
   }
 }
 
+void OverviewItem::OnWindowParentChanged(aura::Window* window,
+                                         aura::Window* parent) {
+  if (!parent || !prepared_for_overview_ ||
+      !OverviewController::Get()->InOverviewSession()) {
+    return;
+  }
+
+  if (root_window_ != window->GetRootWindow()) {
+    overview_session_->AddItemInMruOrder(
+        window, /*reposition=*/false, /*animate=*/true,
+        /*restack=*/true, /*use_spawn_animation=*/true);
+    window_destruction_delegate_->OnOverviewItemWindowDestroying(
+        this, /*reposition=*/true);
+  }
+}
+
 void OverviewItem::OnWindowBoundsChanged(aura::Window* window,
                                          const gfx::Rect& old_bounds,
                                          const gfx::Rect& new_bounds,
diff --git a/ash/wm/overview/overview_item.h b/ash/wm/overview/overview_item.h
index 5114187..f8103f2 100644
--- a/ash/wm/overview/overview_item.h
+++ b/ash/wm/overview/overview_item.h
@@ -136,6 +136,8 @@
   void OnWindowPropertyChanged(aura::Window* window,
                                const void* key,
                                intptr_t old) override;
+  void OnWindowParentChanged(aura::Window* window,
+                             aura::Window* parent) override;
   void OnWindowBoundsChanged(aura::Window* window,
                              const gfx::Rect& old_bounds,
                              const gfx::Rect& new_bounds,
diff --git a/ash/wm/overview/overview_item_base.cc b/ash/wm/overview/overview_item_base.cc
index 36e362df..812fc847 100644
--- a/ash/wm/overview/overview_item_base.cc
+++ b/ash/wm/overview/overview_item_base.cc
@@ -8,6 +8,7 @@
 
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
+#include "ash/style/rounded_label_widget.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/overview/overview_constants.h"
 #include "ash/wm/overview/overview_controller.h"
diff --git a/ash/wm/overview/overview_item_view.cc b/ash/wm/overview/overview_item_view.cc
index 919141a8..d5d836f 100644
--- a/ash/wm/overview/overview_item_view.cc
+++ b/ash/wm/overview/overview_item_view.cc
@@ -191,17 +191,9 @@
     const aura::Window* window = overview_item_->GetWindow();
     if (SnapGroup* snap_group =
             snap_group_controller->GetSnapGroupForGivenWindow(window)) {
-      const int corner_radius = window_util::GetMiniWindowRoundedCornerRadius();
-      SetRoundedCornersRadius(window == snap_group->window1()
-                                  ? gfx::RoundedCornersF(
-                                        /*upper_left=*/corner_radius,
-                                        /*upper_right=*/0, /*lower_right=*/0,
-                                        /*lower_left=*/corner_radius)
-                                  : gfx::RoundedCornersF(
-                                        /*upper_left=*/0,
-                                        /*upper_right=*/corner_radius,
-                                        /*lower_right=*/corner_radius,
-                                        /*lower_left=*/0));
+      SetRoundedCornersRadius(window_util::GetMiniWindowRoundedCorners(
+          window, /*include_header_rounding=*/true));
+
       // `SetRoundedCornersRadius()` will trigger rounded corners update for
       // header view, preview view and focus ring automatically. Early return to
       // avoid duplicate updates.
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 3b7f16d..7bf4d2d 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -16,6 +16,7 @@
 #include "ash/root_window_settings.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
+#include "ash/style/rounded_label_widget.h"
 #include "ash/wm/desks/desk.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/desks/legacy_desk_bar_view.h"
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index f47399f..0a7471f 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -3687,6 +3687,44 @@
   EXPECT_EQ(OcclusionState::VISIBLE, window1->GetOcclusionState());
 }
 
+// Verify that when an overview item is moved to a different display, it
+// is properly removed from the original grid and displayed in the new one with
+// no crash. See original crash reported at http://b/320479135.
+TEST_P(OverviewSessionTest,
+       NoCrashWhenSettingOverviewItemBoundsOnAnotherDisplay) {
+  UpdateDisplay("800x700,801+0-800x700");
+  display::DisplayManager* display_manager = Shell::Get()->display_manager();
+  EXPECT_EQ(2U, display_manager->GetNumDisplays());
+  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
+
+  std::unique_ptr<aura::Window> window =
+      CreateAppWindow(gfx::Rect(10, 10, 200, 100));
+  // Explicitly call `set_allow_set_bounds_direct()` to true to trigger the same
+  // stack trace.
+  WindowState::Get(window.get())->set_allow_set_bounds_direct(true);
+  aura::Window* old_root_window = window->GetRootWindow();
+
+  ToggleOverview();
+  ASSERT_TRUE(IsInOverviewSession());
+
+  const auto& grids = GetOverviewSession()->grid_list();
+  ASSERT_EQ(2u, grids.size());
+  auto grid0 = grids[0].get();
+  ASSERT_TRUE(grid0);
+  const auto& overview_items = grid0->window_list();
+  ASSERT_EQ(overview_items.size(), 1u);
+  EXPECT_TRUE(IsWindowInItsCorrespondingOverviewGrid(window.get()));
+
+  // Verify that when setting the window bounds to another display, the window
+  // will be moved properly.
+  window->SetBoundsInScreen(
+      gfx::Rect(900, 10, 200, 100),
+      display::Screen::GetScreen()->GetDisplayNearestWindow(
+          Shell::GetAllRootWindows()[1].get()));
+  EXPECT_NE(window->GetRootWindow(), old_root_window);
+  EXPECT_TRUE(IsWindowInItsCorrespondingOverviewGrid(window.get()));
+}
+
 // If you update the parameterisation of OverviewSessionTest also update the
 // parameterisation of OverviewRasterScaleTest below.
 INSTANTIATE_TEST_SUITE_P(
@@ -10898,6 +10936,41 @@
   EXPECT_EQ(display_bounds3, wallpaper_view_layer->bounds());
 }
 
+// Test that:
+// Upon Entering Overview:
+// -Wallpaper view layer should be clipped across all displays;
+// - Wallpaper underlay layer should be visible across all displays.
+// Upon Exiting Overview:
+// - Wallpaper view layer should be restored across all displays;
+// - Wallpaper underlay layer should not be visible across all displays.
+TEST_F(OakTest, MultiDisplayTest) {
+  UpdateDisplay("800x700,801+0-800x700,1602+0-800x700");
+  display::DisplayManager* display_manager = Shell::Get()->display_manager();
+  EXPECT_EQ(3U, display_manager->GetNumDisplays());
+
+  auto verify_layers_bounds_on_all_displays = [&](bool in_overview) {
+    for (auto root : Shell::GetAllRootWindows()) {
+      auto* wallpaper_widget_controller =
+          RootWindowController::ForWindow(root)->wallpaper_widget_controller();
+      auto* wallpaper_view_layer =
+          wallpaper_widget_controller->wallpaper_view()->layer();
+      auto* wallpaper_underlay_layer =
+          wallpaper_widget_controller->wallpaper_underlay_layer();
+      EXPECT_EQ(root->bounds(), wallpaper_underlay_layer->bounds());
+      EXPECT_EQ(in_overview, wallpaper_underlay_layer->IsVisible());
+      EXPECT_EQ(in_overview, !wallpaper_view_layer->clip_rect().IsEmpty());
+    }
+  };
+
+  ToggleOverview();
+  ASSERT_TRUE(IsInOverviewSession());
+  verify_layers_bounds_on_all_displays(/*in_overview=*/true);
+
+  ToggleOverview();
+  ASSERT_FALSE(IsInOverviewSession());
+  verify_layers_bounds_on_all_displays(/*in_overview=*/false);
+}
+
 // Tests that the wallpaper is clipped in partial overview mode and adjusts
 // correctly when the snapped window is resized.
 TEST_F(OakTest, PartialOverviewVisualsAndResize) {
diff --git a/ash/wm/overview/overview_test_base.cc b/ash/wm/overview/overview_test_base.cc
index a3fe3fd..01ae575 100644
--- a/ash/wm/overview/overview_test_base.cc
+++ b/ash/wm/overview/overview_test_base.cc
@@ -10,6 +10,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/style/close_button.h"
+#include "ash/style/rounded_label_widget.h"
 #include "ash/style/system_shadow.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_drop_target.h"
diff --git a/ash/wm/overview/overview_test_util.cc b/ash/wm/overview/overview_test_util.cc
index 675a29c..dc23baa 100644
--- a/ash/wm/overview/overview_test_util.cc
+++ b/ash/wm/overview/overview_test_util.cc
@@ -174,4 +174,16 @@
   }
 }
 
+bool IsWindowInItsCorrespondingOverviewGrid(aura::Window* window) {
+  const auto& overview_items =
+      GetOverviewGridForRoot(window->GetRootWindow())->window_list();
+  for (auto& overview_item : overview_items) {
+    if (overview_item->Contains(window)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 }  // namespace ash
diff --git a/ash/wm/overview/overview_test_util.h b/ash/wm/overview/overview_test_util.h
index 8c6028b..447e2b6 100644
--- a/ash/wm/overview/overview_test_util.h
+++ b/ash/wm/overview/overview_test_util.h
@@ -14,6 +14,7 @@
 
 namespace ash {
 
+class OverviewGrid;
 class OverviewItemBase;
 
 void SendKey(ui::KeyboardCode key, int flags = ui::EF_NONE);
@@ -68,6 +69,10 @@
 void WaitForOcclusionStateChange(aura::Window* window,
                                  aura::Window::OcclusionState target_state);
 
+// Returns true if the given `window` is on its corresponding overview grid,
+// returns false otherwise.
+bool IsWindowInItsCorrespondingOverviewGrid(aura::Window* window);
+
 }  // namespace ash
 
 #endif  // ASH_WM_OVERVIEW_OVERVIEW_TEST_UTIL_H_
diff --git a/ash/wm/overview/scoped_overview_wallpaper_clipper.cc b/ash/wm/overview/scoped_overview_wallpaper_clipper.cc
index 838b460..2fced934 100644
--- a/ash/wm/overview/scoped_overview_wallpaper_clipper.cc
+++ b/ash/wm/overview/scoped_overview_wallpaper_clipper.cc
@@ -14,6 +14,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/display/screen.h"
 #include "ui/views/animation/animation_builder.h"
+#include "ui/wm/core/coordinate_conversion.h"
 
 namespace ash {
 
@@ -43,13 +44,18 @@
   auto* wallpaper_view_layer =
       wallpaper_widget_controller->wallpaper_view()->layer();
 
+  // `GetGridEffectiveBounds()` returns the bounds in screen coordinates.
+  // Convert these to the parent's coordinates, as layer bounds are always
+  // relative to their parent.
+  gfx::Rect target_clip_rect = overview_grid_->GetGridEffectiveBounds();
+  wm::ConvertRectFromScreen(root_window, &target_clip_rect);
+
   views::AnimationBuilder()
       .SetPreemptionStrategy(
           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
       .Once()
       .SetDuration(kWallpaperClippingAnimationDuration)
-      .SetClipRect(wallpaper_view_layer,
-                   overview_grid_->GetGridEffectiveBounds(),
+      .SetClipRect(wallpaper_view_layer, target_clip_rect,
                    gfx::Tween::ACCEL_20_DECEL_100)
       .SetRoundedCorners(wallpaper_view_layer, kWallpaperClipRoundedCornerRadii,
                          gfx::Tween::ACCEL_20_DECEL_100);
diff --git a/ash/wm/scoped_layer_tree_synchronizer.cc b/ash/wm/scoped_layer_tree_synchronizer.cc
index a58eb48..dd2df23 100644
--- a/ash/wm/scoped_layer_tree_synchronizer.cc
+++ b/ash/wm/scoped_layer_tree_synchronizer.cc
@@ -368,6 +368,22 @@
   return layer_mask_info.rounded_corner_bounds();
 }
 
+gfx::Transform AccumulateTargetTransform(const ui::Layer* layer,
+                                         const gfx::Transform& transform) {
+  gfx::Transform translation;
+  translation.Translate(layer->bounds().x(), layer->bounds().y());
+
+  gfx::Transform accumulated_transform(transform);
+  accumulated_transform.PreConcat(translation);
+
+  const gfx::Transform& layer_transform = layer->GetTargetTransform();
+  if (!layer_transform.IsIdentity()) {
+    accumulated_transform.PreConcat(layer_transform);
+  }
+
+  return accumulated_transform;
+}
+
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -391,21 +407,22 @@
     return false;
   }
 
-  return SynchronizeLayerTreeRoundedCornersImpl(layer, reference_bounds);
+  gfx::Transform transform;
+  layer->GetTargetTransformRelativeTo(root_layer_, &transform);
+
+  return SynchronizeLayerTreeRoundedCornersImpl(layer, reference_bounds,
+                                                transform);
 }
 
 bool ScopedLayerTreeSynchronizerBase::SynchronizeLayerTreeRoundedCornersImpl(
     ui::Layer* layer,
-    const gfx::RRectF& reference_bounds) {
+    const gfx::RRectF& reference_bounds,
+    const gfx::Transform& transform) {
   CHECK(layer);
+  CHECK(transform.IsScaleOrTranslation());
 
   bool layer_altered = false;
   if (!layer->rounded_corner_radii().IsEmpty()) {
-    gfx::Transform transform;
-    layer->GetTargetTransformRelativeTo(root_layer_, &transform);
-
-    CHECK(transform.IsScaleOrTranslation());
-
     // Get the `layer` bounds in the `root_layer_` coordinate space.
     // `transform` accounts for layer offset from its parent.
     gfx::RRectF layer_rrectf(gfx::RectF(layer->bounds().size()),
@@ -467,8 +484,8 @@
 
   bool subtree_altered = false;
   for (ui::Layer* child : layer->children()) {
-    subtree_altered |=
-        SynchronizeLayerTreeRoundedCornersImpl(child, reference_bounds);
+    subtree_altered |= SynchronizeLayerTreeRoundedCornersImpl(
+        child, reference_bounds, AccumulateTargetTransform(child, transform));
   }
 
   return subtree_altered || layer_altered;
@@ -492,7 +509,7 @@
   }
 
   for (ui::Layer* child : layer->children()) {
-    RestoreLayerTree(child);
+    RestoreLayerTreeImpl(child);
   }
 }
 
diff --git a/ash/wm/scoped_layer_tree_synchronizer.h b/ash/wm/scoped_layer_tree_synchronizer.h
index 97caef7a..ae33b38 100644
--- a/ash/wm/scoped_layer_tree_synchronizer.h
+++ b/ash/wm/scoped_layer_tree_synchronizer.h
@@ -19,6 +19,7 @@
 namespace gfx {
 class RRectF;
 class RoundedCornersF;
+class Transform;
 }  // namespace gfx
 
 namespace ui {
@@ -58,9 +59,11 @@
   ui::Layer* root_layer() { return root_layer_; }
 
  private:
+  // `transform` is the relative target transform of layer to the `root_layer`.
   bool SynchronizeLayerTreeRoundedCornersImpl(
       ui::Layer* layer,
-      const gfx::RRectF& reference_bounds);
+      const gfx::RRectF& reference_bounds,
+      const gfx::Transform& transform);
 
   void RestoreLayerTreeImpl(ui::Layer* layer);
 
diff --git a/ash/wm/snap_group/snap_group_controller.cc b/ash/wm/snap_group/snap_group_controller.cc
index 3c113224..69288378 100644
--- a/ash/wm/snap_group/snap_group_controller.cc
+++ b/ash/wm/snap_group/snap_group_controller.cc
@@ -89,13 +89,6 @@
   snap_groups_.push_back(std::move(snap_group));
   snap_group_ptr->RefreshWindowBoundsInSnapGroup(/*on_snap_group_added=*/true);
 
-  // Notify observers after refreshing the window bounds since the divider
-  // position calculation in `GetEquivalentDividerPosition` relies on the window
-  // bounds.
-  for (Observer& observer : observers_) {
-    observer.OnSnapGroupCreated();
-  }
-
   return true;
 }
 
@@ -114,10 +107,6 @@
   window_to_snap_group_map_.erase(window2);
   snap_group->StopObservingWindows();
 
-  for (Observer& observer : observers_) {
-    observer.OnSnapGroupRemoved(snap_group);
-  }
-
   std::erase_if(snap_groups_, base::MatchesUniquePtr(snap_group));
 
   return true;
@@ -153,14 +142,6 @@
   return can_enter_overview_;
 }
 
-void SnapGroupController::AddObserver(Observer* observer) {
-  observers_.AddObserver(observer);
-}
-
-void SnapGroupController::RemoveObserver(Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
 void SnapGroupController::MinimizeTopMostSnapGroup() {
   auto* topmost_snap_group = GetTopmostSnapGroup();
   topmost_snap_group->MinimizeWindows();
diff --git a/ash/wm/snap_group/snap_group_controller.h b/ash/wm/snap_group/snap_group_controller.h
index 162d0ca..7f0e814 100644
--- a/ash/wm/snap_group/snap_group_controller.h
+++ b/ash/wm/snap_group/snap_group_controller.h
@@ -10,9 +10,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/wm/overview/overview_observer.h"
-#include "ash/wm/wm_metrics.h"
 #include "base/containers/flat_map.h"
-#include "base/observer_list.h"
 #include "ui/display/display_observer.h"
 
 namespace aura {
@@ -33,15 +31,6 @@
 class ASH_EXPORT SnapGroupController : public OverviewObserver,
                                        public display::DisplayObserver {
  public:
-  class Observer : public base::CheckedObserver {
-   public:
-    // Called to notify with the creation of snap group.
-    virtual void OnSnapGroupCreated() = 0;
-
-    // Called to notify the removal of `snap_group`.
-    virtual void OnSnapGroupRemoved(SnapGroup* snap_group) = 0;
-  };
-
   using SnapGroups = std::vector<std::unique_ptr<SnapGroup>>;
   using WindowToSnapGroupMap = base::flat_map<aura::Window*, SnapGroup*>;
 
@@ -82,9 +71,6 @@
   // clamshell.
   bool CanEnterOverview() const;
 
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
-
   // Minimizes the most recently used and unminimized snap groups.
   void MinimizeTopMostSnapGroup();
 
@@ -139,8 +125,6 @@
   // window is in a `SnapGroup` or not.
   WindowToSnapGroupMap window_to_snap_group_map_;
 
-  base::ObserverList<Observer> observers_;
-
   display::ScopedDisplayObserver display_observer_{this};
 
   // If false, overview will not be allowed to show on the other side of the
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index 16fe44b..79093ad 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -17,7 +17,6 @@
 #include "ash/wm/overview/overview_metrics.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "ash/wm/overview/overview_types.h"
-#include "ash/wm/snap_group/snap_group_controller.h"
 #include "ash/wm/splitview/layout_divider_controller.h"
 #include "ash/wm/splitview/split_view_divider.h"
 #include "ash/wm/splitview/split_view_types.h"
diff --git a/ash/wm/window_cycle/window_cycle_controller.cc b/ash/wm/window_cycle/window_cycle_controller.cc
index efc76cc..81763b4 100644
--- a/ash/wm/window_cycle/window_cycle_controller.cc
+++ b/ash/wm/window_cycle/window_cycle_controller.cc
@@ -21,6 +21,7 @@
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/screen_pinning_controller.h"
 #include "ash/wm/snap_group/snap_group.h"
+#include "ash/wm/snap_group/snap_group_controller.h"
 #include "ash/wm/window_cycle/window_cycle_event_filter.h"
 #include "ash/wm/window_cycle/window_cycle_list.h"
 #include "ash/wm/window_util.h"
@@ -413,8 +414,7 @@
       Shell::Get()->mru_window_tracker()->BuildWindowForCycleWithPipList(
           desks_mru_type);
 
-  SnapGroupController* snap_group_controller =
-      Shell::Get()->snap_group_controller();
+  SnapGroupController* snap_group_controller = SnapGroupController::Get();
   if (!snap_group_controller) {
     return window_list;
   }
diff --git a/ash/wm/window_cycle/window_cycle_controller.h b/ash/wm/window_cycle/window_cycle_controller.h
index 9ca49d73..13a1b5a7 100644
--- a/ash/wm/window_cycle/window_cycle_controller.h
+++ b/ash/wm/window_cycle/window_cycle_controller.h
@@ -19,11 +19,11 @@
 
 namespace aura {
 class Window;
-}
+}  // namespace aura
 
 namespace ui {
 class LocatedEvent;
-}
+}  // namespace ui
 
 namespace ash {
 
diff --git a/ash/wm/window_cycle/window_cycle_item_view.cc b/ash/wm/window_cycle/window_cycle_item_view.cc
index 835b99f0..72f164bd 100644
--- a/ash/wm/window_cycle/window_cycle_item_view.cc
+++ b/ash/wm/window_cycle/window_cycle_item_view.cc
@@ -230,16 +230,13 @@
 
 void GroupContainerCycleView::RefreshItemVisuals() {
   if (mini_views_.size() == 2u) {
-    const int corner_radius = window_util::GetMiniWindowRoundedCornerRadius();
-    mini_views_[0]->SetRoundedCornersRadius(gfx::RoundedCornersF(
-        /*upper_left=*/corner_radius,
-        /*upper_right=*/0, /*lower_right=*/0,
-        /*lower_left=*/corner_radius));
-    mini_views_[1]->SetRoundedCornersRadius(gfx::RoundedCornersF(
-        /*upper_left=*/0,
-        /*upper_right=*/corner_radius,
-        /*lower_right=*/corner_radius,
-        /*lower_left=*/0));
+    mini_views_[0]->SetRoundedCornersRadius(
+        window_util::GetMiniWindowRoundedCorners(
+            mini_views_[0]->source_window(), /*include_header_rounding=*/true));
+    mini_views_[1]->SetRoundedCornersRadius(
+        window_util::GetMiniWindowRoundedCorners(
+            mini_views_[1]->source_window(),
+            /*include_header_rounding=*/true));
   }
 
   for (ash::WindowCycleItemView* mini_view : mini_views_) {
diff --git a/ash/wm/window_restore/pine_constants.h b/ash/wm/window_restore/pine_constants.h
index 3f49b407..00b11ee 100644
--- a/ash/wm/window_restore/pine_constants.h
+++ b/ash/wm/window_restore/pine_constants.h
@@ -52,6 +52,13 @@
 inline constexpr ui::ColorId kIconBackgroundColor =
     cros_tokens::kCrosSysSystemOnBase;
 
+// Width of the preview container inside the pine dialog. Contents of the
+// preview can be either the items view or the screenshot.
+inline constexpr int kPreviewContainerWidth = 344;
+
+// Corner radius of the screenshot preview inside the pine dialog.
+inline constexpr int kScreenshotPreviewRadius = 12;
+
 }  // namespace ash::pine
 
 #endif  // ASH_WM_WINDOW_RESTORE_PINE_CONSTANTS_H_
diff --git a/ash/wm/window_restore/pine_contents_data.cc b/ash/wm/window_restore/pine_contents_data.cc
index fde583c..1934852b 100644
--- a/ash/wm/window_restore/pine_contents_data.cc
+++ b/ash/wm/window_restore/pine_contents_data.cc
@@ -10,17 +10,15 @@
 
 PineContentsData::~PineContentsData() = default;
 
-PineContentsData::AppInfo::AppInfo(const std::string& app_id)
-    : app_id(app_id) {}
+PineContentsData::AppInfo::AppInfo(const std::string& app_id,
+                                   const std::u16string& title)
+    : app_id(app_id), title(title) {}
 
 PineContentsData::AppInfo::AppInfo(const std::string& app_id,
-                                   const std::u16string& tab_title,
+                                   const std::u16string& title,
                                    const std::vector<GURL>& tab_urls,
                                    const size_t tab_count)
-    : app_id(app_id),
-      tab_title(tab_title),
-      tab_urls(tab_urls),
-      tab_count(tab_count) {}
+    : app_id(app_id), title(title), tab_urls(tab_urls), tab_count(tab_count) {}
 
 PineContentsData::AppInfo::AppInfo(const AppInfo&) = default;
 
diff --git a/ash/wm/window_restore/pine_contents_data.h b/ash/wm/window_restore/pine_contents_data.h
index c01913b..dea69ee 100644
--- a/ash/wm/window_restore/pine_contents_data.h
+++ b/ash/wm/window_restore/pine_contents_data.h
@@ -24,9 +24,9 @@
   ~PineContentsData();
 
   struct AppInfo {
-    explicit AppInfo(const std::string& id);
+    explicit AppInfo(const std::string& id, const std::u16string& title);
     AppInfo(const std::string& app_id,
-            const std::u16string& tab_title,
+            const std::u16string& title,
             const std::vector<GURL>& tab_urls,
             const size_t tab_count);
     AppInfo(const AppInfo&);
@@ -34,8 +34,11 @@
     // App id. Used to retrieve the app name and app icon from the app registry
     // cache.
     std::string app_id;
-    // Used for browser and PWAs. Shows a more descriptive title than "Chrome".
-    std::u16string tab_title;
+    // This title has two uses. If it is a browser, then it shows the active tab
+    // title, so that it is more descriptive than "Chrome". Otherwise, it shows
+    // a temporary title (last session's window title)  that will be overridden
+    // once we can fetch titles from the app service using `app_id`.
+    std::u16string title;
     // Used by browser only. Urls of up to 5 tabs including the active tab. Used
     // to retrieve favicons.
     std::vector<GURL> tab_urls;
diff --git a/ash/wm/window_restore/pine_contents_view.cc b/ash/wm/window_restore/pine_contents_view.cc
index e7fa64f59..177fca1 100644
--- a/ash/wm/window_restore/pine_contents_view.cc
+++ b/ash/wm/window_restore/pine_contents_view.cc
@@ -48,15 +48,9 @@
 // TODO(http://b/328459389): Update `SetFontList()` to use
 // `ash::TypographyProvider`.
 
-constexpr gfx::Size kItemsContainerPreferredSize(
-    320,
-    pine::kItemsContainerInsets.height() +
-        pine::kItemIconBackgroundPreferredSize.height() * pine::kMaxItems +
-        pine::kItemsContainerChildSpacing * (pine::kMaxItems - 1));
-
 constexpr int kButtonContainerChildSpacing = 10;
-constexpr int kContentsChildSpacing = 20;
-constexpr gfx::Insets kContentsInsets = gfx::Insets::VH(15, 15);
+constexpr int kContentsChildSpacing = 16;
+constexpr gfx::Insets kContentsInsets = gfx::Insets(20);
 constexpr int kContentsRounding = 20;
 constexpr int kContentsTitleFontSize = 22;
 constexpr int kContentsDescriptionFontSize = 14;
@@ -65,6 +59,16 @@
 constexpr int kContextMenuMaxWidth = 285;
 constexpr gfx::Insets kContextMenuLabelInsets = gfx::Insets::VH(0, 16);
 
+// Width of the actions container, which includes multiple buttons that users
+// can take actions to change their settings.
+constexpr int kActionsContainerWidth = 300;
+// Height of the container that holds the items view.
+constexpr int kItemsViewContainerHeight = 240;
+// Minimum height of the container that holds the screenshot.
+constexpr int kScreenshotContainerMinHeight = 214;
+// Minimum height of the screenshot itself.
+constexpr int kScreenshotMinHeight = 88;
+
 }  // namespace
 
 PineContentsView::PineContentsView() {
@@ -75,7 +79,7 @@
   SetOrientation(views::BoxLayout::Orientation::kHorizontal);
 
   views::View* spacer;
-  AddChildView(
+  auto* actions_container_view = AddChildView(
       // This box layout view is the container for the left hand side (in LTR)
       // of the contents view. It contains the title, buttons container and
       // settings button.
@@ -83,7 +87,6 @@
           .SetBetweenChildSpacing(kLeftContentsChildSpacing)
           .SetCrossAxisAlignment(views::BoxLayout::CrossAxisAlignment::kStart)
           .SetOrientation(views::BoxLayout::Orientation::kVertical)
-          .SetPreferredSize(kItemsContainerPreferredSize)
           .AddChildren(
               // Title.
               views::Builder<views::Label>()
@@ -145,41 +148,85 @@
   views::AsViewClass<views::BoxLayoutView>(spacer->parent())
       ->SetFlexForView(spacer, 1);
 
+  gfx::Size screenshot_size;
   const PineContentsData* pine_contents_data =
       Shell::Get()->pine_controller()->pine_contents_data();
   CHECK(pine_contents_data);
-  if (pine_contents_data->image.isNull()) {
-    items_container_view_ =
+  const bool should_show_items_view = pine_contents_data->image.isNull();
+  if (should_show_items_view) {
+    preview_container_view_ =
         AddChildView(std::make_unique<PineItemsContainerView>(
             pine_contents_data->apps_infos));
-    items_container_view_->SetPreferredSize(kItemsContainerPreferredSize);
+    preview_container_view_->SetPreferredSize(
+        gfx::Size(pine::kPreviewContainerWidth, kItemsViewContainerHeight));
   } else {
     const gfx::ImageSkia& pine_image = pine_contents_data->image;
-    const gfx::Size preview_size = pine_image.size();
+    screenshot_size = pine_image.size();
+    screenshot_size.set_height(
+        std::max(kScreenshotMinHeight, screenshot_size.height()));
 
+    views::View* image_view;
+    views::BoxLayoutView* icon_row_container;
     views::View* icon_row_spacer;
-    AddChildView(
-        views::Builder<views::View>()
-            .SetLayoutManager(std::make_unique<views::FillLayout>())
-            .SetPreferredSize(preview_size)
+    // This box layout is used to set the vertical space when the screenshot's
+    // height is smaller than `kScreenshotContainerMinHeight`. Thus the
+    // screenshot and the icon row can be centered inside the container.
+    preview_container_view_ = AddChildView(
+        views::Builder<views::BoxLayoutView>()
             .AddChildren(
-                views::Builder<views::ImageView>()
-                    .SetImage(pine_image)
-                    .SetImageSize(preview_size),
-                views::Builder<views::BoxLayoutView>()
-                    .SetOrientation(views::BoxLayout::Orientation::kVertical)
-                    .AddChildren(views::Builder<views::View>().CopyAddressTo(
-                        &icon_row_spacer)))
+                views::Builder<views::View>()
+                    .SetLayoutManager(std::make_unique<views::FillLayout>())
+                    .SetPreferredSize(screenshot_size)
+                    .AddChildren(
+                        views::Builder<views::ImageView>()
+                            .CopyAddressTo(&image_view)
+                            .SetPaintToLayer()
+                            .SetImage(pine_image)
+                            .SetImageSize(screenshot_size),
+                        views::Builder<views::BoxLayoutView>()
+                            .CopyAddressTo(&icon_row_container)
+                            .SetPaintToLayer()
+                            .SetOrientation(
+                                views::BoxLayout::Orientation::kVertical)
+                            .AddChildren(views::Builder<views::View>()
+                                             .CopyAddressTo(&icon_row_spacer))))
             .Build());
 
-    auto* icon_row_container =
-        views::AsViewClass<views::BoxLayoutView>(icon_row_spacer->parent());
+    image_view->layer()->SetFillsBoundsOpaquely(false);
+    image_view->layer()->SetRoundedCornerRadius(
+        gfx::RoundedCornersF(pine::kScreenshotPreviewRadius));
+    icon_row_container->layer()->SetFillsBoundsOpaquely(false);
+
     screenshot_icon_row_view_ = icon_row_container->AddChildView(
         std::make_unique<PineScreenshotIconRowView>(
             pine_contents_data->apps_infos));
     icon_row_container->SetFlexForView(icon_row_spacer, 1);
   }
 
+  // The height of the pine dialog is dynamic, depending on the height of the
+  // screenshot. For the screenshot, its width is fixed as
+  // `kPreviewContainerWidth` while its height is calculated based on the
+  // display's aspect ratio.
+  const int screenshot_height = screenshot_size.height();
+  const int pine_contents_height =
+      should_show_items_view
+          ? kItemsViewContainerHeight
+          : std::max(kScreenshotContainerMinHeight, screenshot_height);
+  actions_container_view->SetPreferredSize(
+      gfx::Size(kActionsContainerWidth, pine_contents_height));
+
+  // Set the screenshto preview container vertical margin based on the height of
+  // the screenshot.
+  if (!should_show_items_view &&
+      screenshot_height < kScreenshotContainerMinHeight) {
+    const int vertical_gap = kScreenshotContainerMinHeight - screenshot_height;
+    const int bottom_inset = vertical_gap / 2;
+    const int top_inset =
+        vertical_gap % 2 == 1 ? bottom_inset + 1 : bottom_inset;
+    preview_container_view_->SetInsideBorderInsets(
+        gfx::Insets::TLBR(top_inset, 0, bottom_inset, 0));
+  }
+
   // Add a highlight border to match the Quick Settings menu, i.e.,
   // `TrayBubbleView`.
   SetBorder(std::make_unique<views::HighlightBorder>(
diff --git a/ash/wm/window_restore/pine_contents_view.h b/ash/wm/window_restore/pine_contents_view.h
index a9dbb64..afd3da14 100644
--- a/ash/wm/window_restore/pine_contents_view.h
+++ b/ash/wm/window_restore/pine_contents_view.h
@@ -24,7 +24,6 @@
 
 class PillButton;
 class PineContextMenuModel;
-class PineItemsContainerView;
 class PineScreenshotIconRowView;
 
 class ASH_EXPORT PineContentsView : public views::BoxLayoutView {
@@ -61,7 +60,7 @@
   // The menu runner that is responsible for the context menu.
   std::unique_ptr<views::MenuRunner> menu_runner_;
 
-  raw_ptr<PineItemsContainerView> items_container_view_ = nullptr;
+  raw_ptr<BoxLayoutView> preview_container_view_ = nullptr;
   raw_ptr<PineScreenshotIconRowView> screenshot_icon_row_view_ = nullptr;
 
   raw_ptr<PillButton> restore_button_for_testing_ = nullptr;
diff --git a/ash/wm/window_restore/pine_context_menu_model_unittest.cc b/ash/wm/window_restore/pine_context_menu_model_unittest.cc
index 4827e70e..6914100 100644
--- a/ash/wm/window_restore/pine_context_menu_model_unittest.cc
+++ b/ash/wm/window_restore/pine_context_menu_model_unittest.cc
@@ -18,6 +18,7 @@
 #include "ash/wm/window_restore/pine_contents_view.h"
 #include "ash/wm/window_restore/pine_context_menu_model.h"
 #include "ash/wm/window_restore/pine_controller.h"
+#include "ash/wm/window_restore/pine_test_base.h"
 #include "ash/wm/window_restore/window_restore_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -30,54 +31,16 @@
 
 namespace {
 
-constexpr char kTestUserEmail[] = "testuser@pine";
-
 constexpr size_t kMenuItemCount = 5u;
 
 }  // namespace
 
-class PineContextMenuModelTest : public AshTestBase {
+class PineContextMenuModelTest : public PineTestBase {
  public:
-  PineContextMenuModelTest() {
-    switches::SetIgnoreForestSecretKeyForTest(true);
-  }
+  PineContextMenuModelTest() = default;
   PineContextMenuModelTest(const PineContextMenuModelTest&) = delete;
   PineContextMenuModelTest& operator=(const PineContextMenuModelTest) = delete;
-  ~PineContextMenuModelTest() override {
-    switches::SetIgnoreForestSecretKeyForTest(false);
-  }
-
-  // AshTestBase:
-  void SetUp() override {
-    AshTestBase::SetUp();
-
-    TestSessionControllerClient* session_controller =
-        GetSessionControllerClient();
-    session_controller->Reset();
-
-    // Inject our own PrefService as the restore preference is normally
-    // registered in chrome/browser/ash/ and is not registered in ash unit
-    // tests.
-    auto test_prefs = std::make_unique<TestingPrefServiceSimple>();
-    RegisterUserProfilePrefs(test_prefs.get()->registry(), /*country=*/"",
-                             /*for_test=*/true);
-    // Note: normally, this pref is registered with the
-    // `user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF` flag.
-    test_prefs.get()->registry()->RegisterIntegerPref(
-        prefs::kRestoreAppsAndPagesPrefName,
-        static_cast<int>(full_restore::RestoreOption::kAskEveryTime));
-
-    session_controller->AddUserSession(kTestUserEmail,
-                                       user_manager::UserType::kRegular,
-                                       /*provide_pref_service=*/false);
-    session_controller->SetUserPrefService(
-        AccountId::FromUserEmail(kTestUserEmail), std::move(test_prefs));
-
-    // Switch to the test user and simulate login.
-    session_controller->SwitchActiveUser(
-        AccountId::FromUserEmail(kTestUserEmail));
-    session_controller->SetSessionState(session_manager::SessionState::ACTIVE);
-  }
+  ~PineContextMenuModelTest() override = default;
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_{features::kForestFeature};
diff --git a/ash/wm/window_restore/pine_controller.cc b/ash/wm/window_restore/pine_controller.cc
index a207261..3c974c2 100644
--- a/ash/wm/window_restore/pine_controller.cc
+++ b/ash/wm/window_restore/pine_controller.cc
@@ -5,11 +5,13 @@
 #include "ash/wm/window_restore/pine_controller.h"
 
 #include "ash/birch/birch_model.h"
+#include "ash/constants/app_types.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/constants/notifier_catalogs.h"
 #include "ash/display/screen_ash.h"
 #include "ash/public/cpp/image_util.h"
+#include "ash/public/cpp/resources/grit/ash_public_unscaled_resources.h"
 #include "ash/public/cpp/system/anchored_nudge_data.h"
 #include "ash/public/cpp/system/anchored_nudge_manager.h"
 #include "ash/public/cpp/window_properties.h"
@@ -17,18 +19,22 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_id.h"
+#include "ash/style/dark_light_mode_controller_impl.h"
 #include "ash/style/system_dialog_delegate_view.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/overview/overview_controller.h"
-#include "ash/wm/overview/overview_utils.h"
+#include "ash/wm/overview/overview_grid.h"
+#include "ash/wm/overview/overview_session.h"
 #include "ash/wm/window_restore/pine_contents_data.h"
 #include "ash/wm/window_restore/window_restore_util.h"
+#include "ash/wm/window_util.h"
 #include "base/command_line.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/metrics/histogram_functions.h"
 #include "chromeos/ui/base/display_util.h"
 #include "components/prefs/pref_service.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/compositor/layer.h"
@@ -45,9 +51,6 @@
 constexpr int kNudgeMaxShownCount = 3;
 constexpr base::TimeDelta kNudgeTimeBetweenShown = base::Hours(24);
 
-// If set to true, ignores the prefs show limit for testing.
-bool g_ignore_prefs_for_testing = false;
-
 // Records the UMA metrics for the pine screenshot taken on the last shutdown.
 // Resets the prefs used to store the metrics across shutdowns.
 void RecordPineScreenshotMetrics(PrefService* local_state) {
@@ -95,9 +98,6 @@
 // Returns true if this is the first time login and we should show the pine
 // onboarding message.
 bool ShouldShowPineOnboarding() {
-  if (g_ignore_prefs_for_testing) {
-    return true;
-  }
   PrefService* prefs = GetActivePrefService();
   return prefs && prefs->GetBoolean(prefs::kShouldShowPineOnboarding);
 }
@@ -106,12 +106,18 @@
 
 PineController::PineController() {
   Shell::Get()->overview_controller()->AddObserver(this);
+
+  activation_change_observation_.Observe(Shell::Get()->activation_client());
 }
 
 PineController::~PineController() {
   Shell::Get()->overview_controller()->RemoveObserver(this);
 }
 
+bool PineController::ShouldShowPineDialog() const {
+  return !!pine_contents_data_ && !pine_contents_data_->apps_infos.empty();
+}
+
 void PineController::MaybeShowPineOnboardingMessage(bool restore_on) {
   if (onboarding_widget_) {
     return;
@@ -184,30 +190,28 @@
 
   // Chrome.
   data->apps_infos.emplace_back(
-      "mgndgikekgjfcpckkfioiadnlibdjbkf", /*tab_title=*/u"Chrome",
+      "mgndgikekgjfcpckkfioiadnlibdjbkf", /*tab_title=*/u"Reddit",
       std::vector<GURL>{
           GURL("https://www.cnn.com/"), GURL("https://www.reddit.com/"),
           GURL("https://www.youtube.com/"), GURL("https://www.waymo.com/"),
           GURL("https://www.google.com/")},
       /*tab_count=*/10u);
-  // Meet (PWA).
-  data->apps_infos.emplace_back("kjgfgldnnfoeklkmfkjfagphfepbbdan");
-  // Camera.
-  data->apps_infos.emplace_back("njfbnohfdkmbmnjapinfcopialeghnmh");
-  // Settings.
-  data->apps_infos.emplace_back("odknhmnlageboeamepcngndbggdpaobj");
-  // Files.
-  data->apps_infos.emplace_back("fkiggjmkendpmbegkagpmagjepfkpmeb");
-  // Calculator.
-  data->apps_infos.emplace_back("oabkinaljpjeilageghcdlnekhphhphl");
-  // Chrome.
+  // PWA.
+  data->apps_infos.emplace_back("kjgfgldnnfoeklkmfkjfagphfepbbdan", u"Meet");
+
+  // SWA.
+  data->apps_infos.emplace_back("njfbnohfdkmbmnjapinfcopialeghnmh", u"Camera");
+  data->apps_infos.emplace_back("odknhmnlageboeamepcngndbggdpaobj",
+                                u"Settings");
+  data->apps_infos.emplace_back("fkiggjmkendpmbegkagpmagjepfkpmeb", u"Files");
+  data->apps_infos.emplace_back("oabkinaljpjeilageghcdlnekhphhphl",
+                                u"Calculator");
+
   data->apps_infos.emplace_back(
       "mgndgikekgjfcpckkfioiadnlibdjbkf", /*tab_title=*/u"Maps",
       std::vector<GURL>{GURL("https://www.google.com/maps/")},
       /*tab_count=*/1);
-  // Files.
-  data->apps_infos.emplace_back("fkiggjmkendpmbegkagpmagjepfkpmeb");
-  // Chrome.
+  data->apps_infos.emplace_back("fkiggjmkendpmbegkagpmagjepfkpmeb", u"Files");
   data->apps_infos.emplace_back(
       "mgndgikekgjfcpckkfioiadnlibdjbkf", /*tab_title=*/u"Twitter",
       std::vector<GURL>{GURL("https://www.twitter.com/"),
@@ -244,6 +248,10 @@
     return;
   }
 
+  if (!pine_contents_data_) {
+    return;
+  }
+
   RecordPineScreenshotMetrics(Shell::Get()->local_state());
   image_util::DecodeImageFile(
       base::BindOnce(&PineController::OnPineImageDecoded,
@@ -257,12 +265,24 @@
                                          OverviewEnterExitType::kNormal);
 }
 
+void PineController::OnOverviewModeEnding(OverviewSession* overview_session) {
+  in_pine_ = false;
+  for (const auto& grid : overview_session->grid_list()) {
+    if (grid->pine_widget()) {
+      in_pine_ = true;
+      break;
+    }
+  }
+}
+
 void PineController::OnOverviewModeEndingAnimationComplete(bool canceled) {
-  if (canceled || !features::IsForestFeatureEnabled()) {
+  // If `canceled` is true, overview was reentered before the exit animations
+  // were finished. `in_pine_` will be reset the next time overview ends.
+  if (canceled || !in_pine_ || !features::IsForestFeatureEnabled()) {
     return;
   }
 
-  // TODO(sophiewen): Check if pine was in session?
+  in_pine_ = false;
 
   PrefService* prefs = GetActivePrefService();
   if (!prefs) {
@@ -277,7 +297,7 @@
 
   // Nudge has been shown within the last 24 hours already.
   base::Time now = base::Time::Now();
-  if ((now - prefs->GetTime(prefs::kPineNudgeLastShown)) <
+  if (now - prefs->GetTime(prefs::kPineNudgeLastShown) <
       kNudgeTimeBetweenShown) {
     return;
   }
@@ -285,15 +305,26 @@
   AnchoredNudgeData nudge_data(
       kEducationNudgeId, NudgeCatalogName::kPineEducationNudge,
       l10n_util::GetStringUTF16(IDS_ASH_PINE_EDUCATION_NUDGE));
+  nudge_data.image_model =
+      ui::ResourceBundle::GetSharedInstance().GetThemedLottieImageNamed(
+          DarkLightModeControllerImpl::Get()->IsDarkModeEnabled()
+              ? IDR_PINE_NUDGE_IMAGE_DM
+              : IDR_PINE_NUDGE_IMAGE_LM);
+  nudge_data.fill_image_size = true;
   AnchoredNudgeManager::Get()->Show(nudge_data);
 
   prefs->SetInteger(prefs::kPineNudgeShownCount, shown_count + 1);
   prefs->SetTime(prefs::kPineNudgeLastShown, now);
 }
 
-// static
-void PineController::SetIgnorePrefsForTesting(bool val) {
-  g_ignore_prefs_for_testing = val;
+void PineController::OnWindowActivated(ActivationReason reason,
+                                       aura::Window* gained_active,
+                                       aura::Window* lost_active) {
+  if (gained_active && window_util::IsWindowUserPositionable(gained_active) &&
+      static_cast<AppType>(gained_active->GetProperty(
+          aura::client::kAppType)) != AppType::NON_APP) {
+    pine_contents_data_.reset();
+  }
 }
 
 void PineController::OnPineImageDecoded(const gfx::ImageSkia& pine_image) {
@@ -331,11 +362,6 @@
 }
 
 void PineController::OnOnboardingAcceptPressed(bool restore_on) {
-  // TODO(sophiewen): Update the pref when UX decide what to do.
-  if (!restore_on) {
-    // We only record the action taken if the user had Restore off.
-    base::UmaHistogramBoolean(kPineOnboardingHistogram, true);
-  }
   // Wait until the onboarding widget is destroyed before starting overview,
   // since we disallow entering overview while system modal windows are open.
   // Use a weak ptr since `this` can be deleted before we close all windows.
@@ -347,6 +373,16 @@
             }
           },
           weak_ptr_factory_.GetWeakPtr()));
+  if (restore_on) {
+    return;
+  }
+  // The onboarding dialog would only be shown if `GetActivePrefService()` is
+  // not null.
+  GetActivePrefService()->SetInteger(
+      prefs::kRestoreAppsAndPagesPrefName,
+      static_cast<int>(full_restore::RestoreOption::kAskEveryTime));
+  // We only record the action taken if the user had Restore off.
+  base::UmaHistogramBoolean(kPineOnboardingHistogram, true);
 }
 
 void PineController::OnOnboardingCancelPressed() {
diff --git a/ash/wm/window_restore/pine_controller.h b/ash/wm/window_restore/pine_controller.h
index f22ac3c4..e316acf 100644
--- a/ash/wm/window_restore/pine_controller.h
+++ b/ash/wm/window_restore/pine_controller.h
@@ -10,6 +10,8 @@
 #include "base/memory/weak_ptr.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/widget/unique_widget_ptr.h"
+#include "ui/wm/public/activation_change_observer.h"
+#include "ui/wm/public/activation_client.h"
 
 namespace ash {
 
@@ -22,7 +24,8 @@
 
 // Controls showing the pine dialog. Receives data from the full restore
 // service.
-class ASH_EXPORT PineController : public OverviewObserver {
+class ASH_EXPORT PineController : public OverviewObserver,
+                                  public wm::ActivationChangeObserver {
  public:
   PineController();
   PineController(const PineController&) = delete;
@@ -34,6 +37,10 @@
     return pine_contents_data_.get();
   }
 
+  // Returns true if `this` contains `pine_contents_data_` with non-empty app
+  // restore data.
+  bool ShouldShowPineDialog() const;
+
   // Shows the onboarding message. If `restore_on` is true, only the
   // "Continue" button will be shown. Otherwise shows both buttons.
   void MaybeShowPineOnboardingMessage(bool restore_on);
@@ -57,14 +64,18 @@
   void MaybeEndPineOverviewSession();
 
   // OverviewObserver:
+  void OnOverviewModeEnding(OverviewSession* overview_session) override;
   void OnOverviewModeEndingAnimationComplete(bool canceled) override;
 
+  // wm::ActivationChangeObserver:
+  void OnWindowActivated(ActivationReason reason,
+                         aura::Window* gained_active,
+                         aura::Window* lost_active) override;
+
  private:
   friend class PineTestApi;
   FRIEND_TEST_ALL_PREFIXES(PineTest, OnboardingMetrics);
 
-  static void SetIgnorePrefsForTesting(bool val);
-
   // Callback function for when the pine image is finished decoding.
   void OnPineImageDecoded(const gfx::ImageSkia& pine_image);
 
@@ -75,6 +86,10 @@
   void OnOnboardingAcceptPressed(bool restore_on);
   void OnOnboardingCancelPressed();
 
+  // True if overview was in pine session, up until the overview animation is
+  // ended.
+  bool in_pine_ = false;
+
   // The first-time experience onboarding dialog.
   views::UniqueWidgetPtr onboarding_widget_;
 
@@ -84,6 +99,9 @@
   // TODO(sammiequon): Delete this object when an app window is created.
   std::unique_ptr<PineContentsData> pine_contents_data_;
 
+  base::ScopedObservation<wm::ActivationClient, wm::ActivationChangeObserver>
+      activation_change_observation_{this};
+
   base::WeakPtrFactory<PineController> weak_ptr_factory_{this};
 };
 
diff --git a/ash/wm/window_restore/pine_item_view.cc b/ash/wm/window_restore/pine_item_view.cc
index 64bd2a3..e987bb5 100644
--- a/ash/wm/window_restore/pine_item_view.cc
+++ b/ash/wm/window_restore/pine_item_view.cc
@@ -5,6 +5,7 @@
 #include "ash/wm/window_restore/pine_item_view.h"
 
 #include "ash/public/cpp/saved_desk_delegate.h"
+#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/style/typography.h"
 #include "ash/wm/window_restore/pine_constants.h"
@@ -116,34 +117,36 @@
       break;
     }
 
+    needs_layout = true;
+
+    views::Builder<views::ImageView> builder;
+    builder
+        // TODO(b/322360273): The border is temporary for more
+        // contrast until specs are ready.
+        .SetBorder(views::CreateRoundedRectBorder(
+            /*thickness=*/1,
+            /*corner_radius=*/kFaviconPreferredSize.width(), SK_ColorBLACK))
+        .SetImageSize(kFaviconPreferredSize);
+
+    // If the image data is null, use a default cube icon instead.
     const gfx::ImageSkia& favicon = favicons[i];
-    // TODO(b/329454790): If favicon is null, use default icon instead.
     if (favicon.isNull()) {
-      continue;
+      builder
+          .SetImage(ui::ImageModel::FromVectorIcon(
+              kDefaultAppIcon, cros_tokens::kCrosSysOnPrimary))
+          .SetBackground(views::CreateThemedRoundedRectBackground(
+              cros_tokens::kCrosSysPrimary, kFaviconPreferredSize.width()));
+    } else {
+      builder.SetImage(gfx::ImageSkiaOperations::CreateResizedImage(
+          favicon, skia::ImageOperations::RESIZE_BEST, kFaviconPreferredSize));
     }
 
-    needs_layout = true;
-    favicon_container_view_->AddChildView(
-        views::Builder<views::ImageView>()
-            // TODO(b/322360273): The border is temporary for more
-            // contrast until specs are ready.
-            .SetBorder(views::CreateRoundedRectBorder(
-                /*thickness=*/1,
-                /*corner_radius=*/kFaviconPreferredSize.width(), SK_ColorBLACK))
-            .SetImageSize(kFaviconPreferredSize)
-            .SetImage(gfx::ImageSkiaOperations::CreateResizedImage(
-                favicon, skia::ImageOperations::RESIZE_BEST,
-                kFaviconPreferredSize))
-            .Build());
+    favicon_container_view_->AddChildView(std::move(builder).Build());
   }
 
   // Insert a count of the overflow tabs that could not be individually
   // displayed.
   if (tab_count_ > kTabMaxElements) {
-    // TODO(b/329454790): Remove when default icon is added, as this should
-    // already be marked true.
-    needs_layout = true;
-
     views::Label* count_label;
     favicon_container_view_->AddChildView(
         views::Builder<views::Label>()
diff --git a/ash/wm/window_restore/pine_items_container_view.cc b/ash/wm/window_restore/pine_items_container_view.cc
index 84984f8..509ac0c 100644
--- a/ash/wm/window_restore/pine_items_container_view.cc
+++ b/ash/wm/window_restore/pine_items_container_view.cc
@@ -9,6 +9,7 @@
 #include "ash/wm/window_restore/pine_constants.h"
 #include "ash/wm/window_restore/pine_item_view.h"
 #include "ash/wm/window_restore/pine_items_overflow_view.h"
+#include "ash/wm/window_restore/window_restore_util.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
 #include "ui/chromeos/styles/cros_tokens_color_mappings.h"
@@ -38,7 +39,8 @@
   SetMainAxisAlignment(views::BoxLayout::MainAxisAlignment::kStart);
   SetOrientation(views::BoxLayout::Orientation::kVertical);
 
-  // TODO(sammiequon): Handle case where the app is not ready or installed.
+  // TODO(http://b/328830102): Handle case where the app is not ready or
+  // installed.
   apps::AppRegistryCache* cache =
       apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(
           Shell::Get()->session_controller()->GetActiveAccountId());
@@ -54,13 +56,17 @@
       break;
     }
 
-    std::u16string title = app_info.tab_title;
-    // `cache` might be null in a test environment. In that case, we will
-    // use an empty title.
-    if (cache && title.empty()) {
+    // `title` will be the window title from the previous session stored in the
+    // full restore file. The title fetched from the app service would more
+    // accurate, but the app might not be installed yet. Browsers are always
+    // installed and `title` will be the active tab title fetched from session
+    // restore. `cache` might be null in a test environment.
+    // TODO(http://b/328830102): Title should be updated once app is installed.
+    std::u16string title = app_info.title;
+    if (cache && !IsBrowserAppId(app_info.app_id)) {
       cache->ForOneApp(app_info.app_id,
                        [&title](const apps::AppUpdate& update) {
-                         title = base::ASCIIToUTF16(update.Name());
+                         title = base::UTF8ToUTF16(update.Name());
                        });
     }
 
diff --git a/ash/wm/window_restore/pine_screenshot_icon_row_view.cc b/ash/wm/window_restore/pine_screenshot_icon_row_view.cc
index 150d4ce..1d67e8f 100644
--- a/ash/wm/window_restore/pine_screenshot_icon_row_view.cc
+++ b/ash/wm/window_restore/pine_screenshot_icon_row_view.cc
@@ -22,10 +22,9 @@
 namespace {
 
 // Constants for the icon row inside the screenshot preview.
-constexpr int kIconRowRadius = 12;
 constexpr int kIconRowChildSpacing = 4;
 constexpr gfx::Insets kIconRowInsets =
-    gfx::Insets::TLBR(kIconRowRadius + 4, 4, 4, 4);
+    gfx::Insets::TLBR(pine::kScreenshotPreviewRadius + 4, 4, 4, 4);
 constexpr int kIconRowIconSize = 20;
 constexpr int kIconRowHeight =
     kIconRowIconSize + kIconRowInsets.top() + kIconRowInsets.bottom();
@@ -44,9 +43,10 @@
   const int elements_size = static_cast<int>(apps_infos.size());
   const int child_num =
       std::min(elements_size, pine::kScreenshotIconRowMaxElements);
-  const int row_width =
-      child_num * kIconRowIconSize + (child_num - 1) * kIconRowChildSpacing +
-      kIconRowInsets.left() + kIconRowInsets.right() + kIconRowRadius;
+  const int row_width = child_num * kIconRowIconSize +
+                        (child_num - 1) * kIconRowChildSpacing +
+                        kIconRowInsets.left() + kIconRowInsets.right() +
+                        pine::kScreenshotPreviewRadius;
   SetPreferredSize(gfx::Size(row_width, kIconRowHeight));
 
   const bool exceed_max_elements =
@@ -106,11 +106,11 @@
   const auto bottom_left = SkPoint::Make(0.f, kIconRowHeight);
   const auto bottom_right = SkPoint::Make(width, height);
 
-  const int cutout_curve1_end_x = kIconRowRadius;
-  const int cutout_curve1_end_y = kIconRowRadius;
+  const int cutout_curve1_end_x = pine::kScreenshotPreviewRadius;
+  const int cutout_curve1_end_y = pine::kScreenshotPreviewRadius;
 
-  const int cutout_curve2_end_x = width - kIconRowRadius;
-  const int cutout_curve2_end_y = 2 * kIconRowRadius;
+  const int cutout_curve2_end_x = width - pine::kScreenshotPreviewRadius;
+  const int cutout_curve2_end_y = 2 * pine::kScreenshotPreviewRadius;
 
   auto clip_path =
       SkPathBuilder()
@@ -120,19 +120,19 @@
           // connecting it to the top-right rounded corner.
           .arcTo(SkPoint::Make(0, cutout_curve1_end_y),
                  SkPoint::Make(cutout_curve1_end_x, cutout_curve1_end_y),
-                 kIconRowRadius)
+                 pine::kScreenshotPreviewRadius)
           // Draw the top-right rounded corner and a vertical line connecting
           // it to the bottom-right concave arc.
           .arcTo(SkPoint::Make(cutout_curve2_end_x, cutout_curve1_end_y),
                  SkPoint::Make(cutout_curve2_end_x, cutout_curve2_end_y),
-                 kIconRowRadius)
+                 pine::kScreenshotPreviewRadius)
           // Draw the bottom-right concave arc and a horizontal line
           // connecting it to the bottom-left rounded corner.
           .arcTo(SkPoint::Make(cutout_curve2_end_x, kIconRowHeight),
-                 bottom_right, kIconRowRadius)
+                 bottom_right, pine::kScreenshotPreviewRadius)
           // Draw the bottom-left rounded corner and the vertical line
           // connecting it to the top-left point.
-          .arcTo(bottom_left, top_left, kIconRowRadius)
+          .arcTo(bottom_left, top_left, pine::kScreenshotPreviewRadius)
           .close()
           .detach();
   SetClipPath(clip_path);
diff --git a/ash/wm/window_restore/pine_test_api.cc b/ash/wm/window_restore/pine_test_api.cc
index ae7d3fa..31d0451 100644
--- a/ash/wm/window_restore/pine_test_api.cc
+++ b/ash/wm/window_restore/pine_test_api.cc
@@ -32,6 +32,12 @@
 
 PineTestApi::~PineTestApi() = default;
 
+void PineTestApi::SetPineContentsDataForTesting(
+    std::unique_ptr<PineContentsData> pine_contents_data) {
+  Shell::Get()->pine_controller()->pine_contents_data_ =
+      std::move(pine_contents_data);
+}
+
 SystemDialogDelegateView* PineTestApi::GetOnboardingDialog() {
   auto* onboarding_widget =
       Shell::Get()->pine_controller()->onboarding_widget_.get();
diff --git a/ash/wm/window_restore/pine_test_api.h b/ash/wm/window_restore/pine_test_api.h
index e5e5c81..00da20b 100644
--- a/ash/wm/window_restore/pine_test_api.h
+++ b/ash/wm/window_restore/pine_test_api.h
@@ -5,6 +5,8 @@
 #ifndef ASH_WM_WINDOW_RESTORE_PINE_TEST_API_H_
 #define ASH_WM_WINDOW_RESTORE_PINE_TEST_API_H_
 
+#include <memory>
+
 #include "ash/wm/window_restore/pine_contents_view.h"
 #include "ash/wm/window_restore/pine_item_view.h"
 #include "ash/wm/window_restore/pine_items_container_view.h"
@@ -30,14 +32,15 @@
   const PillButton* cancel_button() const {
     return pine_contents_view_->cancel_button_for_testing_;
   }
-  const PineItemsContainerView* items_container_view() const {
-    return pine_contents_view_->items_container_view_;
+  const views::BoxLayoutView* preview_container_view() const {
+    return pine_contents_view_->preview_container_view_;
   }
   const PineScreenshotIconRowView* screenshot_icon_row_view() const {
     return pine_contents_view_->screenshot_icon_row_view_;
   }
   const PineItemsOverflowView* overflow_view() const {
-    return pine_contents_view_->items_container_view_
+    return static_cast<PineItemsContainerView*>(
+               pine_contents_view_->preview_container_view_)
         ->overflow_view_for_testing_;
   }
 
@@ -90,6 +93,9 @@
   PineTestApi& operator=(const PineTestApi&) = delete;
   ~PineTestApi();
 
+  void SetPineContentsDataForTesting(
+      std::unique_ptr<PineContentsData> pine_contents_data);
+
   SystemDialogDelegateView* GetOnboardingDialog();
 };
 
diff --git a/ash/wm/window_restore/pine_test_base.cc b/ash/wm/window_restore/pine_test_base.cc
new file mode 100644
index 0000000..574d4833
--- /dev/null
+++ b/ash/wm/window_restore/pine_test_base.cc
@@ -0,0 +1,67 @@
+// 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 "ash/wm/window_restore/pine_test_base.h"
+
+#include "ash/constants/ash_pref_names.h"
+#include "ash/constants/ash_switches.h"
+#include "ash/public/cpp/ash_prefs.h"
+#include "ash/wm/window_restore/window_restore_util.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+
+namespace ash {
+
+namespace {
+
+constexpr char kTestUserEmail[] = "testuser@pine";
+
+}  // namespace
+
+PineTestBase::PineTestBase() {
+  switches::SetIgnoreForestSecretKeyForTest(true);
+}
+
+PineTestBase::~PineTestBase() {
+  switches::SetIgnoreForestSecretKeyForTest(false);
+}
+
+// AshTestBase:
+void PineTestBase::SetUp() {
+  AshTestBase::SetUp();
+
+  TestSessionControllerClient* session_controller =
+      GetSessionControllerClient();
+  session_controller->Reset();
+
+  // Inject our own PrefService as the restore preference is normally
+  // registered in chrome/browser/ash/ and is not registered in ash unit
+  // tests.
+  auto test_prefs = std::make_unique<TestingPrefServiceSimple>();
+  RegisterUserProfilePrefs(test_prefs.get()->registry(), /*country=*/"",
+                           /*for_test=*/true);
+  // Note: normally, this pref is registered with the
+  // `user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF` flag.
+  test_prefs.get()->registry()->RegisterIntegerPref(
+      prefs::kRestoreAppsAndPagesPrefName,
+      static_cast<int>(full_restore::RestoreOption::kAskEveryTime));
+
+  session_controller->AddUserSession(kTestUserEmail,
+                                     user_manager::UserType::kRegular,
+                                     /*provide_pref_service=*/false);
+  session_controller->SetUserPrefService(
+      AccountId::FromUserEmail(kTestUserEmail), std::move(test_prefs));
+
+  // Switch to the test user and simulate login.
+  session_controller->SwitchActiveUser(
+      AccountId::FromUserEmail(kTestUserEmail));
+  session_controller->SetSessionState(session_manager::SessionState::ACTIVE);
+}
+
+PrefService* PineTestBase::GetTestPrefService() {
+  return GetSessionControllerClient()->GetUserPrefService(
+      AccountId::FromUserEmail(kTestUserEmail));
+}
+
+}  // namespace ash
diff --git a/ash/wm/window_restore/pine_test_base.h b/ash/wm/window_restore/pine_test_base.h
new file mode 100644
index 0000000..bb2f9d8
--- /dev/null
+++ b/ash/wm/window_restore/pine_test_base.h
@@ -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.
+
+#ifndef ASH_WM_WINDOW_RESTORE_PINE_TEST_BASE_H_
+#define ASH_WM_WINDOW_RESTORE_PINE_TEST_BASE_H_
+
+#include "ash/test/ash_test_base.h"
+
+namespace ash {
+
+class PineTestBase : public AshTestBase {
+ public:
+  PineTestBase();
+  PineTestBase(const PineTestBase&) = delete;
+  PineTestBase& operator=(const PineTestBase&) = delete;
+  ~PineTestBase() override;
+
+  // AshTestBase:
+  void SetUp() override;
+
+  PrefService* GetTestPrefService();
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_WINDOW_RESTORE_PINE_TEST_BASE_H_
diff --git a/ash/wm/window_restore/pine_unittest.cc b/ash/wm/window_restore/pine_unittest.cc
index 2bd5162..c0f78da 100644
--- a/ash/wm/window_restore/pine_unittest.cc
+++ b/ash/wm/window_restore/pine_unittest.cc
@@ -34,6 +34,7 @@
 #include "ash/wm/window_restore/pine_items_overflow_view.h"
 #include "ash/wm/window_restore/pine_screenshot_icon_row_view.h"
 #include "ash/wm/window_restore/pine_test_api.h"
+#include "ash/wm/window_restore/pine_test_base.h"
 #include "ash/wm/window_restore/window_restore_util.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -48,12 +49,12 @@
 
 namespace ash {
 
-class PineTest : public AshTestBase {
+class PineTest : public PineTestBase {
  public:
-  PineTest() { switches::SetIgnoreForestSecretKeyForTest(true); }
+  PineTest() = default;
   PineTest(const PineTest&) = delete;
   PineTest& operator=(const PineTest&) = delete;
-  ~PineTest() override { switches::SetIgnoreForestSecretKeyForTest(false); }
+  ~PineTest() override = default;
 
   void StartPineOverviewSession(std::unique_ptr<PineContentsData> data) {
     Shell::Get()->pine_controller()->MaybeStartPineOverviewSession(
@@ -74,8 +75,7 @@
         views::AsViewClass<PineContentsView>(pine_widget->GetContentsView());
     ASSERT_TRUE(contents_view);
     auto contents_view_test_api = PineContentsViewTestApi(contents_view);
-    ASSERT_TRUE(contents_view_test_api.items_container_view() ||
-                contents_view_test_api.screenshot_icon_row_view());
+    ASSERT_TRUE(contents_view_test_api.preview_container_view());
   }
 
   const PineContentsView* GetContentsView() const {
@@ -99,7 +99,7 @@
   std::unique_ptr<PineContentsData> MakeTestAppIds(int n) {
     auto data = std::make_unique<PineContentsData>();
     for (int i = 0; i < n; ++i) {
-      data->apps_infos.emplace_back(app_constants::kChromeAppId);
+      data->apps_infos.emplace_back(app_constants::kChromeAppId, u"Title");
     }
 
     return data;
@@ -257,10 +257,7 @@
   const PineContentsData* pine_contents_data =
       Shell::Get()->pine_controller()->pine_contents_data();
   EXPECT_TRUE(pine_contents_data && !pine_contents_data->image.isNull());
-  const PineContentsView* contents_view = GetContentsView();
-  // Screenshot icon row should be shown instead of the list view when there is
-  // a screenshot.
-  EXPECT_FALSE(PineContentsViewTestApi(contents_view).items_container_view());
+  // Screenshot icon row should be shown when there is a screenshot.
   const PineScreenshotIconRowView* screenshot_icon_row_view =
       GetScreenshotIconRowView();
   EXPECT_TRUE(screenshot_icon_row_view);
@@ -284,10 +281,7 @@
   const PineContentsData* pine_contents_data =
       Shell::Get()->pine_controller()->pine_contents_data();
   EXPECT_TRUE(pine_contents_data && !pine_contents_data->image.isNull());
-  const PineContentsView* contents_view = GetContentsView();
-  // Screenshot icon row should be shown instead of the list view when there is
-  // a screenshot.
-  EXPECT_FALSE(PineContentsViewTestApi(contents_view).items_container_view());
+  // Screenshot icon row should be shown when there is a screenshot.
   const PineScreenshotIconRowView* screenshot_icon_row_view =
       GetScreenshotIconRowView();
   EXPECT_TRUE(screenshot_icon_row_view);
@@ -345,20 +339,40 @@
   EXPECT_FALSE(anchored_nudge_manager->GetShownNudgeForTest(kEducationNudgeId));
 }
 
+// Tests that we only show the nudge for pine overview.
+TEST_F(PineTest, NudgePine) {
+  Shell::Get()
+      ->pine_controller()
+      ->MaybeStartPineOverviewSessionDevAccelerator();
+  WaitForOverviewEntered();
+  ToggleOverview();
+  auto* anchored_nudge_manager = Shell::Get()->anchored_nudge_manager();
+  EXPECT_TRUE(anchored_nudge_manager->GetShownNudgeForTest(kEducationNudgeId));
+  anchored_nudge_manager->Cancel(kEducationNudgeId);
+
+  // Reset `pine_contents_data` so we start normal overview.
+  PineTestApi().SetPineContentsDataForTesting(nullptr);
+
+  // Start and end overview normally. Test we don't show the nudge.
+  ToggleOverview();
+  auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  ASSERT_FALSE(OverviewGridTestApi(overview_grid).pine_widget());
+  ToggleOverview();
+  EXPECT_FALSE(anchored_nudge_manager->GetShownNudgeForTest(kEducationNudgeId));
+}
+
 // Tests the onboarding metrics are recorded correctly.
 TEST_F(PineTest, OnboardingMetrics) {
   base::HistogramTester histogram_tester;
-  PineController::SetIgnorePrefsForTesting(true);
 
-  // Set some fake data to simulate having restore data.
-  // TODO(sophiewen): Remove this when UX decide what to do.
-  auto* pine_controller = Shell::Get()->pine_controller();
-  pine_controller->pine_contents_data_ = MakeTestAppIds(1);
+  // The pref is set to false in tests by default.
+  GetTestPrefService()->SetBoolean(prefs::kShouldShowPineOnboarding, true);
 
   // Verify initial histogram counts.
   histogram_tester.ExpectTotalCount(kPineOnboardingHistogram, 0);
 
   // Press "Accept". Test we increment `true`.
+  auto* pine_controller = Shell::Get()->pine_controller();
   pine_controller->MaybeShowPineOnboardingMessage(
       /*restore_on=*/false);
   auto* dialog = PineTestApi().GetOnboardingDialog();
@@ -367,6 +381,7 @@
   histogram_tester.ExpectBucketCount(kPineOnboardingHistogram,
                                      /*sample=*/true,
                                      /*expected_count=*/1);
+  GetTestPrefService()->SetBoolean(prefs::kShouldShowPineOnboarding, true);
 
   // Press "Cancel". Test we increment `false`.
   pine_controller->MaybeShowPineOnboardingMessage(
@@ -377,6 +392,7 @@
   histogram_tester.ExpectBucketCount(kPineOnboardingHistogram,
                                      /*sample=*/false,
                                      /*expected_count=*/1);
+  GetTestPrefService()->SetBoolean(prefs::kShouldShowPineOnboarding, true);
 
   // Verify total counts.
   histogram_tester.ExpectTotalCount(kPineOnboardingHistogram, 2);
@@ -384,7 +400,10 @@
   // Show the onboarding dialog with 'Restore' on. Test we don't record.
   pine_controller->MaybeShowPineOnboardingMessage(
       /*restore_on=*/true);
-  LeftClickOn(PineTestApi().GetOnboardingDialog()->GetAcceptButtonForTesting());
+  dialog = PineTestApi().GetOnboardingDialog();
+  LeftClickOn(dialog->GetAcceptButtonForTesting());
+  views::test::WidgetDestroyedWaiter(dialog->GetWidget()).Wait();
+  WaitForOverviewEntered();
   histogram_tester.ExpectTotalCount(kPineOnboardingHistogram, 2);
 }
 
diff --git a/ash/wm/window_restore/window_restore_controller.cc b/ash/wm/window_restore/window_restore_controller.cc
index 7cd7ceb..7e68f143 100644
--- a/ash/wm/window_restore/window_restore_controller.cc
+++ b/ash/wm/window_restore/window_restore_controller.cc
@@ -539,8 +539,8 @@
     mru_windows =
         Shell::Get()->mru_window_tracker()->BuildMruWindowList(kAllDesks);
   }
-  std::unique_ptr<app_restore::WindowInfo> window_info = BuildWindowInfo(
-      window, activation_index, /*for_saved_desks=*/false, mru_windows);
+  std::unique_ptr<app_restore::WindowInfo> window_info =
+      BuildWindowInfo(window, activation_index, mru_windows);
   ::full_restore::SaveWindowInfo(*window_info);
 
   if (g_save_window_callback_for_testing)
diff --git a/ash/wm/window_restore/window_restore_util.cc b/ash/wm/window_restore/window_restore_util.cc
index eff1d66..01697a62 100644
--- a/ash/wm/window_restore/window_restore_util.cc
+++ b/ash/wm/window_restore/window_restore_util.cc
@@ -13,6 +13,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/path_service.h"
 #include "base/ranges/algorithm.h"
+#include "components/app_constants/constants.h"
 #include "components/app_restore/window_properties.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/screen_position_client.h"
@@ -51,7 +52,6 @@
 std::unique_ptr<app_restore::WindowInfo> BuildWindowInfo(
     aura::Window* window,
     std::optional<int> activation_index,
-    bool for_saved_desks,
     const std::vector<raw_ptr<aura::Window, VectorExperimental>>& mru_windows) {
   auto window_info = std::make_unique<app_restore::WindowInfo>();
   int window_activation_index = -1;
@@ -137,14 +137,12 @@
   // For saved desks, store the readable app name so that we can have a nice
   // error message if the user tries to used the saved desk on a device that
   // doesn't have the app.
-  if (for_saved_desks) {
-    std::string* app_id = window->GetProperty(kAppIDKey);
-    window_info->app_title =
-        app_id
-            ? base::UTF8ToUTF16(
-                  Shell::Get()->saved_desk_delegate()->GetAppShortName(*app_id))
-            : window->GetTitle();
-  }
+  std::string* app_id = window->GetProperty(kAppIDKey);
+  window_info->app_title =
+      app_id
+          ? base::UTF8ToUTF16(
+                Shell::Get()->saved_desk_delegate()->GetAppShortName(*app_id))
+          : window->GetTitle();
 
   // Save window size restriction of ARC app window.
   if (IsArcWindow(window)) {
@@ -162,6 +160,10 @@
   return window_info;
 }
 
+bool IsBrowserAppId(const std::string& id) {
+  return id == app_constants::kChromeAppId || id == app_constants::kLacrosAppId;
+}
+
 base::FilePath GetShutdownPineImagePath() {
   if (!pine_image_path_for_test_.empty()) {
     return pine_image_path_for_test_;
diff --git a/ash/wm/window_restore/window_restore_util.h b/ash/wm/window_restore/window_restore_util.h
index ff38f067..de11bbbb 100644
--- a/ash/wm/window_restore/window_restore_util.h
+++ b/ash/wm/window_restore/window_restore_util.h
@@ -37,14 +37,14 @@
 // Builds the WindowInfo for `window`. Optionally passes `activation_index`,
 // which is used to set `WindowInfo.activation_index` if it has value.
 // Otherwise, `WindowInfo.activation_index` will be calculated from
-// `mru_windows`. If `for_saved_desks` this was called from a feature which
-// saves desks, and we need to add extra information such as the app title.
+// `mru_windows`.
 std::unique_ptr<app_restore::WindowInfo> BuildWindowInfo(
     aura::Window* window,
     std::optional<int> activation_index,
-    bool for_saved_desks,
     const std::vector<raw_ptr<aura::Window, VectorExperimental>>& mru_windows);
 
+bool IsBrowserAppId(const std::string& id);
+
 // Gets the path for the pine image being taken on shutdown. It will be written
 // to /home/chronos/u-<hash>/pine_image.png.
 ASH_EXPORT base::FilePath GetShutdownPineImagePath();
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 802dad03..2eb8e53 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -35,6 +35,7 @@
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
 #include "ash/shell_observer.h"
+#include "ash/style/rounded_label_widget.h"
 #include "ash/system/notification_center/ash_message_popup_collection.h"
 #include "ash/system/notification_center/message_popup_animation_waiter.h"
 #include "ash/system/notification_center/notification_center_tray.h"
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 0cba76b..25356ec 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1276,7 +1276,9 @@
     ]
 
     deps += [
+      ":base_jni",
       ":base_shared_preferences_jni",
+      ":process_launcher_jni",
       "//third_party/ashmem",
       "//third_party/cpu_features:ndk_compat",
     ]
@@ -1322,6 +1324,7 @@
   if (is_robolectric) {
     # Make jni.h available.
     configs += [ "//third_party/jdk" ]
+    deps += [ ":base_robolectric_jni" ]
   }
   if (is_android || is_robolectric) {
     sources += [
@@ -1356,11 +1359,7 @@
       "android/trace_event_binding.cc",
       "android/trace_event_binding.h",
     ]
-    deps += [
-      ":base_jni",
-      ":process_launcher_jni",
-      "//build:robolectric_buildflags",
-    ]
+    deps += [ "//build:robolectric_buildflags" ]
     public_deps += [ "//third_party/jni_zero:jni_zero" ]
   }  # is_android || is_robolectric
 
@@ -4281,6 +4280,24 @@
 }
 
 if (is_android || is_robolectric) {
+  # This is a subset of the main "base_jni", used in the robolectric toolchain.
+  # While it would be nice to just make 2 unique generate_jnis, we can't because
+  # we will have 2 GEN_JNI classes in one compilation unit. This is fixable, see
+  # crbug.com/330173100.
+  generate_jni("base_robolectric_jni") {
+    sources = [
+      "android/java/src/org/chromium/base/Callback.java",
+      "android/java/src/org/chromium/base/CommandLine.java",
+      "android/java/src/org/chromium/base/JNIUtils.java",
+      "android/java/src/org/chromium/base/JavaExceptionReporter.java",
+      "android/java/src/org/chromium/base/Token.java",
+      "android/java/src/org/chromium/base/TraceEvent.java",
+      "android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+      "android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java",
+    ]
+    public_deps = [ ":android_runtime_jni_headers" ]
+  }
+
   generate_jni("base_jni") {
     sources = [
       "android/java/src/org/chromium/base/ApkAssets.java",
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_page.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_page.cc
index 751cc12..2d9538e 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_page.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_page.cc
@@ -107,6 +107,10 @@
     slot_span_to_decommit->DecommitIfPossible(root);
   }
 
+  // There should not be a slot span in the buffer at the position this is
+  // going into.
+  PA_DCHECK(!root->global_empty_slot_span_ring[current_index]);
+
   // We put the empty slot span on our global list of "slot spans that were once
   // empty", thus providing it a bit of breathing room to get re-used before we
   // really free it. This reduces the number of system calls. Otherwise any
@@ -249,8 +253,8 @@
   in_empty_cache_ = 0;
   if (is_empty()) {
     Decommit(root);
-    root->global_empty_slot_span_ring[empty_cache_index_] = nullptr;
   }
+  root->global_empty_slot_span_ring[empty_cache_index_] = nullptr;
 }
 
 void SlotSpanMetadata::SortFreelist() {
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
index 0a1aa88..9a702e8 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
@@ -1411,7 +1411,8 @@
     // The ring is not always full, may be nullptr.
     if (slot_span) {
       slot_span->DecommitIfPossible(this);
-      global_empty_slot_span_ring[index] = nullptr;
+      // DecommitIfPossible() should set the buffer to null.
+      PA_DCHECK(!global_empty_slot_span_ring[index]);
     }
     index += 1;
     // Walk through the entirety of possible slots, even though the last ones
diff --git a/base/android/callback_android.cc b/base/android/callback_android.cc
index b33acbb..d1e36ab 100644
--- a/base/android/callback_android.cc
+++ b/base/android/callback_android.cc
@@ -8,9 +8,15 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
-#include "base/base_jni/Callback_jni.h"
 #include "base/time/time.h"
 #include "base/types/optional_ref.h"
+#include "build/robolectric_buildflags.h"
+
+#if BUILDFLAG(IS_ROBOLECTRIC)
+#include "base/base_robolectric_jni/Callback_jni.h"  // nogncheck
+#else
+#include "base/base_jni/Callback_jni.h"
+#endif
 
 namespace base {
 namespace android {
diff --git a/base/android/command_line_android.cc b/base/android/command_line_android.cc
index dd61d6d..5aec96d 100644
--- a/base/android/command_line_android.cc
+++ b/base/android/command_line_android.cc
@@ -4,8 +4,14 @@
 
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
-#include "base/base_jni/CommandLine_jni.h"
 #include "base/command_line.h"
+#include "build/robolectric_buildflags.h"
+
+#if BUILDFLAG(IS_ROBOLECTRIC)
+#include "base/base_robolectric_jni/CommandLine_jni.h"  // nogncheck
+#else
+#include "base/base_jni/CommandLine_jni.h"
+#endif
 
 using base::android::ConvertUTF8ToJavaString;
 using base::android::ConvertJavaStringToUTF8;
diff --git a/base/android/java_exception_reporter.cc b/base/android/java_exception_reporter.cc
index bada62c4..e2b9b4d 100644
--- a/base/android/java_exception_reporter.cc
+++ b/base/android/java_exception_reporter.cc
@@ -7,12 +7,18 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
-#include "base/base_jni/JavaExceptionReporter_jni.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "build/robolectric_buildflags.h"
+
+#if BUILDFLAG(IS_ROBOLECTRIC)
+#include "base/base_robolectric_jni/JavaExceptionReporter_jni.h"  // nogncheck
+#else
+#include "base/base_jni/JavaExceptionReporter_jni.h"
+#endif
 
 using base::android::JavaParamRef;
 using base::android::JavaRef;
diff --git a/base/android/jni_utils.cc b/base/android/jni_utils.cc
index 740d875..6f28da4 100644
--- a/base/android/jni_utils.cc
+++ b/base/android/jni_utils.cc
@@ -9,8 +9,13 @@
 #include "base/containers/flat_map.h"
 #include "base/no_destructor.h"
 #include "base/synchronization/lock.h"
+#include "build/robolectric_buildflags.h"
 
+#if BUILDFLAG(IS_ROBOLECTRIC)
+#include "base/base_robolectric_jni/JNIUtils_jni.h"  // nogncheck
+#else
 #include "base/base_jni/JNIUtils_jni.h"
+#endif
 
 namespace base {
 namespace android {
diff --git a/base/android/native_uma_recorder.cc b/base/android/native_uma_recorder.cc
index bb1d66a8..88ca9c3b 100644
--- a/base/android/native_uma_recorder.cc
+++ b/base/android/native_uma_recorder.cc
@@ -6,7 +6,6 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
-#include "base/base_jni/NativeUmaRecorder_jni.h"
 #include "base/format_macros.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_base.h"
@@ -15,6 +14,13 @@
 #include "base/metrics/user_metrics.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
+#include "build/robolectric_buildflags.h"
+
+#if BUILDFLAG(IS_ROBOLECTRIC)
+#include "base/base_robolectric_jni/NativeUmaRecorder_jni.h"  // nogncheck
+#else
+#include "base/base_jni/NativeUmaRecorder_jni.h"
+#endif
 
 namespace base {
 namespace android {
diff --git a/base/android/token_android.cc b/base/android/token_android.cc
index 0096f4e..5c854e0 100644
--- a/base/android/token_android.cc
+++ b/base/android/token_android.cc
@@ -4,8 +4,14 @@
 
 #include "base/android/token_android.h"
 
-#include "base/base_jni/Token_jni.h"
 #include "base/numerics/safe_conversions.h"
+#include "build/robolectric_buildflags.h"
+
+#if BUILDFLAG(IS_ROBOLECTRIC)
+#include "base/base_robolectric_jni/Token_jni.h"  // nogncheck
+#else
+#include "base/base_jni/Token_jni.h"
+#endif
 
 namespace base {
 namespace android {
diff --git a/base/android/trace_event_binding.cc b/base/android/trace_event_binding.cc
index 0739ccc..2caa77a 100644
--- a/base/android/trace_event_binding.cc
+++ b/base/android/trace_event_binding.cc
@@ -8,10 +8,16 @@
 
 #include "base/android/jni_string.h"
 #include "base/android/trace_event_binding.h"
-#include "base/base_jni/TraceEvent_jni.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/trace_event/base_tracing.h"
 #include "base/tracing_buildflags.h"
+#include "build/robolectric_buildflags.h"
+
+#if BUILDFLAG(IS_ROBOLECTRIC)
+#include "base/base_robolectric_jni/TraceEvent_jni.h"  // nogncheck
+#else
+#include "base/base_jni/TraceEvent_jni.h"
+#endif
 
 #if BUILDFLAG(ENABLE_BASE_TRACING)
 #include "base/trace_event/trace_event_impl.h"  // no-presubmit-check
diff --git a/base/containers/heap_array_unittest.cc b/base/containers/heap_array_unittest.cc
index 66fe1ca..fdc05dd9 100644
--- a/base/containers/heap_array_unittest.cc
+++ b/base/containers/heap_array_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include <algorithm>
 #include <type_traits>
 
 #include "base/containers/span.h"
@@ -207,6 +208,15 @@
 #endif
 }
 
+TEST(HeapArray, Fill) {
+  auto vec = HeapArray<uint32_t>::Uninit(4);
+  std::ranges::fill(vec, 0x76543210);
+  EXPECT_EQ(0x76543210u, vec[0]);
+  EXPECT_EQ(0x76543210u, vec[1]);
+  EXPECT_EQ(0x76543210u, vec[2]);
+  EXPECT_EQ(0x76543210u, vec[3]);
+}
+
 TEST(HeapArray, CopiedFrom) {
   span<uint32_t> empty_span;
   auto empty_vec = HeapArray<uint32_t>::CopiedFrom(empty_span);
diff --git a/base/debug/asan_service.cc b/base/debug/asan_service.cc
index 8c59e02..503034c 100644
--- a/base/debug/asan_service.cc
+++ b/base/debug/asan_service.cc
@@ -15,9 +15,8 @@
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_WIN)
-#include "base/files/file.h"
-#include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/win/windows_types.h"
 #endif  // BUILDFLAG(IS_WIN)
 
 #if defined(COMPONENT_BUILD) && BUILDFLAG(IS_WIN)
@@ -72,15 +71,11 @@
   if (!is_initialized_) {
 #if BUILDFLAG(IS_WIN)
     if (logging::IsLoggingToFileEnabled()) {
-      // This path is allowed by the sandbox when `--enable-logging
-      //  --log-file={path}` are both specified when launching Chromium.
-      auto log_file = base::File(
-          base::FilePath(logging::GetLogFileFullPath()),
-          base::File::Flags::FLAG_OPEN_ALWAYS | base::File::Flags::FLAG_APPEND);
-      if (log_file.IsValid()) {
+      // Sandboxed processes cannot open files but are provided a HANDLE.
+      HANDLE log_handle = logging::DuplicateLogFileHandle();
+      if (log_handle) {
         // Sanitizer APIs need a HANDLE cast to void*.
-        __sanitizer_set_report_fd(
-            reinterpret_cast<void*>(log_file.TakePlatformFile()));
+        __sanitizer_set_report_fd(reinterpret_cast<void*>(log_handle));
       }
     }
 #endif  // BUILDFLAG(IS_WIN)
diff --git a/base/files/file_util.h b/base/files/file_util.h
index aedddcc9..1e48ae7 100644
--- a/base/files/file_util.h
+++ b/base/files/file_util.h
@@ -23,6 +23,7 @@
 #include "base/files/file_path.h"
 #include "base/files/scoped_file.h"
 #include "base/functional/callback.h"
+#include "base/types/pass_key.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -33,11 +34,28 @@
 #include "base/posix/eintr_wrapper.h"
 #endif
 
+namespace content::internal {
+class ChildProcessLauncherHelper;
+}  // namespace content::internal
+
 namespace base {
 
 class Environment;
 class Time;
 
+#if BUILDFLAG(IS_WIN)
+class PreventExecuteMappingClasses {
+ public:
+  using PassKey = base::PassKey<PreventExecuteMappingClasses>;
+
+ private:
+  static PassKey GetPassKey() { return PassKey(); }
+
+  // Allowed to open log files in arbitrary locations.
+  friend class content::internal::ChildProcessLauncherHelper;
+};
+#endif
+
 //-----------------------------------------------------------------------------
 // Functions that involve filesystem access or modification:
 
@@ -128,6 +146,13 @@
 // untrusted process. See also `File::FLAG_WIN_NO_EXECUTE`.
 BASE_EXPORT bool PreventExecuteMapping(const FilePath& path);
 
+// Same as PreventExecuteMapping but DCHECK for known allowed paths is omitted.
+// Only call this if you know the path you are providing is safe to mark as
+// non-executable, such as log files.
+BASE_EXPORT bool PreventExecuteMappingUnchecked(
+    const FilePath& path,
+    base::PassKey<PreventExecuteMappingClasses> passkey);
+
 // Set `path_key` to the second of two valid paths that support safely marking a
 // file as non-execute. The first allowed path is always PATH_TEMP. This is
 // needed to avoid layering violations, as the user data dir is an embedder
diff --git a/base/files/file_util_win.cc b/base/files/file_util_win.cc
index 8bb4146c..4b548193d 100644
--- a/base/files/file_util_win.cc
+++ b/base/files/file_util_win.cc
@@ -379,6 +379,7 @@
 // might cause the browser or operating system to fail in unexpected ways.
 bool IsPathSafeToSetAclOn(const FilePath& path) {
 #if BUILDFLAG(CLANG_PROFILING)
+  // TODO(crbug.com/329482479) Use PreventExecuteMappingUnchecked for .profraw.
   // Ignore .profraw profiling files, as they can occur anywhere, and only occur
   // during testing.
   if (path.Extension() == FILE_PATH_LITERAL(".profraw")) {
@@ -1152,13 +1153,13 @@
                                  /*Flags=*/0);
 }
 
-bool PreventExecuteMapping(const FilePath& path) {
+bool PreventExecuteMappingInternal(const FilePath& path, bool skip_path_check) {
   if (!base::FeatureList::IsEnabled(
           features::kEnforceNoExecutableFileHandles)) {
     return true;
   }
 
-  bool is_path_safe = IsPathSafeToSetAclOn(path);
+  bool is_path_safe = skip_path_check || IsPathSafeToSetAclOn(path);
 
   if (!is_path_safe) {
     // To mitigate the effect of past OS bugs where attackers are able to use
@@ -1207,6 +1208,16 @@
                                /*recursive=*/false);
 }
 
+bool PreventExecuteMapping(const FilePath& path) {
+  return PreventExecuteMappingInternal(path, false);
+}
+
+bool PreventExecuteMappingUnchecked(
+    const FilePath& path,
+    base::PassKey<PreventExecuteMappingClasses> passkey) {
+  return PreventExecuteMappingInternal(path, true);
+}
+
 void SetExtraNoExecuteAllowedPath(int path_key) {
   DCHECK(!g_extra_allowed_path_for_no_execute ||
          g_extra_allowed_path_for_no_execute == path_key);
diff --git a/base/logging.cc b/base/logging.cc
index 94e4290..1f400937 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -536,7 +536,7 @@
   }
 #endif
 
-  // ignore file options unless logging to file is set.
+  // Ignore file options unless logging to file is set.
   if ((g_logging_destination & LOG_TO_FILE) == 0)
     return true;
 
@@ -548,13 +548,13 @@
   // default log file will re-initialize to the new options.
   CloseLogFileUnlocked();
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
   if (settings.log_file) {
     DCHECK(!settings.log_file_path);
     g_log_file = settings.log_file;
     return true;
   }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
 
   DCHECK(settings.log_file_path) << "LOG_TO_FILE set but no log_file_path!";
 
@@ -1155,6 +1155,24 @@
 }
 #endif
 
+#if BUILDFLAG(IS_WIN)
+HANDLE DuplicateLogFileHandle() {
+  // `g_log_file` should only be valid, or nullptr, but be very careful that we
+  // do not duplicate INVALID_HANDLE_VALUE as it aliases the process handle.
+  if (!(g_logging_destination & LOG_TO_FILE) || !g_log_file ||
+      g_log_file == INVALID_HANDLE_VALUE) {
+    return nullptr;
+  }
+  HANDLE duplicate = nullptr;
+  if (!::DuplicateHandle(::GetCurrentProcess(), g_log_file,
+                         ::GetCurrentProcess(), &duplicate, 0,
+                         /*bInheritHandle=*/TRUE, DUPLICATE_SAME_ACCESS)) {
+    return nullptr;
+  }
+  return duplicate;
+}
+#endif
+
 // Used for testing. Declared in test/scoped_logging_settings.h.
 ScopedLoggingSettings::ScopedLoggingSettings()
     : min_log_level_(g_min_log_level),
diff --git a/base/logging.h b/base/logging.h
index 3b97725..2cb419e 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -28,6 +28,10 @@
 #include <cstdio>
 #endif
 
+#if BUILDFLAG(IS_WIN)
+#include "base/win/windows_types.h"
+#endif
+
 //
 // Optional message capabilities
 // -----------------------------
@@ -188,6 +192,7 @@
 // with bitwise OR.
 // Unless destination is LOG_NONE, all logs with severity ERROR and above will
 // be written to stderr in addition to the specified destination.
+// LOG_TO_FILE includes logging to externally-provided file handles.
 enum : uint32_t {
   LOG_NONE = 0,
   LOG_TO_FILE = 1 << 0,
@@ -248,6 +253,13 @@
   // ChromeOS uses the syslog log format by default.
   LogFormat log_format = LogFormat::LOG_FORMAT_SYSLOG;
 #endif
+#if BUILDFLAG(IS_WIN)
+  // Contains an optional file that logs should be written to. If present,
+  // `log_file_path` will be ignored, and the logging system will take ownership
+  // of the HANDLE. If there's an error writing to this file, no fallback paths
+  // will be opened.
+  HANDLE log_file = nullptr;
+#endif
 };
 
 // Define different names for the BaseInitLoggingImpl() function depending on
@@ -755,6 +767,9 @@
 
 // Returns the default log file path.
 BASE_EXPORT std::wstring GetLogFileFullPath();
+
+// Duplicates the log file handle to send into a child process.
+BASE_EXPORT HANDLE DuplicateLogFileHandle();
 #endif
 
 }  // namespace logging
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 8b35a3b..4c03573 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -261,7 +261,6 @@
       ":base_unittests_jni_headers",
       ":test_support_jni_headers",
     ]
-    public_deps += [ ":test_support_java" ]
   }
 
   if (is_ios) {
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
index 12f01ba2..c5572c7 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
@@ -10,7 +10,6 @@
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.ContextWrapper;
-import android.content.SharedPreferences;
 import android.os.Build;
 import android.os.Build.VERSION;
 import android.os.Bundle;
@@ -18,7 +17,6 @@
 import android.os.Looper;
 import android.os.SystemClock;
 import android.system.Os;
-import android.text.TextUtils;
 
 import androidx.core.content.ContextCompat;
 import androidx.test.InstrumentationRegistry;
@@ -44,7 +42,6 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.InMemorySharedPreferences;
 import org.chromium.base.test.util.InMemorySharedPreferencesContext;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.ScalableTimeout;
@@ -179,7 +176,6 @@
             }
             finishAllAppTasks(getTargetContext());
             BaseJUnit4TestRule.clearJobSchedulerJobs();
-            checkOrDeleteOnDiskSharedPreferences(false);
             clearDataDirectory(sInMemorySharedPreferencesContext);
             InstrumentationRegistry.getInstrumentation().setInTouchMode(true);
             // //third_party/mockito is looking for android.support.test.InstrumentationRegistry.
@@ -484,7 +480,6 @@
 
         try {
             writeClangCoverageProfileIfEnabled();
-            checkOrDeleteOnDiskSharedPreferences(true);
 
             // There is a bug on L and below that DestroyActivitiesRule does not cause onStop and
             // onDestroy. On other versions, DestroyActivitiesRule may still fail flakily. Ignore
@@ -630,77 +625,6 @@
         }
     }
 
-    private static boolean isSharedPrefFileAllowed(File f) {
-        // WebView prefs need to stay because webview tests have no (good) way of hooking
-        // SharedPreferences for instantiated WebViews.
-        String[] allowlist =
-                new String[] {
-                    "WebViewChromiumPrefs.xml",
-                    "org.chromium.android_webview.devui.MainActivity.xml",
-                    "AwComponentUpdateServicePreferences.xml",
-                    "ComponentsProviderServicePreferences.xml",
-                    "org.chromium.webengine.test.instrumentation_test_apk_preferences.xml",
-                    "AwOriginVisitLoggerPrefs.xml",
-                };
-        for (String name : allowlist) {
-            // SharedPreferences may also access a ".bak" backup file from a previous run. See
-            // https://crbug.com/1462105#c4 and
-            // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/app/SharedPreferencesImpl.java;l=213;drc=6f7c5e0914a18e6adafaa319e670363772e51691
-            // for details.
-            String backupName = name + ".bak";
-
-            if (f.getName().equals(name) || f.getName().equals(backupName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void checkOrDeleteOnDiskSharedPreferences(boolean check) {
-        File dataDir = ContextCompat.getDataDir(InstrumentationRegistry.getTargetContext());
-        File prefsDir = new File(dataDir, "shared_prefs");
-        File[] files = prefsDir.listFiles();
-        if (files == null) {
-            return;
-        }
-        ArrayList<File> badFiles = new ArrayList<>();
-        for (File f : files) {
-            if (isSharedPrefFileAllowed(f)) {
-                continue;
-            }
-            if (check) {
-                badFiles.add(f);
-            } else {
-                f.delete();
-            }
-        }
-        if (!badFiles.isEmpty()) {
-            String errorMsg =
-                    "Found unexpected shared preferences file(s) after test ran.\n"
-                        + "All code should use ContextUtils.getApplicationContext() when accessing"
-                        + " SharedPreferences so that tests are hooked to use"
-                        + " InMemorySharedPreferences. This could also mean needing to override"
-                        + " getSharedPreferences() on custom Context subclasses (e.g."
-                        + " ChromeBaseAppCompatActivity does this to make Preferences screens"
-                        + " work).\n\n";
-
-            SharedPreferences testPrefs =
-                    ContextUtils.getApplicationContext()
-                            .getSharedPreferences("test", Context.MODE_PRIVATE);
-            if (!(testPrefs instanceof InMemorySharedPreferences)) {
-                errorMsg +=
-                        String.format(
-                                "ContextUtils.getApplicationContext() was set to type \"%s\", which"
-                                    + " does not delegate to InMemorySharedPreferencesContext (this"
-                                    + " is likely the issues).\n\n",
-                                ContextUtils.getApplicationContext().getClass().getName());
-            }
-
-            errorMsg += "Files:\n * " + TextUtils.join("\n * ", badFiles);
-            throw new AssertionError(errorMsg);
-        }
-    }
-
     /** Configure the required environment variable if Clang coverage argument exists. */
     private void setClangCoverageEnvIfEnabled() {
         String clangProfileFile =
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4TestRule.java b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4TestRule.java
index 915c85d..02fe526 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4TestRule.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4TestRule.java
@@ -5,16 +5,41 @@
 package org.chromium.base.test;
 
 import android.app.job.JobScheduler;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+
+import androidx.core.content.ContextCompat;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.UmaRecorderHolder;
+import org.chromium.base.test.util.InMemorySharedPreferences;
 import org.chromium.base.test.util.InMemorySharedPreferencesContext;
 
+import java.io.File;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 /** Holds setUp / tearDown logic common to all instrumentation tests. */
 class BaseJUnit4TestRule implements TestRule {
+    // These are SharedPreferences that we do not fail tests for using real (instead of in-memory)
+    // implementations. This is generally because it is impractical to use a test-only Context
+    // for them (e.g. multi-process, or third-party code).
+    private static final List<String> SHARED_PREFS_ALLOWLIST =
+            List.of(
+                    "AwComponentUpdateServicePreferences",
+                    "AwOriginVisitLoggerPrefs",
+                    "ComponentsProviderServicePreferences",
+                    "WebViewChromiumPrefs",
+                    "org.chromium.android_webview.devui.MainActivity",
+                    "org.chromium.webengine.test.instrumentation_test_apk_preferences");
+
     @Override
     public Statement apply(Statement base, Description description) {
         return new Statement() {
@@ -27,9 +52,11 @@
                             "BaseJUnit4TestRule requires that you use "
                                     + "BaseChromiumAndroidJUnitRunner (or a subclass)");
                 }
+                deleteOnDiskSharedPreferences();
                 UmaRecorderHolder.resetForTesting();
                 try {
                     base.evaluate();
+                    assertNoOnDiskSharedPreferences();
                 } finally {
                     clearJobSchedulerJobs();
                 }
@@ -40,4 +67,63 @@
     static void clearJobSchedulerJobs() {
         BaseJUnit4ClassRunner.getApplication().getSystemService(JobScheduler.class).cancelAll();
     }
+
+    private static void deleteOnDiskSharedPreferences() {
+        for (String name : findSharedPreferences()) {
+            // Using Application ensure we are not using InMemorySharedPreferencesContext.
+            BaseJUnit4ClassRunner.getApplication().deleteSharedPreferences(name);
+        }
+    }
+
+    private static void assertNoOnDiskSharedPreferences() {
+        Collection<String> unwantedPrefs = findSharedPreferences();
+        unwantedPrefs.removeAll(SHARED_PREFS_ALLOWLIST);
+        if (unwantedPrefs.isEmpty()) {
+            return;
+        }
+        String errorMsg =
+                """
+                Found unexpected shared preferences file(s) after test ran.
+
+                All code should use ContextUtils.getApplicationContext() when accessing
+                SharedPreferences so that test are hooked to use InMemorySharedPreferences. It may
+                also be necessary to override getSharedPreferences() on Context subclasses
+                (e.g. ChromeBaseAppCompatActivity does this to make Preferences screens work).
+
+                If it is not practical to have tests use an InMemorySharedPreferences in you case,
+                then you can add the shared preferences to SHARED_PREFS_ALLOWLIST.
+                """;
+
+        SharedPreferences testPrefs =
+                ContextUtils.getApplicationContext()
+                        .getSharedPreferences("test", Context.MODE_PRIVATE);
+        if (!(testPrefs instanceof InMemorySharedPreferences)) {
+            errorMsg +=
+                    String.format(
+                            "\nContextUtils.getApplicationContext() was set to type \"%s\", which"
+                                    + " does not delegate to InMemorySharedPreferencesContext (this"
+                                    + " is likely the issues).\n",
+                            ContextUtils.getApplicationContext().getClass().getName());
+        }
+
+        errorMsg += "Shared Preferences:\n * " + TextUtils.join("\n * ", unwantedPrefs);
+        throw new AssertionError(errorMsg);
+    }
+
+    private static Collection<String> findSharedPreferences() {
+        File dataDir = ContextCompat.getDataDir(BaseJUnit4ClassRunner.getApplication());
+        File prefsDir = new File(dataDir, "shared_prefs");
+        File[] files = prefsDir.listFiles();
+        Set<String> ret = new HashSet<>();
+        if (files != null) {
+            for (File f : files) {
+                if (f.getName().endsWith(".xml")) {
+                    ret.add(f.getName().substring(0, f.getName().length() - 4));
+                } else if (f.getName().endsWith(".xml.bak")) {
+                    ret.add(f.getName().substring(0, f.getName().length() - 8));
+                }
+            }
+        }
+        return ret;
+    }
 }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ElementInState.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ElementInState.java
index 0c257a5..77a11b4 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/ElementInState.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ElementInState.java
@@ -9,7 +9,7 @@
 import java.util.Set;
 
 /** Represents an Element added to a {@link ConditionalState}. */
-interface ElementInState {
+public interface ElementInState {
 
     /**
      * @return an id used to identify during a {@link Transition} if the same element is a part of
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java b/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java
index d53857e..72249ca 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java
@@ -63,9 +63,10 @@
         }
 
         /** Declare as an element a View that matches |viewMatcher|. */
-        public Builder declareView(ViewElement viewElement) {
-            mElements.mElementsInState.add(new ViewElementInState(viewElement, /* gate= */ null));
-            return this;
+        public ViewElementInState declareView(ViewElement viewElement) {
+            ViewElementInState inState = new ViewElementInState(viewElement, /* gate= */ null);
+            mElements.mElementsInState.add(inState);
+            return inState;
         }
 
         /**
@@ -73,9 +74,10 @@
          *
          * <p>The element is only expected if |gate| returns true.
          */
-        public Builder declareViewIf(ViewElement viewElement, Condition gate) {
-            mElements.mElementsInState.add(new ViewElementInState(viewElement, gate));
-            return this;
+        public ViewElementInState declareViewIf(ViewElement viewElement, Condition gate) {
+            ViewElementInState inState = new ViewElementInState(viewElement, gate);
+            mElements.mElementsInState.add(inState);
+            return inState;
         }
 
         /**
@@ -86,9 +88,9 @@
          * LogicalElements do not generate exit Conditions when going to another ConditionalState
          * with the same LogicalElement.
          */
-        public Builder declareLogicalElement(LogicalElement logicalElement) {
+        public LogicalElement declareLogicalElement(LogicalElement logicalElement) {
             mElements.mElementsInState.add(logicalElement);
-            return this;
+            return logicalElement;
         }
 
         /**
@@ -101,9 +103,9 @@
          * <p>Further, no promises are made that the Condition is false after exiting the State. Use
          * a scoped {@link LogicalElement} in this case.
          */
-        public Builder declareEnterCondition(Condition condition) {
+        public Condition declareEnterCondition(Condition condition) {
             mElements.mOtherEnterConditions.add(condition);
-            return this;
+            return condition;
         }
 
         /**
@@ -113,9 +115,15 @@
          * <p>No promises are made that the Condition is false as long as the ConditionalState is
          * ACTIVE. For these cases, use a scoped {@link LogicalElement}.
          */
-        public Builder declareExitCondition(Condition condition) {
+        public Condition declareExitCondition(Condition condition) {
             mElements.mOtherExitConditions.add(condition);
-            return this;
+            return condition;
+        }
+
+        /** Declare a custom element, already rendered to an ElementInState. */
+        public <T extends ElementInState> T declareElementInState(T elementInState) {
+            mElements.mElementsInState.add(elementInState);
+            return elementInState;
         }
 
         void addAll(Elements otherElements) {
@@ -134,5 +142,4 @@
             return elements;
         }
     }
-
 }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/StationFacility.java b/base/test/android/javatests/src/org/chromium/base/test/transit/StationFacility.java
index 907d4393..f2a2f53 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/StationFacility.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/StationFacility.java
@@ -33,8 +33,13 @@
     /**
      * Constructor.
      *
-     * <p>Instantiate a subclass, then call {@link #enterSync(StationFacility, Trigger)} to enter
-     * it.
+     * <p>Instantiate a concrete subclass instead of this base class.
+     *
+     * <p>If the TransitStation is still NEW, the Enter conditions of this facility with be added to
+     * the transition to the station.
+     *
+     * <p>If the TransitStation is already ACTIVE, call {@link #enterSync(StationFacility, Trigger)}
+     * to enter this Facility synchronously with a Transition.
      *
      * @param station the TransitStation this StationFacility is scoped to.
      */
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java b/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java
index af73032..04bf27da 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java
@@ -19,22 +19,36 @@
      * <p>A different instance of the same subclass {@link TransitStation} does not count; it must
      * be the TransitStation instance returned by the last {@link Trip} transition.
      *
-     * @param expected the TransitStation expected to be the last
-     * @throws AssertionError if the final station is not the same as |expected|
+     * @param expectedStation the TransitStation expected to be the last
+     * @param expectedFacilities the StationFacilities expected to be the active
+     * @throws AssertionError if the final station is not the same as |expected| or any expect
+     *     StationFacility was not active.
      */
-    public static void assertFinalDestination(TransitStation expected) {
+    public static void assertFinalDestination(
+            TransitStation expectedStation, StationFacility... expectedFacilities) {
         TransitStation activeStation = TrafficControl.getActiveStation();
-        if (activeStation != expected) {
+        if (activeStation != expectedStation) {
             raiseAssertion(
                     String.format(
                             "Expected final destination to be %s, but was %s",
-                            expected, activeStation));
+                            expectedStation, activeStation));
         }
-        if (expected.getPhase() != Phase.ACTIVE) {
+        @Phase int phase = expectedStation.getPhase();
+        if (phase != Phase.ACTIVE) {
             raiseAssertion(
                     String.format(
-                            "Station %s expected to be the final one, but it is not ACTIVE",
-                            expected));
+                            "Station %s expected to be the final one and ACTIVE, but it is %s",
+                            expectedStation, ConditionalState.phaseToString(phase)));
+        }
+
+        for (StationFacility facility : expectedFacilities) {
+            phase = facility.getPhase();
+            if (phase != Phase.ACTIVE) {
+                raiseAssertion(
+                        String.format(
+                                "Facility %s expected to be ACTIVE at the end, but it is in %s",
+                                facility, ConditionalState.phaseToString(phase)));
+            }
         }
     }
 
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/TransitStation.java b/base/test/android/javatests/src/org/chromium/base/test/transit/TransitStation.java
index 32bf4ec..c9bcd7c6 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/TransitStation.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/TransitStation.java
@@ -32,11 +32,11 @@
         TrafficControl.notifyCreatedStation(this);
     }
 
-    Elements getElementsIncludingFacilities() {
+    Elements getElementsIncludingFacilitiesWithPhase(@Phase int phase) {
         Elements.Builder allElements = Elements.newBuilder();
         allElements.addAll(getElements());
         for (StationFacility facility : mFacilities) {
-            if (facility.getPhase() == Phase.ACTIVE) {
+            if (facility.getPhase() == phase) {
                 allElements.addAll(facility.getElements());
             }
         }
@@ -58,4 +58,44 @@
     public int getId() {
         return mId;
     }
+
+    @Override
+    void setStateTransitioningTo() {
+        super.setStateTransitioningTo();
+
+        for (StationFacility facility : mFacilities) {
+            facility.setStateTransitioningTo();
+        }
+    }
+
+    @Override
+    void setStateActive() {
+        super.setStateActive();
+
+        for (StationFacility facility : mFacilities) {
+            facility.setStateActive();
+        }
+    }
+
+    @Override
+    void setStateTransitioningFrom() {
+        super.setStateTransitioningFrom();
+
+        for (StationFacility facility : mFacilities) {
+            if (facility.getPhase() == Phase.ACTIVE) {
+                facility.setStateTransitioningFrom();
+            }
+        }
+    }
+
+    @Override
+    void setStateFinished() {
+        super.setStateFinished();
+
+        for (StationFacility facility : mFacilities) {
+            if (facility.getPhase() == Phase.TRANSITIONING_FROM) {
+                facility.setStateFinished();
+            }
+        }
+    }
 }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java b/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java
index e4cc496b..503e562 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java
@@ -8,6 +8,7 @@
 
 import org.chromium.base.Log;
 import org.chromium.base.test.transit.ConditionWaiter.ConditionWaitStatus;
+import org.chromium.base.test.transit.ConditionalState.Phase;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -110,8 +111,11 @@
         ArrayList<ConditionWaitStatus> waitStatuses = new ArrayList<>();
 
         Elements originElements =
-                origin != null ? origin.getElementsIncludingFacilities() : Elements.EMPTY;
-        Elements destinationElements = destination.getElementsIncludingFacilities();
+                origin != null
+                        ? origin.getElementsIncludingFacilitiesWithPhase(Phase.ACTIVE)
+                        : Elements.EMPTY;
+        Elements destinationElements =
+                destination.getElementsIncludingFacilitiesWithPhase(Phase.NEW);
 
         // Create ENTER Conditions for Views that should appear and LogicalElements that should
         // be true.
diff --git a/base/tracing/protos/chrome_track_event.proto b/base/tracing/protos/chrome_track_event.proto
index 6b02d81..882d6ffe 100644
--- a/base/tracing/protos/chrome_track_event.proto
+++ b/base/tracing/protos/chrome_track_event.proto
@@ -43,8 +43,9 @@
   optional TaskScopeType type = 1;
   optional int64 scope_task_id = 2;
   optional int64 running_task_id_to_be_restored = 3;
-  optional int64 continuation_task_id_to_be_restored = 4;
-  optional int64 parent_task_id = 5;
+
+  reserved 4, 5;
+  reserved "continuation_task_id_to_be_restored", "parent_task_id";
 }
 
 message ChromeTaskAnnotator {
diff --git a/build/config/rust.gni b/build/config/rust.gni
index 9b0ee75..23d713bd 100644
--- a/build/config/rust.gni
+++ b/build/config/rust.gni
@@ -120,9 +120,6 @@
   # Rust gtest interop.
   enable_rust_gtest_interop = enable_rust
 
-  # Enable Boringssl Rust bindings generation
-  enable_rust_boringssl = enable_rust
-
   # Enable experimental Fontations Rust font stack.
   use_typeface_fontations = enable_rust
 }
diff --git a/build/config/siso/main.star b/build/config/siso/main.star
index 8481dd7..f83904c 100644
--- a/build/config/siso/main.star
+++ b/build/config/siso/main.star
@@ -167,8 +167,10 @@
         # use_remoteexec = true
         "./obj/chrome/test/unit_tests/site_settings_handler_unittest.o",
         "./obj/components/policy/chrome_settings_proto_generated_compile_proto/chrome_settings.pb.o",
+        "./obj/content/test/content_browsertests/cross_origin_opener_policy_browsertest.o",
         "./obj/content/test/content_browsertests/navigation_controller_impl_browsertest.o",
         "./obj/content/test/content_unittests/auction_runner_unittest.o",
+        "./obj/content/test/test_support/service_worker_test_utils.o",
         "./obj/net/dns/tests/host_resolver_manager_unittest.o",
         "./obj/net/third_party/quiche/quiche_tests/oghttp2_adapter_test.o",
         "./obj/net/third_party/quiche/quiche_tests/quic_connection_test.o",
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index ca29609..f73b1357 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -87,9 +87,6 @@
     // https://crbug.com/327473683
     "race:SetCoveredByBucketing\n"
 
-    // https://crbug.com/329130356
-    "race:IsRafAlignedEvent\n"
-
     // In V8 each global safepoint might lock isolate mutexes in a different
     // order. This is allowed in this context as it is always guarded by a
     // single global mutex.
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index 93dc1a54..b4cf3cc3 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -1730,8 +1730,8 @@
   FakeContentLayerClient client_;
 };
 
-// Disabled on ASAN/debug due to test flakiness. See https://crbug.com/1517464
-#if defined(ADDRESS_SANITIZER) || !defined(NDEBUG)
+// TODO(https://issues.chromium.org/41490442): Flaky on Linux/ASAN/debug.
+#if BUILDFLAG(IS_LINUX) || defined(ADDRESS_SANITIZER) || !defined(NDEBUG)
 SINGLE_THREAD_TEST_F(LayerTreeHostAnimationTestIsAnimating);
 #else
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestIsAnimating);
diff --git a/chrome/MAJOR_BRANCH_DATE b/chrome/MAJOR_BRANCH_DATE
index f8ce456..b4fe844 100644
--- a/chrome/MAJOR_BRANCH_DATE
+++ b/chrome/MAJOR_BRANCH_DATE
@@ -1 +1 @@
-MAJOR_BRANCH_DATE=2024-02-20
+MAJOR_BRANCH_DATE=2024-03-19
diff --git a/chrome/VERSION b/chrome/VERSION
index e16c493..e95db0a 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
-MAJOR=124
+MAJOR=125
 MINOR=0
-BUILD=6367
+BUILD=6368
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 8d37f880e..d71d0ab 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -3596,7 +3596,6 @@
     "java/src/org/chromium/chrome/browser/autofill/CreditCardScannerBridge.java",
     "java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePrompt.java",
     "java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptController.java",
-    "java/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetBridge.java",
     "java/src/org/chromium/chrome/browser/autofill/iban/AutofillSaveIbanBottomSheetBridge.java",
     "java/src/org/chromium/chrome/browser/autofill/save_card/AutofillSaveCardBottomSheetBridge.java",
     "java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsDelegate.java",
@@ -3652,6 +3651,7 @@
     "java/src/org/chromium/chrome/browser/download/OpenDownloadDialogBridge.java",
     "java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java",
     "java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java",
+    "java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetBridge.java",
     "java/src/org/chromium/chrome/browser/feedback/ConnectivityChecker.java",
     "java/src/org/chromium/chrome/browser/feedback/ScreenshotTask.java",
     "java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 6406705..2c7945e 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -121,8 +121,6 @@
   "java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePrompt.java",
   "java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptController.java",
   "java/src/org/chromium/chrome/browser/autofill/WebContentsViewRectProvider.java",
-  "java/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetBridge.java",
-  "java/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetContent.java",
   "java/src/org/chromium/chrome/browser/autofill/iban/AutofillSaveIbanBottomSheetBridge.java",
   "java/src/org/chromium/chrome/browser/autofill/iban/AutofillSaveIbanBottomSheetCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill/iban/AutofillSaveIbanBottomSheetMediator.java",
@@ -619,6 +617,8 @@
   "java/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinator.java",
   "java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/externalnav/IntentWithRequestMetadataHandler.java",
+  "java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetBridge.java",
+  "java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetContent.java",
   "java/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollector.java",
   "java/src/org/chromium/chrome/browser/feedback/ConnectivityChecker.java",
   "java/src/org/chromium/chrome/browser/feedback/ConnectivityFeedbackSource.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 6eecebbb..3de8806 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -39,8 +39,6 @@
   "junit/src/org/chromium/chrome/browser/autofill/AutofillSuggestionTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java",
-  "junit/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetBridgeTest.java",
-  "junit/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetContentTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/iban/AutofillSaveIbanBottomSheetBridgeTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/iban/AutofillSaveIbanBottomSheetCoordinatorTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/iban/AutofillSaveIbanBottomSheetMediatorTest.java",
@@ -209,6 +207,8 @@
   "junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java",
   "junit/src/org/chromium/chrome/browser/dragdrop/DragAndDropLauncherActivityUnitTest.java",
   "junit/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinatorUnitTest.java",
+  "junit/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetBridgeTest.java",
+  "junit/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetContentTest.java",
   "junit/src/org/chromium/chrome/browser/feed/FeedActionDelegateImplTest.java",
   "junit/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollectorUnitTest.java",
   "junit/src/org/chromium/chrome/browser/feedback/FeedFeedbackCollectorTest.java",
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 4dfb668..3e9d6d62 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
@@ -476,11 +476,11 @@
     void setSelectedTabGroupColor(int selectedColor) {
         mModel.set(TabGridDialogProperties.TAB_GROUP_COLOR_ID, selectedColor);
 
-        TabModelFilter filter = mCurrentTabModelFilterSupplier.get();
+        TabGroupModelFilter filter = (TabGroupModelFilter) mCurrentTabModelFilterSupplier.get();
         Tab currentTab = TabModelUtils.getTabById(filter.getTabModel(), mCurrentTabId);
 
         if (currentTab != null) {
-            TabGroupColorUtils.storeTabGroupColor(currentTab.getRootId(), selectedColor);
+            filter.setTabGroupColor(currentTab.getRootId(), selectedColor);
         }
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogManager.java
index 8eee0d4..b8e7a0a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogManager.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogManager.java
@@ -7,6 +7,7 @@
 import android.app.Activity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
 import androidx.appcompat.widget.AppCompatEditText;
@@ -19,6 +20,7 @@
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver;
 import org.chromium.chrome.browser.tasks.tab_management.ColorPickerCoordinator.ColorPickerLayoutType;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.tab_groups.TabGroupColorId;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
@@ -34,10 +36,13 @@
         /**
          * Attempt to show the tab group creation dialog to the user.
          *
-         * @param tabCount The total tab count when creating the tab group.
-         * @param isIncognito Whether the current tab model is incognito.
+         * @param rootId The destination root id when creating a new tab group.
+         * @param filter The current TabGroupModelFilter that this group is created on.
          */
-        protected void showDialog(int tabCount, boolean isIncognito) {
+        protected void showDialog(int rootId, TabGroupModelFilter filter) {
+            int tabCount = filter.getRelatedTabCountForRootId(rootId);
+            boolean isIncognito = filter.isIncognito();
+
             View customView =
                     LayoutInflater.from(mActivity)
                             .inflate(R.layout.tab_group_creation_dialog, null);
@@ -60,7 +65,12 @@
                             isIncognito,
                             ColorPickerLayoutType.DYNAMIC,
                             null);
-            colorPickerCoordinator.setSelectedColorItem(colors.get(1));
+            final @TabGroupColorId int colorId = filter.getTabGroupColor(rootId);
+            colorPickerCoordinator.setSelectedColorItem(colorId);
+
+            LinearLayout linearLayout =
+                    (LinearLayout) customView.findViewById(R.id.creation_dialog_layout);
+            linearLayout.addView(colorPickerCoordinator.getContainerView());
 
             TabGroupCreationTextInputLayout groupTitle =
                     customView.findViewById(R.id.tab_group_title);
@@ -146,9 +156,7 @@
                     public void didCreateNewGroup(Tab destinationTab, TabGroupModelFilter filter) {
                         // TODO(crbug.com/1517346): Consider removing the cancel button for
                         // longpress add as the undo flow does not exist there.
-                        mShowDialogDelegate.showDialog(
-                                filter.getRelatedTabCountForRootId(destinationTab.getRootId()),
-                                filter.isIncognito());
+                        mShowDialogDelegate.showDialog(destinationTab.getRootId(), filter);
                     }
                 };
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java
index 33d316eb..902132b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java
@@ -15,13 +15,12 @@
 import org.chromium.base.Token;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.bookmarks.PendingRunnable;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupColorUtils;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupTitleUtils;
 import org.chromium.components.tab_groups.TabGroupColorId;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
@@ -184,11 +183,12 @@
             // TODO(b:324911877): Create drawable icon.
             // propertyModel.set(TabGroupRowProperties.START_DRAWABLE, null);
 
-            @TabGroupColorId
-            int colorIndex = TabGroupColorUtils.getOrCreateTabGroupColor(tabId, mFilter);
-            builder.with(COLOR_INDEX, colorIndex);
+            if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) {
+                @TabGroupColorId int colorIndex = mFilter.getTabGroupColor(tabId);
+                builder.with(COLOR_INDEX, colorIndex);
+            }
 
-            String userTitle = TabGroupTitleUtils.getTabGroupTitle(tabId);
+            String userTitle = mFilter.getTabGroupTitle(tabId);
             int numberOfTabs = mFilter.getRelatedTabList(tabId).size();
             Pair<String, Integer> titleData = new Pair<>(userTitle, numberOfTabs);
             builder.with(TITLE_DATA, titleData);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java
index cd45d44..1a4fbf6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java
@@ -11,14 +11,13 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupRowProperties.COLOR_INDEX;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGroupRowProperties.TITLE_DATA;
 
-import android.graphics.Color;
-
 import androidx.core.util.Pair;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
@@ -28,13 +27,15 @@
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features;
+import org.chromium.base.test.util.Features.DisableFeatures;
+import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupColorUtils;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupTitleUtils;
 import org.chromium.components.tab_groups.TabGroupColorId;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -44,8 +45,10 @@
 
 /** Tests for {@link TabGroupListMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
+@EnableFeatures(ChromeFeatureList.TAB_GROUP_PARITY_ANDROID)
 public class TabGroupListMediatorUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
 
     @Mock private TabGroupModelFilter mTabGroupModelFilter;
     @Mock private TabModel mTabModel;
@@ -84,19 +87,19 @@
     @SmallTest
     public void testOneGroup() {
         MockTab tab = new MockTab(0, mProfile);
-        TabGroupTitleUtils.storeTabGroupTitle(0, "Title");
-        TabGroupColorUtils.storeTabGroupColor(0, TabGroupColorId.GREY);
         when(mTabGroupModelFilter.getCount()).thenReturn(1);
         when(mTabGroupModelFilter.getTabAt(0)).thenReturn(tab);
         when(mTabGroupModelFilter.isTabInTabGroup(tab)).thenReturn(true);
         when(mTabGroupModelFilter.getRelatedTabList(0)).thenReturn(Collections.singletonList(tab));
+        when(mTabGroupModelFilter.getTabGroupTitle(0)).thenReturn("Title");
+        when(mTabGroupModelFilter.getTabGroupColor(0)).thenReturn(TabGroupColorId.BLUE);
 
         new TabGroupListMediator(mModelList, mTabGroupModelFilter);
         assertEquals(1, mModelList.size());
 
         PropertyModel model = mModelList.get(0).model;
         assertEquals(new Pair<>("Title", 1), model.get(TITLE_DATA));
-        assertEquals(TabGroupColorId.GREY, model.get(COLOR_INDEX));
+        assertEquals(TabGroupColorId.BLUE, model.get(COLOR_INDEX));
     }
 
     @Test
@@ -107,15 +110,14 @@
         tab1.setRootId(2);
 
         MockTab tab2 = new MockTab(2, mProfile);
-        TabGroupTitleUtils.storeTabGroupTitle(2, "Title");
-        TabGroupColorUtils.storeTabGroupColor(2, TabGroupColorId.GREY);
-
         when(mTabGroupModelFilter.getTabModel()).thenReturn(mTabModel);
         when(mTabGroupModelFilter.getCount()).thenReturn(1);
         when(mTabGroupModelFilter.getTabAt(0)).thenReturn(tab1);
         when(mTabGroupModelFilter.getTabAt(1)).thenReturn(tab2);
         when(mTabGroupModelFilter.isTabInTabGroup(tab1)).thenReturn(true);
         when(mTabGroupModelFilter.getRelatedTabList(2)).thenReturn(Arrays.asList(tab1, tab2));
+        when(mTabGroupModelFilter.getTabGroupTitle(2)).thenReturn("Title");
+        when(mTabGroupModelFilter.getTabGroupColor(2)).thenReturn(TabGroupColorId.BLUE);
         when(mTabModel.getCount()).thenReturn(2);
         when(mTabModel.getTabAt(0)).thenReturn(tab1);
         when(mTabModel.getTabAt(1)).thenReturn(tab2);
@@ -125,7 +127,7 @@
 
         PropertyModel model = mModelList.get(0).model;
         assertEquals(new Pair<>("Title", 2), model.get(TITLE_DATA));
-        assertEquals(TabGroupColorId.GREY, model.get(COLOR_INDEX));
+        assertEquals(TabGroupColorId.BLUE, model.get(COLOR_INDEX));
     }
 
     @Test
@@ -133,13 +135,8 @@
     public void testTwoGroups() {
         // Tabs look like: [1, 2], [3, 4, 5].
         MockTab tab1 = new MockTab(1, mProfile);
-        TabGroupTitleUtils.storeTabGroupTitle(1, "Foo");
-        TabGroupColorUtils.storeTabGroupColor(1, TabGroupColorId.BLUE);
-
         MockTab tab2 = new MockTab(2, mProfile);
         MockTab tab3 = new MockTab(3, mProfile);
-        TabGroupTitleUtils.storeTabGroupTitle(3, "Bar");
-        TabGroupColorUtils.storeTabGroupColor(3, TabGroupColorId.RED);
         MockTab tab4 = new MockTab(4, mProfile);
         MockTab tab5 = new MockTab(5, mProfile);
 
@@ -150,6 +147,10 @@
         when(mTabGroupModelFilter.isTabInTabGroup(tab3)).thenReturn(true);
         when(mTabGroupModelFilter.getRelatedTabList(1)).thenReturn(Arrays.asList(tab1, tab2));
         when(mTabGroupModelFilter.getRelatedTabList(3)).thenReturn(Arrays.asList(tab3, tab4, tab5));
+        when(mTabGroupModelFilter.getTabGroupTitle(1)).thenReturn("Foo");
+        when(mTabGroupModelFilter.getTabGroupTitle(3)).thenReturn("Bar");
+        when(mTabGroupModelFilter.getTabGroupColor(1)).thenReturn(TabGroupColorId.BLUE);
+        when(mTabGroupModelFilter.getTabGroupColor(3)).thenReturn(TabGroupColorId.RED);
 
         new TabGroupListMediator(mModelList, mTabGroupModelFilter);
         assertEquals(2, mModelList.size());
@@ -170,12 +171,12 @@
         assertEquals(0, mModelList.size());
 
         MockTab tab = new MockTab(0, mProfile);
-        TabGroupTitleUtils.storeTabGroupTitle(0, "Title");
-        TabGroupColorUtils.storeTabGroupColor(0, Color.GREEN);
         when(mTabGroupModelFilter.getCount()).thenReturn(1);
         when(mTabGroupModelFilter.getTabAt(0)).thenReturn(tab);
         when(mTabGroupModelFilter.isTabInTabGroup(tab)).thenReturn(true);
         when(mTabGroupModelFilter.getRelatedTabList(0)).thenReturn(Collections.singletonList(tab));
+        when(mTabGroupModelFilter.getTabGroupTitle(0)).thenReturn("Title");
+        when(mTabGroupModelFilter.getTabGroupColor(3)).thenReturn(TabGroupColorId.GREEN);
         assertEquals(0, mModelList.size());
 
         verify(mTabGroupModelFilter)
@@ -198,4 +199,21 @@
         ShadowLooper.idleMainLooper();
         assertEquals(2, mModelList.size());
     }
+
+    @Test
+    @SmallTest
+    @DisableFeatures(ChromeFeatureList.TAB_GROUP_PARITY_ANDROID)
+    public void testNoParity() {
+        MockTab tab = new MockTab(0, mProfile);
+        when(mTabGroupModelFilter.getCount()).thenReturn(1);
+        when(mTabGroupModelFilter.getTabAt(0)).thenReturn(tab);
+        when(mTabGroupModelFilter.isTabInTabGroup(tab)).thenReturn(true);
+        when(mTabGroupModelFilter.getRelatedTabList(0)).thenReturn(Collections.singletonList(tab));
+        when(mTabGroupModelFilter.getTabGroupColor(0)).thenReturn(TabGroupColorId.BLUE);
+
+        new TabGroupListMediator(mModelList, mTabGroupModelFilter);
+        assertEquals(1, mModelList.size());
+        // 0 is the default value.
+        assertEquals(0, mModelList.get(0).model.get(COLOR_INDEX));
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManager.java
index 9a90d2e4..1096d7ee 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManager.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManager.java
@@ -38,9 +38,7 @@
                 new TabModelObserver() {
                     @Override
                     public void tabClosureCommitted(Tab tab) {
-                        TabGroupModelFilter filter =
-                                (TabGroupModelFilter)
-                                        tabModelFilterProvider.getTabModelFilter(tab.isIncognito());
+                        TabGroupModelFilter filter = filterFromTab(tab);
                         int rootId = tab.getRootId();
                         Tab groupTab = filter.getGroupLastShownTab(rootId);
                         if (groupTab == null || !filter.isTabInTabGroup(groupTab)) {
@@ -62,20 +60,20 @@
                         if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) {
                             final @TabGroupColorId int colorId =
                                     TabGroupColorUtils.getNextSuggestedColorId(filter);
-                            TabGroupColorUtils.storeTabGroupColor(
-                                    destinationTab.getRootId(), colorId);
+                            filter.setTabGroupColor(destinationTab.getRootId(), colorId);
                         }
                     }
 
                     @Override
                     public void willMergeTabToGroup(Tab movedTab, int newRootId) {
+                        TabGroupModelFilter filter = filterFromTab(movedTab);
                         String sourceGroupTitle =
                                 TabGroupTitleUtils.getTabGroupTitle(movedTab.getRootId());
                         String targetGroupTitle = TabGroupTitleUtils.getTabGroupTitle(newRootId);
                         // If the target group has no title but the source group has a title,
                         // handover the stored title to the group after merge.
                         if (sourceGroupTitle != null && targetGroupTitle == null) {
-                            TabGroupTitleUtils.storeTabGroupTitle(newRootId, sourceGroupTitle);
+                            filter.setTabGroupTitle(newRootId, sourceGroupTitle);
                         }
 
                         if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) {
@@ -86,7 +84,7 @@
                             // handover the stored color to the group after merge.
                             if (sourceGroupColor != INVALID_COLOR_ID
                                     && targetGroupColor == INVALID_COLOR_ID) {
-                                TabGroupColorUtils.storeTabGroupColor(newRootId, sourceGroupColor);
+                                filter.setTabGroupColor(newRootId, sourceGroupColor);
                             }
                         }
                     }
@@ -99,10 +97,7 @@
                         // If the group size is 2, i.e. the group becomes a single tab after
                         // ungroup, delete the stored visual data. When tab groups of size 1 are
                         // supported this behavior is no longer valid.
-                        TabGroupModelFilter filter =
-                                (TabGroupModelFilter)
-                                        tabModelFilterProvider.getTabModelFilter(
-                                                movedTab.isIncognito());
+                        TabGroupModelFilter filter = filterFromTab(movedTab);
                         int sizeThreshold =
                                 ChromeFeatureList.sAndroidTabGroupStableIds.isEnabled() ? 1 : 2;
                         boolean shouldDeleteVisualData =
@@ -123,7 +118,7 @@
                         if (rootId != newRootId) {
                             if (title != null) {
                                 TabGroupTitleUtils.deleteTabGroupTitle(rootId);
-                                TabGroupTitleUtils.storeTabGroupTitle(newRootId, title);
+                                filter.setTabGroupTitle(newRootId, title);
                             }
 
                             if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) {
@@ -131,7 +126,7 @@
                                 assert colorId != INVALID_COLOR_ID;
 
                                 TabGroupColorUtils.deleteTabGroupColor(rootId);
-                                TabGroupColorUtils.storeTabGroupColor(newRootId, colorId);
+                                filter.setTabGroupColor(newRootId, colorId);
                             }
                         }
                     }
@@ -145,6 +140,11 @@
                 .addTabGroupObserver(mFilterObserver);
     }
 
+    private TabGroupModelFilter filterFromTab(Tab tab) {
+        return (TabGroupModelFilter)
+                mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(tab.isIncognito());
+    }
+
     /** Destroy any members that need clean up. */
     public void destroy() {
         TabModelFilterProvider tabModelFilterProvider =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPane.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPane.java
index 385ccff..7ae96b1 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPane.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPane.java
@@ -91,8 +91,10 @@
 
     @Override
     public void destroy() {
-        mTabGroupListCoordinator.destroy();
-        mTabGroupListCoordinator = null;
+        if (mTabGroupListCoordinator != null) {
+            mTabGroupListCoordinator.destroy();
+            mTabGroupListCoordinator = null;
+        }
         mRootView.removeAllViews();
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPaneUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPaneUnitTest.java
index 198e42b..d89485f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPaneUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPaneUnitTest.java
@@ -46,4 +46,40 @@
         pane.notifyLoadHint(LoadHint.COLD);
         assertEquals(0, pane.getRootView().getChildCount());
     }
+
+    @Test
+    public void testDestroy_WhileHot() {
+        TabGroupsPane pane =
+                new TabGroupsPane(
+                        ApplicationProvider.getApplicationContext(),
+                        LazyOneshotSupplier.fromValue(mTabGroupModelFilter),
+                        mOnToolbarAlphaChange);
+        pane.notifyLoadHint(LoadHint.HOT);
+        pane.destroy();
+        assertEquals(0, pane.getRootView().getChildCount());
+    }
+
+    @Test
+    public void testDestroy_WhileCold() {
+        TabGroupsPane pane =
+                new TabGroupsPane(
+                        ApplicationProvider.getApplicationContext(),
+                        LazyOneshotSupplier.fromValue(mTabGroupModelFilter),
+                        mOnToolbarAlphaChange);
+        pane.notifyLoadHint(LoadHint.HOT);
+        pane.notifyLoadHint(LoadHint.COLD);
+        pane.destroy();
+        assertEquals(0, pane.getRootView().getChildCount());
+    }
+
+    @Test
+    public void testDestroy_NoLoadHint() {
+        TabGroupsPane pane =
+                new TabGroupsPane(
+                        ApplicationProvider.getApplicationContext(),
+                        LazyOneshotSupplier.fromValue(mTabGroupModelFilter),
+                        mOnToolbarAlphaChange);
+        pane.destroy();
+        assertEquals(0, pane.getRootView().getChildCount());
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 1ccf8e4..1b7c98f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -1183,12 +1183,16 @@
 
                     @Override
                     protected String getTabGroupTitle(int tabRootId) {
-                        return TabGroupTitleUtils.getTabGroupTitle(tabRootId);
+                        TabGroupModelFilter filter =
+                                (TabGroupModelFilter) mCurrentTabModelFilterSupplier.get();
+                        return filter.getTabGroupTitle(tabRootId);
                     }
 
                     @Override
                     protected void storeTabGroupTitle(int tabRootId, String title) {
-                        TabGroupTitleUtils.storeTabGroupTitle(tabRootId, title);
+                        TabGroupModelFilter filter =
+                                (TabGroupModelFilter) mCurrentTabModelFilterSupplier.get();
+                        filter.setTabGroupTitle(tabRootId, title);
                     }
                 };
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutTest.java
index 9a65bec..3b2a805 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutTest.java
@@ -118,6 +118,7 @@
 import org.chromium.chrome.test.util.ChromeRenderTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.MenuUtils;
+import org.chromium.components.tab_groups.TabGroupColorId;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.WebContentsUtils;
@@ -390,7 +391,14 @@
         verifyTabSwitcherCardCount(cta, 1);
         // Hardcode TabGroupColorId.GREY as the tab group color for render testing purposes.
         Tab tab = cta.getTabModelSelector().getCurrentTab();
-        TabGroupColorUtils.storeTabGroupColor(tab.getRootId(), 0);
+        TabGroupModelFilter filter =
+                (TabGroupModelFilter)
+                        cta.getTabModelSelectorSupplier()
+                                .get()
+                                .getTabModelFilterProvider()
+                                .getCurrentTabModelFilter();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> filter.setTabGroupColor(tab.getRootId(), TabGroupColorId.GREY));
         // Leave and re enter GTS to refetch the favicon.
         leaveTabSwitcher(cta);
         enterTabSwitcher(cta);
@@ -1824,13 +1832,13 @@
         verifyTabSwitcherCardCount(cta, 3);
 
         // Get the next suggested color id.
-        int nextSuggestedColorId =
-                TabGroupColorUtils.getNextSuggestedColorId(
-                        (TabGroupModelFilter)
-                                cta.getTabModelSelectorSupplier()
-                                        .get()
-                                        .getTabModelFilterProvider()
-                                        .getCurrentTabModelFilter());
+        TabGroupModelFilter filter =
+                (TabGroupModelFilter)
+                        cta.getTabModelSelectorSupplier()
+                                .get()
+                                .getTabModelFilterProvider()
+                                .getCurrentTabModelFilter();
+        int nextSuggestedColorId = TabGroupColorUtils.getNextSuggestedColorId(filter);
 
         // Merge first two tabs into a group.
         TabModel normalTabModel = cta.getTabModelSelector().getModel(false);
@@ -1839,10 +1847,7 @@
                         Arrays.asList(normalTabModel.getTabAt(0), normalTabModel.getTabAt(1)));
         createTabGroup(cta, false, tabGroup);
         TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    TabGroupTitleUtils.storeTabGroupTitle(
-                            normalTabModel.getTabAt(0).getRootId(), "Foo");
-                });
+                () -> filter.setTabGroupTitle(normalTabModel.getTabAt(0).getRootId(), "Foo"));
         verifyTabSwitcherCardCount(cta, 2);
         assertTrue(
                 snackbarManager.getCurrentSnackbarForTesting().getController()
@@ -1901,13 +1906,13 @@
         verifyTabSwitcherCardCount(cta, 5);
 
         // Get the next suggested color id.
-        int nextSuggestedColorId1 =
-                TabGroupColorUtils.getNextSuggestedColorId(
-                        (TabGroupModelFilter)
-                                cta.getTabModelSelectorSupplier()
-                                        .get()
-                                        .getTabModelFilterProvider()
-                                        .getCurrentTabModelFilter());
+        TabGroupModelFilter filter =
+                (TabGroupModelFilter)
+                        cta.getTabModelSelectorSupplier()
+                                .get()
+                                .getTabModelFilterProvider()
+                                .getCurrentTabModelFilter();
+        int nextSuggestedColorId1 = TabGroupColorUtils.getNextSuggestedColorId(filter);
 
         // Merge last two tabs into a group.
         TabModel normalTabModel = cta.getTabModelSelector().getModel(false);
@@ -1943,10 +1948,8 @@
         verifyTabSwitcherCardCount(cta, 3);
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
-                    TabGroupTitleUtils.storeTabGroupTitle(
-                            normalTabModel.getTabAt(3).getRootId(), "Foo");
-                    TabGroupTitleUtils.storeTabGroupTitle(
-                            normalTabModel.getTabAt(1).getRootId(), "Bar");
+                    filter.setTabGroupTitle(normalTabModel.getTabAt(3).getRootId(), "Foo");
+                    filter.setTabGroupTitle(normalTabModel.getTabAt(1).getRootId(), "Bar");
                 });
         assertTrue(
                 snackbarManager.getCurrentSnackbarForTesting().getController()
@@ -2009,13 +2012,13 @@
         verifyTabSwitcherCardCount(cta, 3);
 
         // Get the next suggested color id.
-        int nextSuggestedColorId =
-                TabGroupColorUtils.getNextSuggestedColorId(
-                        (TabGroupModelFilter)
-                                cta.getTabModelSelectorSupplier()
-                                        .get()
-                                        .getTabModelFilterProvider()
-                                        .getCurrentTabModelFilter());
+        TabGroupModelFilter filter =
+                (TabGroupModelFilter)
+                        cta.getTabModelSelectorSupplier()
+                                .get()
+                                .getTabModelFilterProvider()
+                                .getCurrentTabModelFilter();
+        int nextSuggestedColorId = TabGroupColorUtils.getNextSuggestedColorId(filter);
 
         // Merge first two tabs into a group.
         TabModel normalTabModel = cta.getTabModelSelector().getModel(false);
@@ -2026,10 +2029,9 @@
         int[] ungroupedRootId = new int[1];
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
-                    TabGroupTitleUtils.storeTabGroupTitle(
-                            normalTabModel.getTabAt(0).getRootId(), "Foo");
+                    filter.setTabGroupTitle(normalTabModel.getTabAt(0).getRootId(), "Foo");
                     ungroupedRootId[0] = normalTabModel.getTabAt(2).getRootId();
-                    TabGroupTitleUtils.storeTabGroupTitle(ungroupedRootId[0], "Bar");
+                    filter.setTabGroupTitle(ungroupedRootId[0], "Bar");
                 });
         verifyTabSwitcherCardCount(cta, 2);
         assertTrue(
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
index 2a2fc55..5f65053 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -1054,21 +1054,15 @@
         createTabGroup(tabgroup, TAB1_ID, TAB_GROUP_ID);
 
         // Mock that we have a stored color stored with reference to root ID of tab1.
-        getGroupColorSharedPreferences()
-                .edit()
-                .putInt(String.valueOf(mTab1.getRootId()), COLOR_2)
-                .apply();
+        // when(mTabGroupModelFilter.getTabGroupColor(mTab1.getRootId())).thenReturn(COLOR_2);
         mModel.set(TabGridDialogProperties.TAB_GROUP_COLOR_ID, COLOR_2);
 
         mMediator.onReset(tabgroup);
         mMediator.setSelectedTabGroupColor(COLOR_3);
 
-        // Assert that the color has changed both in the model and in the shared prefs store.
+        // Assert that the color has changed both in the property model and the model filter.
         assertThat(mModel.get(TabGridDialogProperties.TAB_GROUP_COLOR_ID), equalTo(COLOR_3));
-        assertThat(
-                getGroupColorSharedPreferences()
-                        .getInt(String.valueOf(mTab1.getRootId()), INVALID_COLOR_ID),
-                equalTo(COLOR_3));
+        verify(mTabGroupModelFilter).setTabGroupColor(mTab1.getRootId(), COLOR_3);
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogManagerUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogManagerUnitTest.java
index 0dc49e0..68c072b8 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogManagerUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogManagerUnitTest.java
@@ -83,19 +83,16 @@
 
         verify(mRegularTabGroupModelFilter).addTabGroupObserver(mObserverCaptor.capture());
         TabGroupModelFilterObserver observer = mObserverCaptor.getValue();
-
-        int tabCount = 5;
-        when(mRegularTabGroupModelFilter.getRelatedTabCountForRootId(TAB1_ID)).thenReturn(tabCount);
         observer.didCreateNewGroup(mTab1, mRegularTabGroupModelFilter);
 
-        verify(mShowDialogDelegate).showDialog(tabCount, false);
+        verify(mShowDialogDelegate).showDialog(mTab1.getRootId(), mRegularTabGroupModelFilter);
     }
 
     @Test
     public void testCreationDialogDelegate_showDialog() {
         mTabGroupCreationDialogManager
                 .getShowDialogDelegateForTesting()
-                .showDialog(TAB_COUNT, false);
+                .showDialog(mTab1.getRootId(), mRegularTabGroupModelFilter);
         verify(mModalDialogManager).showDialog(mModelCaptor.capture(), eq(ModalDialogType.APP));
 
         PropertyModel model = mModelCaptor.getValue();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManagerUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManagerUnitTest.java
index 995e6e9f2..227f712 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManagerUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataManagerUnitTest.java
@@ -263,8 +263,7 @@
                 .didCreateNewGroup(mTab1, mTabGroupModelFilter);
 
         // Verify that a default color was stored.
-        verify(mEditorColor).putInt(eq(String.valueOf(TAB1_ID)), eq(COLOR1_ID));
-        verify(mPutIntEditorColor).apply();
+        verify(mTabGroupModelFilter).setTabGroupColor(eq(TAB1_ID), eq(COLOR1_ID));
     }
 
     @Test
@@ -319,10 +318,8 @@
         // The stored title should be assigned to the new root id. The title of the source group
         // will not be deleted until the merge is committed, after
         // SnackbarController#onDismissNoAction is called for the UndoGroupSnackbarController.
-        verify(mEditorTitle).putString(eq(String.valueOf(TAB3_ID)), eq(CUSTOMIZED_TITLE1));
-        verify(mPutStringEditorTitle).apply();
-        verify(mEditorColor).putInt(eq(String.valueOf(TAB3_ID)), eq(COLOR1_ID));
-        verify(mPutIntEditorColor).apply();
+        verify(mTabGroupModelFilter).setTabGroupTitle(eq(TAB3_ID), eq(CUSTOMIZED_TITLE1));
+        verify(mTabGroupModelFilter).setTabGroupColor(eq(TAB3_ID), eq(COLOR1_ID));
     }
 
     @Test
@@ -419,12 +416,10 @@
         // The stored title should be assigned to the new root id.
         verify(mEditorTitle).remove(eq(String.valueOf(TAB1_ID)));
         verify(mRemoveEditorTitle).apply();
-        verify(mEditorTitle).putString(eq(String.valueOf(TAB2_ID)), eq(CUSTOMIZED_TITLE1));
-        verify(mPutStringEditorTitle).apply();
+        verify(mTabGroupModelFilter).setTabGroupTitle(eq(TAB2_ID), eq(CUSTOMIZED_TITLE1));
         verify(mEditorColor).remove(eq(String.valueOf(TAB1_ID)));
         verify(mRemoveEditorColor).apply();
-        verify(mEditorColor).putInt(eq(String.valueOf(TAB2_ID)), eq(COLOR1_ID));
-        verify(mPutIntEditorColor).apply();
+        verify(mTabGroupModelFilter).setTabGroupColor(eq(TAB2_ID), eq(COLOR1_ID));
     }
 
     private void createTabGroup(List<Tab> tabs, int rootId, @Nullable Token groupId) {
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index e671db9..058523a 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -432,6 +432,16 @@
                         })
                 .when(mSpanSizeLookup)
                 .getSpanSize(anyInt());
+
+        doAnswer(
+                        invocation -> {
+                            int rootId = invocation.getArgument(0);
+                            String title = invocation.getArgument(1);
+                            when(mTabGroupModelFilter.getTabGroupTitle(rootId)).thenReturn(title);
+                            return null;
+                        })
+                .when(mTabGroupModelFilter)
+                .setTabGroupTitle(anyInt(), anyString());
     }
 
     @After
@@ -473,10 +483,7 @@
         createTabGroup(tabs, TAB1_ID, TAB_GROUP_ID);
 
         // Mock that we have a stored title stored with reference to root ID of tab1.
-        getGroupTitleSharedPreferences()
-                .edit()
-                .putString(String.valueOf(mTab1.getRootId()), CUSTOMIZED_DIALOG_TITLE1)
-                .apply();
+        mTabGroupModelFilter.setTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
         assertThat(mModel.get(0).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
 
         mTabObserver.onTitleUpdated(mTab1);
@@ -2026,10 +2033,7 @@
         setUpTabListMediator(TabListMediatorType.TAB_GRID_DIALOG, TabListMode.GRID);
 
         // Mock that we have a stored title stored with reference to root ID of tab1.
-        getGroupTitleSharedPreferences()
-                .edit()
-                .putString(String.valueOf(mTab1.getRootId()), CUSTOMIZED_DIALOG_TITLE1)
-                .apply();
+        mTabGroupModelFilter.setTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
         assertThat(
                 mMediator.getTabGroupTitleEditor().getTabGroupTitle(mTab1.getRootId()),
                 equalTo(CUSTOMIZED_DIALOG_TITLE1));
@@ -2045,10 +2049,7 @@
     @Test
     public void getLatestTitle_SingleTabGroupSupported_GTS() {
         // Mock that we have a stored title stored with reference to root ID of tab1.
-        getGroupTitleSharedPreferences()
-                .edit()
-                .putString(String.valueOf(mTab1.getRootId()), CUSTOMIZED_DIALOG_TITLE1)
-                .apply();
+        mTabGroupModelFilter.setTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
         assertThat(
                 mMediator.getTabGroupTitleEditor().getTabGroupTitle(mTab1.getRootId()),
                 equalTo(CUSTOMIZED_DIALOG_TITLE1));
@@ -2066,10 +2067,7 @@
     @Test
     public void getLatestTitle_SingleTabGroupNotSupported_GTS() {
         // Mock that we have a stored title stored with reference to root ID of tab1.
-        getGroupTitleSharedPreferences()
-                .edit()
-                .putString(String.valueOf(mTab1.getRootId()), CUSTOMIZED_DIALOG_TITLE1)
-                .apply();
+        mTabGroupModelFilter.setTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
         assertThat(
                 mMediator.getTabGroupTitleEditor().getTabGroupTitle(mTab1.getRootId()),
                 equalTo(CUSTOMIZED_DIALOG_TITLE1));
@@ -2086,10 +2084,7 @@
     @Test
     public void getLatestTitle_Stored_GTS() {
         // Mock that we have a stored title stored with reference to root ID of tab1.
-        getGroupTitleSharedPreferences()
-                .edit()
-                .putString(String.valueOf(mTab1.getRootId()), CUSTOMIZED_DIALOG_TITLE1)
-                .apply();
+        mTabGroupModelFilter.setTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
         assertThat(
                 mMediator.getTabGroupTitleEditor().getTabGroupTitle(mTab1.getRootId()),
                 equalTo(CUSTOMIZED_DIALOG_TITLE1));
@@ -2149,16 +2144,8 @@
     @Test
     public void tabGroupTitleEditor_storeTitle() {
         TabGroupTitleEditor tabGroupTitleEditor = mMediator.getTabGroupTitleEditor();
-
-        assertNull(
-                getGroupTitleSharedPreferences()
-                        .getString(String.valueOf(mTab1.getRootId()), null));
-
         tabGroupTitleEditor.storeTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
-        assertEquals(
-                CUSTOMIZED_DIALOG_TITLE1,
-                getGroupTitleSharedPreferences()
-                        .getString(String.valueOf(mTab1.getRootId()), null));
+        verify(mTabGroupModelFilter).setTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
     }
 
     @Test
diff --git a/chrome/android/java/res/xml/manage_sync_preferences.xml b/chrome/android/java/res/xml/manage_sync_preferences.xml
index 17b5e02c..fa32df6 100644
--- a/chrome/android/java/res/xml/manage_sync_preferences.xml
+++ b/chrome/android/java/res/xml/manage_sync_preferences.xml
@@ -63,6 +63,11 @@
         android:title="@string/sync_settings"
         android:persistent="false"/>
 
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
+        android:key="sync_apps"
+        android:title="@string/sync_apps"
+        android:persistent="false"/>
+
     <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="turn_off_sync"
         android:title="@string/sign_out_and_turn_off_sync"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OWNERS
index fb7a91c..6c18f7f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OWNERS
@@ -3,5 +3,4 @@
 per-file Overlay*=mdjones@chromium.org
 
 # Secondary
-donnd@chromium.org
 twellington@chromium.org
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index c9a1afab..667ba57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.contextmenu;
 
-import android.os.SystemClock;
 import android.util.Pair;
 import android.view.View;
 
@@ -40,10 +39,6 @@
     private Callback<Integer> mCallback;
     private Runnable mOnMenuShown;
     private Runnable mOnMenuClosed;
-    private long mMenuShownTimeMs;
-    private boolean mSelectedItemBeforeDismiss;
-    private boolean mIsIncognito;
-    private String mPageTitle;
     private ChipDelegate mChipDelegate;
 
     private ContextMenuHelper(long nativeContextMenuHelper, WebContents webContents) {
@@ -105,21 +100,16 @@
         mCurrentPopulator =
                 mPopulatorFactory.createContextMenuPopulator(
                         windowAndroid.getActivity().get(), params, mCurrentNativeDelegate);
-        mIsIncognito = mCurrentPopulator.isIncognito();
-        mPageTitle = mCurrentPopulator.getPageTitle();
         mCurrentContextMenuParams = params;
         mWindow = windowAndroid;
         mCallback =
                 (result) -> {
                     if (mCurrentPopulator == null) return;
 
-                    mSelectedItemBeforeDismiss = true;
                     mCurrentPopulator.onItemSelected(result);
                 };
         mOnMenuShown =
                 () -> {
-                    mSelectedItemBeforeDismiss = false;
-                    mMenuShownTimeMs = SystemClock.uptimeMillis();
                     RecordHistogram.recordBooleanHistogram(
                             "ContextMenu.Shown", mWebContents != null);
                     recordContextMenuShownType(params);
@@ -130,7 +120,6 @@
                 };
         mOnMenuClosed =
                 () -> {
-                    recordTimeToTakeActionHistogram(mSelectedItemBeforeDismiss);
                     mCurrentContextMenu = null;
                     if (mCurrentNativeDelegate != null) {
                         mCurrentNativeDelegate.destroy();
@@ -208,13 +197,6 @@
         }
     }
 
-    private void recordTimeToTakeActionHistogram(boolean selectedItem) {
-        final String histogramName =
-                "ContextMenu.TimeToTakeAction." + (selectedItem ? "SelectedItem" : "Abandoned");
-        final long timeToTakeActionMs = SystemClock.uptimeMillis() - mMenuShownTimeMs;
-        RecordHistogram.recordTimesHistogram(histogramName, timeToTakeActionMs);
-    }
-
     public static void setMenuShownCallbackForTests(Callback<ContextMenuCoordinator> callback) {
         sMenuShownCallbackForTesting = callback;
         ResettersForTesting.register(() -> sMenuShownCallbackForTesting = null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetBridge.java
similarity index 80%
rename from chrome/android/java/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetBridge.java
rename to chrome/android/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetBridge.java
index 2454ad6..e831dad10 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetBridge.java
@@ -2,7 +2,7 @@
 // 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.autofill.fp;
+package org.chromium.chrome.browser.facilitated_payments;
 
 import android.content.Context;
 
@@ -17,17 +17,16 @@
 import org.chromium.ui.base.WindowAndroid;
 
 /**
- * Bridge class providing an entry point for autofill client to trigger the
- * facilitated payment bottom sheet.
+ * Bridge class providing an entry point for facilitated payments client to trigger the bottom sheet.
  */
-@JNINamespace("autofill")
-public class FacilitatedPaymentBottomSheetBridge {
+@JNINamespace("payments::facilitated")
+public class FacilitatedPaymentsBottomSheetBridge {
     private Context mContext;
     private BottomSheetController mBottomSheetController;
 
     @CalledByNative
     @VisibleForTesting
-    /* package */ FacilitatedPaymentBottomSheetBridge() {}
+    /* package */ FacilitatedPaymentsBottomSheetBridge() {}
 
     /**
      * Requests to show the bottom sheet.
@@ -52,6 +51,6 @@
         return (mContext == null)
                 ? false
                 : mBottomSheetController.requestShowContent(
-                        new FacilitatedPaymentBottomSheetContent(mContext), /* animate= */ true);
+                        new FacilitatedPaymentsBottomSheetContent(mContext), /* animate= */ true);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetContent.java
similarity index 82%
rename from chrome/android/java/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetContent.java
rename to chrome/android/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetContent.java
index 90780e6..0be75ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetContent.java
@@ -2,7 +2,7 @@
 // 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.autofill.fp;
+package org.chromium.chrome.browser.facilitated_payments;
 
 import android.content.Context;
 import android.view.View;
@@ -12,11 +12,11 @@
 import org.chromium.chrome.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
-/** The contents of facilitated payment bottom sheet. */
-/*package*/ class FacilitatedPaymentBottomSheetContent implements BottomSheetContent {
+/** The contents of facilitated payments bottom sheet. */
+/*package*/ class FacilitatedPaymentsBottomSheetContent implements BottomSheetContent {
     private final View mView;
 
-    FacilitatedPaymentBottomSheetContent(Context context) {
+    FacilitatedPaymentsBottomSheetContent(Context context) {
         mView = new View(context);
     }
 
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 538bd99..13a342af 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
@@ -17,9 +17,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelFilter;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupColorUtils;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupTitleUtils;
 import org.chromium.url.GURL;
 
 import java.util.ArrayList;
@@ -122,7 +120,7 @@
         // Ensure that the color is set before merging the tabs into a group on restore, to indicate
         // that this is not going to be a new group creation.
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_GROUP_PARITY_ANDROID)) {
-            TabGroupColorUtils.storeTabGroupColor(rootId, color);
+            groupFilter.setTabGroupColor(rootId, color);
         }
 
         if (tabIds.length == 1) {
@@ -140,11 +138,12 @@
 
         if (title == null || title.isEmpty()) return;
 
-        TabGroupTitleUtils.storeTabGroupTitle(rootId, title);
+        groupFilter.setTabGroupTitle(rootId, title);
     }
 
     /**
      * Initializes this class with the given profile.
+     *
      * @param profile The {@link Profile} whose recently closed tabs will be queried.
      * @param tabModelSelector The {@link TabModelSelector} to use to get {@link TabModelFilter}s.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index 3d2d674..9bbaea2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -45,6 +45,7 @@
 import org.chromium.chrome.browser.flags.ActivityType;
 import org.chromium.chrome.browser.init.ActivityProfileProvider;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
+import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.SingleWindowKeyboardVisibilityDelegate;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.metrics.UmaActivityObserver;
@@ -556,12 +557,21 @@
 
     /** Mark that the UMA session has ended. */
     private void umaSessionResume() {
-        mUmaActivityObserver.startUmaSession(ActivityType.TABBED, null, getWindowAndroid());
+        ChromeBrowserInitializer.getInstance()
+                .runNowOrAfterFullBrowserStarted(
+                        () -> {
+                            mUmaActivityObserver.startUmaSession(
+                                    ActivityType.TABBED, null, getWindowAndroid());
+                        });
     }
 
     /** Mark that the UMA session has ended. */
     private void umaSessionEnd() {
-        mUmaActivityObserver.endUmaSession();
+        ChromeBrowserInitializer.getInstance()
+                .runNowOrAfterFullBrowserStarted(
+                        () -> {
+                            mUmaActivityObserver.endUmaSession();
+                        });
     }
 
     @Override
@@ -733,43 +743,19 @@
         int heightIncrease =
                 getResources()
                         .getDimensionPixelSize(
-                                OmniboxFeatures.shouldShowActiveColorOnOmnibox()
-                                        ? R.dimen.toolbar_url_focus_height_increase_active_color
-                                        : R.dimen
-                                                .toolbar_url_focus_height_increase_no_active_color);
+                                R.dimen.toolbar_url_focus_height_increase_active_color);
+        // TODO(crbug.com/330207609) : Comnbine the two values into one value in XML file.
         layoutParams.height =
                 getResources().getDimensionPixelSize(R.dimen.toolbar_height_no_shadow)
                         + heightIncrease;
         mAnchorView.setLayoutParams(layoutParams);
-
-        // Apply extra bottom padding for no active-color treatments.
-        if (!OmniboxFeatures.shouldShowActiveColorOnOmnibox()) {
-            int bottomPadding =
-                    getResources().getDimensionPixelSize(R.dimen.toolbar_url_focus_bottom_padding);
-            mAnchorView.setPaddingRelative(
-                    mAnchorView.getPaddingStart(),
-                    mAnchorView.getPaddingTop(),
-                    mAnchorView.getPaddingEnd(),
-                    bottomPadding);
-        }
     }
 
     /** Apply the color to locationbar's and toolbar's background. */
     private void applyColor(@ColorInt int color) {
-        if (!OmniboxFeatures.shouldShowModernizeVisualUpdate(SearchActivity.this)
-                || OmniboxFeatures.shouldShowActiveColorOnOmnibox()) {
-            return;
-        }
-
-        Drawable locationbarBackground =
-                mContentView.findViewById(R.id.search_location_bar).getBackground();
-        Drawable toolbarBackground = mContentView.findViewById(R.id.toolbar).getBackground();
-        locationbarBackground.setTint(color);
-        toolbarBackground.setTint(color);
-
-        if (OmniboxFeatures.shouldMatchToolbarAndStatusBarColor()) {
-            StatusBarColorController.setStatusBarColor(this.getWindow(), color);
-        }
+        // TODO(crbug.com/330060045) : clean up all the code to update the locationbar's and
+        // toolbar's background when scrolling.
+        return;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
index 0047ad1..32f1750 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
@@ -106,6 +106,7 @@
     @VisibleForTesting public static final String PREF_SYNC_READING_LIST = "sync_reading_list";
     @VisibleForTesting public static final String PREF_SYNC_RECENT_TABS = "sync_recent_tabs";
     @VisibleForTesting public static final String PREF_SYNC_SETTINGS = "sync_settings";
+    @VisibleForTesting public static final String PREF_SYNC_APPS = "sync_apps";
     @VisibleForTesting public static final String PREF_TURN_OFF_SYNC = "turn_off_sync";
     private static final String PREF_ADVANCED_CATEGORY = "advanced_category";
 
@@ -236,6 +237,13 @@
                 UserSelectableType.PAYMENTS, findPreference(PREF_SYNC_PAYMENTS_INTEGRATION));
         mSyncTypePreferencesMap.put(
                 UserSelectableType.PREFERENCES, findPreference(PREF_SYNC_SETTINGS));
+
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.WEB_APK_BACKUP_AND_RESTORE_BACKEND)) {
+            mSyncTypePreferencesMap.put(UserSelectableType.APPS, findPreference(PREF_SYNC_APPS));
+        } else {
+            findPreference(PREF_SYNC_APPS).setVisible(false);
+        }
+
         mSyncTypePreferencesMap.put(
                 UserSelectableType.READING_LIST, findPreference(PREF_SYNC_READING_LIST));
         mSyncTypePreferencesMap.put(UserSelectableType.TABS, findPreference(PREF_SYNC_RECENT_TABS));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS
index 102973a..b8210ff 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/OWNERS
@@ -1,5 +1,4 @@
 gangwu@chromium.org
 
 # Secondary
-donnd@chromium.org
 twellington@chromium.org
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadDialogIncognitoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadDialogIncognitoTest.java
index 6226add..40e9a943 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadDialogIncognitoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadDialogIncognitoTest.java
@@ -200,7 +200,7 @@
         CriteriaHelper.pollInstrumentationThread(
                 () -> {
                     try {
-                        onView(withId(R.id.message_paragraph_2))
+                        onView(withId(R.id.message_paragraph_2)).inRoot(isDialog())
                                 .check(matches(withEffectiveVisibility(visibility)));
                     } catch (NoMatchingViewException | AssertionError e) {
                         throw new CriteriaNotSatisfiedException(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
index 89f4518..a161ff7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
@@ -704,7 +704,7 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     mTabGroupModelFilter.mergeTabsToGroup(tabB.getId(), tabA.getId());
-                    TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Bar");
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Bar");
                     titles[1] = tabA.getTitle();
                     titles[0] = tabB.getTitle();
                     mTabModel.closeMultipleTabs(Arrays.asList(new Tab[] {tabA, tabB}), false);
@@ -766,7 +766,7 @@
                 () -> {
                     mTabGroupModelFilter.mergeTabsToGroup(tabB.getId(), tabA.getId());
                     mTabGroupModelFilter.mergeTabsToGroup(tabC.getId(), tabA.getId());
-                    TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Bar");
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Bar");
                     titles[1] = tabA.getTitle();
                     titles[0] = tabC.getTitle();
                     mTabModel.closeMultipleTabs(Arrays.asList(new Tab[] {tabA, tabC}), false);
@@ -822,7 +822,7 @@
                 () -> {
                     mTabGroupModelFilter.mergeTabsToGroup(tabB.getId(), tabA.getId());
                     mTabGroupModelFilter.mergeTabsToGroup(tabC.getId(), tabA.getId());
-                    TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Bar");
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Bar");
                     titles[1] = tabA.getTitle();
                     titles[0] = tabC.getTitle();
                     mTabModel.closeMultipleTabs(Arrays.asList(new Tab[] {tabA, tabC}), true);
@@ -878,7 +878,7 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     mTabGroupModelFilter.mergeTabsToGroup(tabB.getId(), tabA.getId());
-                    TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Bar");
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Bar");
                     titles[0] = tabA.getTitle();
                     mTabModel.closeMultipleTabs(Arrays.asList(new Tab[] {tabA}), true);
                     mTabModel.commitTabClosure(tabA.getId());
@@ -923,7 +923,7 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     mTabGroupModelFilter.createSingleTabGroup(tabA, /* notify= */ false);
-                    TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Bar");
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Bar");
                     titles[0] = tabA.getTitle();
                     mTabModel.closeMultipleTabs(Arrays.asList(new Tab[] {tabA}), false);
                 });
@@ -972,7 +972,7 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     mTabGroupModelFilter.createSingleTabGroup(tabA, /* notify= */ false);
-                    TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Bar");
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Bar");
                     titles[0] = tabA.getTitle();
                     mTabModel.closeMultipleTabs(Arrays.asList(new Tab[] {tabA}), true);
                     mTabModel.commitTabClosure(tabA.getId());
@@ -1025,7 +1025,7 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     mTabGroupModelFilter.createSingleTabGroup(tabA, /* notify= */ false);
-                    TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Bar");
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Bar");
                     titles[0] = tabA.getTitle();
                     mTabModel.closeMultipleTabs(Arrays.asList(new Tab[] {tabA}), false);
                 });
@@ -1087,7 +1087,7 @@
                 () -> {
                     mTabGroupModelFilter.mergeTabsToGroup(tabB.getId(), tabA.getId());
                     mTabGroupModelFilter.mergeTabsToGroup(tabC.getId(), tabA.getId());
-                    TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Bar");
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Bar");
                     titles[2] = tabA.getTitle();
                     titles[1] = tabB.getTitle();
                     titles[0] = tabC.getTitle();
@@ -1238,7 +1238,7 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     mTabGroupModelFilter.mergeTabsToGroup(tabB.getId(), tabA.getId());
-                    TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Foo");
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Foo");
                     titles[2] = tabA.getTitle();
                     titles[1] = tabB.getTitle();
                     titles[0] = tabC.getTitle();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewDarkModeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewDarkModeTest.java
index cb5fdd6..82c3db1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewDarkModeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewDarkModeTest.java
@@ -23,6 +23,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.chrome.browser.app.ChromeActivity;
@@ -131,6 +132,7 @@
     @MediumTest
     @Feature({"RenderTest"})
     @DisableFeatures(ChromeFeatureList.TRACKING_PROTECTION_3PCD)
+    @DisabledTest(message = "crbug.com/330166513")
     public void testShowOnSecureWebsiteDark() throws IOException {
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         mRenderTestRule.render(getPageInfoView(), "PageInfo_SecureWebsiteDark");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsExportTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsExportTest.java
index d27fa44..2ac496c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsExportTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsExportTest.java
@@ -210,6 +210,7 @@
     @Test
     @SmallTest
     @Feature({"Preferences"})
+    @DisabledTest(message = "crbug.com/330204151")
     public void testExportReauthAfterCancel() {
         mTestHelper.setPasswordSource(
                 new SavedPasswordEntry("https://example.com", "test user", "password"));
@@ -671,6 +672,7 @@
     @Test
     @SmallTest
     @Feature({"Preferences"})
+    @DisabledTest(message = "crbug.com/330204151")
     public void testExportWarningOnResume() {
         mTestHelper.setPasswordSource(
                 new SavedPasswordEntry("https://example.com", "test user", "password"));
@@ -789,6 +791,7 @@
     @SmallTest
     @DisableFeatures(UNIFIED_PASSWORD_MANAGER_LOCAL_PWD_MIGRATION_WARNING)
     @Feature({"Preferences"})
+    @DisabledTest(message = "crbug.com/330204151")
     public void testExportProgressMinimalTime() throws Exception {
         mTestHelper.setPasswordSource(
                 new SavedPasswordEntry("https://example.com", "test user", "password"));
@@ -1097,6 +1100,7 @@
     @Test
     @SmallTest
     @Feature({"Preferences"})
+    @DisabledTest(message = "crbug.com/330204151")
     public void testExportErrorUiAfterConfirmation() {
         mTestHelper.setPasswordSource(
                 new SavedPasswordEntry("https://example.com", "test user", "password"));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
index c953be7..605aaef 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
@@ -646,43 +646,6 @@
     @SmallTest
     @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
     @EnableFeatures(ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE)
-    @CommandLineFlags.Add({
-        ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
-        "enable-features=" + ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE + "<Study",
-        "force-fieldtrials=Study/Group",
-        "force-fieldtrial-params=Study.Group:modernize_visual_update_active_color_on_omnibox/false"
-    })
-    public void testupdateAnchorViewLayout_ActiveColorOff() {
-        SearchActivity searchActivity = startSearchActivity();
-        View anchorView = searchActivity.getAnchorViewForTesting();
-        var layoutParams = anchorView.getLayoutParams();
-
-        int expectedHeight =
-                searchActivity
-                                .getResources()
-                                .getDimensionPixelSize(R.dimen.toolbar_height_no_shadow)
-                        + searchActivity
-                                .getResources()
-                                .getDimensionPixelSize(
-                                        R.dimen.toolbar_url_focus_height_increase_no_active_color);
-        int expectedBottomPadding =
-                searchActivity
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.toolbar_url_focus_bottom_padding);
-
-        Assert.assertEquals(expectedHeight, layoutParams.height);
-        Assert.assertEquals(expectedBottomPadding, anchorView.getPaddingBottom());
-    }
-
-    @Test
-    @SmallTest
-    @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
-    @EnableFeatures(ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE)
-    @CommandLineFlags.Add({
-        "enable-features=" + ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE + "<Study",
-        "force-fieldtrials=Study/Group",
-        "force-fieldtrial-params=Study.Group:modernize_visual_update_active_color_on_omnibox/true"
-    })
     public void testupdateAnchorViewLayout_ActiveColorOn() {
         SearchActivity searchActivity = startSearchActivity();
         View anchorView = searchActivity.getAnchorViewForTesting();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
index 679e293..cfbe5d0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
@@ -93,20 +93,7 @@
     private static final int RENDER_TEST_REVISION = 5;
 
     /** Maps selected types to their UI element IDs. */
-    private static final Map<Integer, String> UI_DATATYPES = new HashMap<>();
-
-    static {
-        UI_DATATYPES.put(UserSelectableType.AUTOFILL, ManageSyncSettings.PREF_SYNC_AUTOFILL);
-        UI_DATATYPES.put(UserSelectableType.BOOKMARKS, ManageSyncSettings.PREF_SYNC_BOOKMARKS);
-        UI_DATATYPES.put(
-                UserSelectableType.PAYMENTS, ManageSyncSettings.PREF_SYNC_PAYMENTS_INTEGRATION);
-        UI_DATATYPES.put(UserSelectableType.HISTORY, ManageSyncSettings.PREF_SYNC_HISTORY);
-        UI_DATATYPES.put(UserSelectableType.PASSWORDS, ManageSyncSettings.PREF_SYNC_PASSWORDS);
-        UI_DATATYPES.put(
-                UserSelectableType.READING_LIST, ManageSyncSettings.PREF_SYNC_READING_LIST);
-        UI_DATATYPES.put(UserSelectableType.TABS, ManageSyncSettings.PREF_SYNC_RECENT_TABS);
-        UI_DATATYPES.put(UserSelectableType.PREFERENCES, ManageSyncSettings.PREF_SYNC_SETTINGS);
-    }
+    private Map<Integer, String> mUiDataTypes;
 
     private SettingsActivity mSettingsActivity;
 
@@ -157,6 +144,21 @@
 
         TemplateUrlServiceFactory.setInstanceForTesting(mTemplateUrlService);
         when(mTemplateUrlService.isEeaChoiceCountry()).thenReturn(false);
+
+        mUiDataTypes = new HashMap<>();
+        mUiDataTypes.put(UserSelectableType.AUTOFILL, ManageSyncSettings.PREF_SYNC_AUTOFILL);
+        mUiDataTypes.put(UserSelectableType.BOOKMARKS, ManageSyncSettings.PREF_SYNC_BOOKMARKS);
+        mUiDataTypes.put(
+                UserSelectableType.PAYMENTS, ManageSyncSettings.PREF_SYNC_PAYMENTS_INTEGRATION);
+        mUiDataTypes.put(UserSelectableType.HISTORY, ManageSyncSettings.PREF_SYNC_HISTORY);
+        mUiDataTypes.put(UserSelectableType.PASSWORDS, ManageSyncSettings.PREF_SYNC_PASSWORDS);
+        mUiDataTypes.put(
+                UserSelectableType.READING_LIST, ManageSyncSettings.PREF_SYNC_READING_LIST);
+        mUiDataTypes.put(UserSelectableType.TABS, ManageSyncSettings.PREF_SYNC_RECENT_TABS);
+        mUiDataTypes.put(UserSelectableType.PREFERENCES, ManageSyncSettings.PREF_SYNC_SETTINGS);
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.WEB_APK_BACKUP_AND_RESTORE_BACKEND)) {
+            mUiDataTypes.put(UserSelectableType.APPS, ManageSyncSettings.PREF_SYNC_APPS);
+        }
     }
 
     @After
@@ -380,7 +382,7 @@
     public void testPaymentsIntegrationUnchecked() {
         mSyncTestRule.setUpAccountAndEnableSyncForTesting();
 
-        Set<Integer> allDataTypesExceptPayments = new HashSet<>(UI_DATATYPES.keySet());
+        Set<Integer> allDataTypesExceptPayments = new HashSet<>(mUiDataTypes.keySet());
         allDataTypesExceptPayments.remove(UserSelectableType.PAYMENTS);
 
         mSyncTestRule.setSelectedTypes(false, allDataTypesExceptPayments);
@@ -422,7 +424,7 @@
         mSyncTestRule.setUpAccountAndEnableSyncForTesting();
         mSyncTestRule.disableDataType(UserSelectableType.PAYMENTS);
 
-        mSyncTestRule.setSelectedTypes(false, UI_DATATYPES.keySet());
+        mSyncTestRule.setSelectedTypes(false, mUiDataTypes.keySet());
         ManageSyncSettings fragment = startManageSyncPreferences();
 
         CheckBoxPreference paymentsIntegration =
@@ -818,6 +820,25 @@
     @Test
     @LargeTest
     @Feature({"Sync", "RenderTest"})
+    @EnableFeatures({ChromeFeatureList.WEB_APK_BACKUP_AND_RESTORE_BACKEND})
+    public void testAdvancedSyncFlowBottomViewWithWebApks() throws Exception {
+        mSyncTestRule.setUpAccountAndEnableSyncForTesting();
+        final ManageSyncSettings fragment = startManageSyncPreferences();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    RecyclerView recyclerView = fragment.getView().findViewById(R.id.recycler_view);
+                    // Sometimes the rendered image may not contain the scrollbar and cause
+                    // flakiness.
+                    // Hide the scrollbar altogether to reduce flakiness.
+                    recyclerView.setVerticalScrollBarEnabled(false);
+                    recyclerView.scrollToPosition(recyclerView.getAdapter().getItemCount() - 1);
+                });
+        render(fragment, "advanced_sync_flow_bottom_view_with_webapks");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"Sync", "RenderTest"})
     public void testAdvancedSyncFlowFromSyncConsentTopView() throws Exception {
         mSyncTestRule.setUpTestAccountAndSignInWithSyncSetupAsIncomplete();
         final ManageSyncSettings fragment = startManageSyncPreferencesFromSyncConsentFlow();
@@ -1185,7 +1206,7 @@
 
     private Map<Integer, CheckBoxPreference> getDataTypes(ManageSyncSettings fragment) {
         Map<Integer, CheckBoxPreference> dataTypes = new HashMap<>();
-        for (Map.Entry<Integer, String> uiDataType : UI_DATATYPES.entrySet()) {
+        for (Map.Entry<Integer, String> uiDataType : mUiDataTypes.entrySet()) {
             Integer selectedType = uiDataType.getKey();
             String prefId = uiDataType.getValue();
             dataTypes.put(selectedType, (CheckBoxPreference) fragment.findPreference(prefId));
@@ -1248,7 +1269,7 @@
     }
 
     private void assertSelectedTypesAre(final Set<Integer> enabledDataTypes) {
-        final Set<Integer> disabledDataTypes = new HashSet<>(UI_DATATYPES.keySet());
+        final Set<Integer> disabledDataTypes = new HashSet<>(mUiDataTypes.keySet());
         disabledDataTypes.removeAll(enabledDataTypes);
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenAddShortcutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenAddShortcutTest.java
index 3802eb8d..643566fb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenAddShortcutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenAddShortcutTest.java
@@ -25,6 +25,7 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
@@ -263,9 +264,16 @@
         Assert.assertEquals(WEBAPP_TITLE, mShortcutHelperDelegate.mRequestedShortcutTitle);
 
         Intent launchIntent = mShortcutHelperDelegate.mRequestedShortcutIntent;
-        Assert.assertEquals(WEBAPP_HTML, launchIntent.getStringExtra(WebappConstants.EXTRA_URL));
-        Assert.assertEquals(WEBAPP_ACTION_NAME, launchIntent.getAction());
-        Assert.assertEquals(mActivity.getPackageName(), launchIntent.getPackage());
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.PWA_UNIVERSAL_INSTALL_UI)) {
+            Assert.assertEquals(mActivity.getPackageName(), launchIntent.getPackage());
+            Assert.assertEquals(Intent.ACTION_VIEW, launchIntent.getAction());
+            Assert.assertEquals(WEBAPP_HTML, launchIntent.getDataString());
+        } else {
+            Assert.assertEquals(
+                    WEBAPP_HTML, launchIntent.getStringExtra(WebappConstants.EXTRA_URL));
+            Assert.assertEquals(WEBAPP_ACTION_NAME, launchIntent.getAction());
+            Assert.assertEquals(mActivity.getPackageName(), launchIntent.getPackage());
+        }
 
         // Add a second shortcut and make sure it matches the second webapp's
         // parameters.
@@ -275,10 +283,16 @@
         Assert.assertEquals(SECOND_WEBAPP_TITLE, mShortcutHelperDelegate.mRequestedShortcutTitle);
 
         Intent newLaunchIntent = mShortcutHelperDelegate.mRequestedShortcutIntent;
-        Assert.assertEquals(
-                SECOND_WEBAPP_HTML, newLaunchIntent.getStringExtra(WebappConstants.EXTRA_URL));
-        Assert.assertEquals(WEBAPP_ACTION_NAME, newLaunchIntent.getAction());
-        Assert.assertEquals(mActivity.getPackageName(), newLaunchIntent.getPackage());
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.PWA_UNIVERSAL_INSTALL_UI)) {
+            Assert.assertEquals(mActivity.getPackageName(), newLaunchIntent.getPackage());
+            Assert.assertEquals(Intent.ACTION_VIEW, newLaunchIntent.getAction());
+            Assert.assertEquals(SECOND_WEBAPP_HTML, newLaunchIntent.getDataString());
+        } else {
+            Assert.assertEquals(
+                    SECOND_WEBAPP_HTML, newLaunchIntent.getStringExtra(WebappConstants.EXTRA_URL));
+            Assert.assertEquals(WEBAPP_ACTION_NAME, newLaunchIntent.getAction());
+            Assert.assertEquals(mActivity.getPackageName(), newLaunchIntent.getPackage());
+        }
     }
 
     @Test
@@ -368,6 +382,7 @@
     @Test
     @SmallTest
     @Feature("{Webapp}")
+    @DisableFeatures({ChromeFeatureList.PWA_UNIVERSAL_INSTALL_UI})
     public void testAddWebappShortcutSplashScreenIcon() throws Exception {
         // Sets the overridden factory to observe splash screen update.
         final TestDataStorageFactory dataStorageFactory = new TestDataStorageFactory();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetBridgeTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetBridgeTest.java
similarity index 77%
rename from chrome/android/junit/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetBridgeTest.java
rename to chrome/android/junit/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetBridgeTest.java
index 200c8e0..75914fdd5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetBridgeTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetBridgeTest.java
@@ -2,7 +2,7 @@
 // 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.autofill.fp;
+package org.chromium.chrome.browser.facilitated_payments;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalTo;
@@ -37,15 +37,15 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
 
-/** Unit tests for {@link FacilitatedPaymentBottomSheetBridge}. */
+/** Unit tests for {@link FacilitatedPaymentsBottomSheetBridge}. */
 @RunWith(BaseRobolectricTestRunner.class)
-public class FacilitatedPaymentBottomSheetBridgeTest {
+public class FacilitatedPaymentsBottomSheetBridgeTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock private WebContents mWebContents;
     @Mock private ManagedBottomSheetController mBottomSheetController;
 
-    private FacilitatedPaymentBottomSheetBridge mFacilitatedPaymentBottomSheetBridge;
+    private FacilitatedPaymentsBottomSheetBridge mFacilitatedPaymentsBottomSheetBridge;
     private WindowAndroid mWindow;
 
     @Before
@@ -54,7 +54,7 @@
         Context mApplicationContext = ApplicationProvider.getApplicationContext();
         mWindow = new WindowAndroid(mApplicationContext);
         BottomSheetControllerFactory.attach(mWindow, mBottomSheetController);
-        mFacilitatedPaymentBottomSheetBridge = new FacilitatedPaymentBottomSheetBridge();
+        mFacilitatedPaymentsBottomSheetBridge = new FacilitatedPaymentsBottomSheetBridge();
     }
 
     @After
@@ -68,11 +68,11 @@
     public void requestShowContent_callsControllerRequestShowContent() {
         when(mWebContents.getTopLevelNativeWindow()).thenReturn(mWindow);
 
-        mFacilitatedPaymentBottomSheetBridge.requestShowContent(mWebContents);
+        mFacilitatedPaymentsBottomSheetBridge.requestShowContent(mWebContents);
 
         verify(mBottomSheetController)
                 .requestShowContent(
-                        any(FacilitatedPaymentBottomSheetContent.class), /* animate= */ eq(true));
+                        any(FacilitatedPaymentsBottomSheetContent.class), /* animate= */ eq(true));
     }
 
     @Test
@@ -80,13 +80,13 @@
     public void requestShowContent_bottomSheetContentImplIsStubbed() {
         when(mWebContents.getTopLevelNativeWindow()).thenReturn(mWindow);
 
-        mFacilitatedPaymentBottomSheetBridge.requestShowContent(mWebContents);
+        mFacilitatedPaymentsBottomSheetBridge.requestShowContent(mWebContents);
 
-        ArgumentCaptor<FacilitatedPaymentBottomSheetContent> contentCaptor =
-                ArgumentCaptor.forClass(FacilitatedPaymentBottomSheetContent.class);
+        ArgumentCaptor<FacilitatedPaymentsBottomSheetContent> contentCaptor =
+                ArgumentCaptor.forClass(FacilitatedPaymentsBottomSheetContent.class);
         verify(mBottomSheetController)
                 .requestShowContent(contentCaptor.capture(), /* animate= */ anyBoolean());
-        FacilitatedPaymentBottomSheetContent content = contentCaptor.getValue();
+        FacilitatedPaymentsBottomSheetContent content = contentCaptor.getValue();
         assertThat(content.getContentView(), notNullValue());
         assertThat(content.getSheetContentDescriptionStringId(), equalTo(R.string.ok));
         assertThat(content.getSheetHalfHeightAccessibilityStringId(), equalTo(R.string.ok));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetContentTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetContentTest.java
similarity index 87%
rename from chrome/android/junit/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetContentTest.java
rename to chrome/android/junit/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetContentTest.java
index 5ee73a6..ad8f5b0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/fp/FacilitatedPaymentBottomSheetContentTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsBottomSheetContentTest.java
@@ -2,7 +2,7 @@
 // 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.autofill.fp;
+package org.chromium.chrome.browser.facilitated_payments;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalTo;
@@ -21,18 +21,18 @@
 import org.chromium.chrome.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
-/** Unit test for {@link FacilitatedPaymentBottomSheetContent}. */
+/** Unit test for {@link FacilitatedPaymentsBottomSheetContent}. */
 @SmallTest
-public final class FacilitatedPaymentBottomSheetContentTest {
+public final class FacilitatedPaymentsBottomSheetContentTest {
     private Context mContext;
-    private FacilitatedPaymentBottomSheetContent mContent;
+    private FacilitatedPaymentsBottomSheetContent mContent;
     private View mView;
 
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.getApplication().getApplicationContext();
         mView = new View(mContext);
-        mContent = new FacilitatedPaymentBottomSheetContent(mContext);
+        mContent = new FacilitatedPaymentsBottomSheetContent(mContext);
     }
 
     @Test
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 472a5b5..ec3d699 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -6634,8 +6634,8 @@
   </message>
 
   <!-- Strings for orca-->
-  <message name="IDS_ACCNAME_ORCA" desc="The name for the orca feature in the task manager (this is a code name)">
-    Orca
+  <message name="IDS_ACCNAME_ORCA" desc="The name for the orca feature in the task manager">
+    Tool: Help me write
   </message>
 
   <!-- Strings for Custom Tab UI -->
diff --git a/chrome/app/chromeos_strings_grdp/IDS_ACCNAME_ORCA.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_ACCNAME_ORCA.png.sha1
index 66de33d1a..312196aa 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_ACCNAME_ORCA.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_ACCNAME_ORCA.png.sha1
@@ -1 +1 @@
-e6ea56898b4ef01c1ee1bc46a3bdb11eeef73d34
\ No newline at end of file
+c80d0a6e45549229bc9bf47530449b05791aa2c1
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 6ece9d4..fb12002a 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2628,66 +2628,26 @@
                desc="The title of a download notification: the current download status is cancelled. This message is for screen reader users.">
                Download cancelled: <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex></ph>
       </message>
-      <if expr="is_macosx">
-        <then>
-          <message name="IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT"
-                   desc="The title of a download notification: the current download status is finished successfully. This message is for screen reader users.">
-                   Download complete: <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex></ph>.
-          </message>
-        </then>
-        <else>
-          <message name="IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT"
-                   desc="The title of a download notification: the current download status is finished successfully. This message is for screen reader users.">
-                   Download complete: <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex></ph>. Press Shift+F6 to cycle to the downloads bar area.
-          </message>
-        </else>
-      </if>
-      <if expr="is_macosx">
-        <then>
-          <message name="IDS_PROMPT_APP_DEEP_SCANNING_ACCESSIBLE_ALERT"
-                   desc="The title of a download notification indicating that the file may be malicious, and the Advanced Protection Program recommends sending the file to Google. This message is for screen reader users">
-            <ph name="FILE_NAME">$1<ex>file.exe</ex></ph> may be dangerous. Send to Google Advanced Protection for scanning?
-          </message>
-        </then>
-        <else>
-          <message name="IDS_PROMPT_APP_DEEP_SCANNING_ACCESSIBLE_ALERT"
-                   desc="The title of a download notification indicating that the file may be malicious, and the Advanced Protection Program recommends sending the file to Google. This message is for screen reader users">
-            <ph name="FILE_NAME">$1<ex>file.exe</ex></ph> may be dangerous. Send to Google Advanced Protection for scanning? Press Shift+F6 to cycle to the downloads bar area.
-          </message>
-        </else>
-      </if>
-      <if expr="is_macosx">
-        <then>
-          <message name="IDS_PROMPT_DEEP_SCANNING_ACCESSIBLE_ALERT"
-                   desc="The title of a download notification indicating that the file may be malicious and Safe Browsing recommends sending the file to Google. This message is for screen reader users">
-            <ph name="FILE_NAME">$1<ex>file.exe</ex></ph> may be dangerous. Send to Google for scanning?
-          </message>
-        </then>
-        <else>
-          <message name="IDS_PROMPT_DEEP_SCANNING_ACCESSIBLE_ALERT"
-                   desc="The title of a download notification indicating that the file may be malicious and Safe Browsing recommends sending the file to Google. This message is for screen reader users">
-            <ph name="FILE_NAME">$1<ex>file.exe</ex></ph> may be dangerous. Send to Google for scanning? Press Shift+F6 to cycle to the downloads bar area.
-          </message>
-        </else>
-      </if>
+      <message name="IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT"
+                desc="The title of a download notification: the current download status is finished successfully. This message is for screen reader users.">
+                Download complete: <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex></ph>.
+      </message>
+      <message name="IDS_DOWNLOAD_PAUSED_ACCESSIBLE_ALERT"
+                desc="The title of a download notification: the current download status is paused. This message is for screen reader users.">
+                Download paused: <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex></ph>.
+      </message>
+      <message name="IDS_PROMPT_DEEP_SCANNING_ACCESSIBLE_ALERT"
+                desc="The title of a download notification indicating that the file may be malicious and Safe Browsing recommends sending the file to Google. This message is for screen reader users">
+        <ph name="FILE_NAME">$1<ex>file.exe</ex></ph> may be dangerous. Send to Google for scanning?
+      </message>
       <message name="IDS_DEEP_SCANNING_ACCESSIBLE_ALERT"
                desc="The title of a download notification indicating that the file may be malicious, and the Advanced Protection Program recommends sending the file to Google. This message is for screen reader users">
         <ph name="FILE_NAME">$1<ex>file.exe</ex></ph> is being scanned.
       </message>
-      <if expr="is_macosx">
-        <then>
-          <message name="IDS_PROMPT_DOWNLOAD_INSECURE_BLOCKED_ACCESSIBLE_ALERT"
-                   desc="The title of a download notification indicating that the download was delivered insecurely. This message is for screen reader users">
-            <ph name="FILE_NAME">$1<ex>file.exe</ex></ph> can't be downloaded securely.
-          </message>
-        </then>
-        <else>
-          <message name="IDS_PROMPT_DOWNLOAD_INSECURE_BLOCKED_ACCESSIBLE_ALERT"
-                   desc="The title of a download notification indicating that the download was delivered insecurely. This message is for screen reader users">
-            <ph name="FILE_NAME">$1<ex>file.exe</ex></ph> can't be downloaded securely. Press Shift+F6 to cycle to the downloads bar area.
-          </message>
-        </else>
-      </if>
+      <message name="IDS_PROMPT_DOWNLOAD_INSECURE_BLOCKED_ACCESSIBLE_ALERT"
+                desc="The title of a download notification indicating that the download was delivered insecurely. This message is for screen reader users">
+        <ph name="FILE_NAME">$1<ex>file.exe</ex></ph> can't be downloaded securely.
+      </message>
 
       <!-- Download Notification Labels -->
       <message name="IDS_DOWNLOAD_NOTIFICATION_COPY_TO_CLIPBOARD"
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT.png.sha1
new file mode 100644
index 0000000..bceb4d4
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT.png.sha1
@@ -0,0 +1 @@
+12466a6c896d5cee1e97aa40345dcd0348986be4
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_PAUSED_ACCESSIBLE_ALERT.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_PAUSED_ACCESSIBLE_ALERT.png.sha1
new file mode 100644
index 0000000..4942a5a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_PAUSED_ACCESSIBLE_ALERT.png.sha1
@@ -0,0 +1 @@
+646140a6d7580035ba90f7b251bef4704b7dd547
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_PROMPT_DEEP_SCANNING_ACCESSIBLE_ALERT.png.sha1 b/chrome/app/generated_resources_grd/IDS_PROMPT_DEEP_SCANNING_ACCESSIBLE_ALERT.png.sha1
index e69bc2a..5b830ca8 100644
--- a/chrome/app/generated_resources_grd/IDS_PROMPT_DEEP_SCANNING_ACCESSIBLE_ALERT.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_PROMPT_DEEP_SCANNING_ACCESSIBLE_ALERT.png.sha1
@@ -1 +1 @@
-7a03d64180861230e954c8b480ea2e301056745e
\ No newline at end of file
+93fd64d044d8a27f818f5dd88d2d9bd2313e449f
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_PROMPT_DOWNLOAD_INSECURE_BLOCKED_ACCESSIBLE_ALERT.png.sha1 b/chrome/app/generated_resources_grd/IDS_PROMPT_DOWNLOAD_INSECURE_BLOCKED_ACCESSIBLE_ALERT.png.sha1
index d627135f1..9d29887 100644
--- a/chrome/app/generated_resources_grd/IDS_PROMPT_DOWNLOAD_INSECURE_BLOCKED_ACCESSIBLE_ALERT.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_PROMPT_DOWNLOAD_INSECURE_BLOCKED_ACCESSIBLE_ALERT.png.sha1
@@ -1 +1 @@
-5a7ce03653dab66e283e8303fa279a44ea171a81
\ No newline at end of file
+ba75ecf7acbc931f2582354c5d8a927559281a19
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 7e9fdb67..bbbff4e 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -4849,6 +4849,9 @@
   <message name="IDS_SETTINGS_MULTIDEVICE_SUITE_TOGGLE_A11Y_LABEL" desc="Accessibility label for toggle button that enables/disables connected phone features.">
     Connected phone features enable.
   </message>
+  <message name="IDS_SETTINGS_MULTIDEVICE_INSTANT_HOTSPOT" desc="Name of a feature. This feature automatically offers the user to tether to their phone if their Chromebook is offline and their phone supports tethering.">
+    Instant hotspot
+  </message>
   <message name="IDS_SETTINGS_MULTIDEVICE_INSTANT_TETHERING" desc="Name of a feature. This feature automatically offers the user to tether to their phone if their Chromebook is offline and their phone supports tethering.">
     Instant Tethering
   </message>
@@ -5913,7 +5916,7 @@
     Allow system services to use your location?
   </message>
   <message name="IDS_SETTINGS_PRIVACY_HUB_GEOLOCATION_DIALOG_BODY" desc="Description of the dialog that prompts the user to enable system geolocation access. This gives detailed explanation of the outcomes of enabling system geolocation access for system services.">
-    This allows system services with the permission to use your location. Location may use sources like Wi-Fi, mobile networks, and sensors to help estimate your device's location. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+    This allows system services with permission to use your location. Location may use sources like Wi-Fi, mobile networks, and sensors to help estimate your device's location. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
   </message>
   <message name="IDS_SETTINGS_PRIVACY_HUB_GEOLOCATION_DIALOG_CONFIRM_BUTTON" desc="Label of the confirm button for the geolocation dialog, that enables geolocation for ChromeOS system services.">
     Allow
@@ -5997,6 +6000,10 @@
   <message name="IDS_SETTINGS_PER_DEVICE_KEYBOARD_KEY_DISABLED" desc="In keyboard remap keys subpage, the dropdown list item for a disabled key.">
     Disabled
   </message>
+   <!-- TODO(b:329880993): Translate following strings before launch. -->
+  <message name="IDS_SETTINGS_PER_DEVICE_KEYBOARD_KEY_RIGHT_ALT" translateable="false" desc="In keyboard remap keys subpage, the dropdown list item for a right alt key.">
+    right alt
+  </message>
   <message name="IDS_SETTINGS_PER_DEVICE_CONNECTED_A11Y_LABEL" desc="The message announced to screen readers when a device is connected.">
     <ph name="DEVICE_NAME">$1<ex>Internal Keyboard</ex></ph> has been connected
   </message>
@@ -6548,6 +6555,15 @@
   <message name="IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DISALLOWED" desc="In Geolocation setting subpage (under Privacy Controls), Dropdown list item to set system-wide geolocation access level. Blocks geolocation access to every geolocation client (including system services).">
     Blocked for all
   </message>
+  <message name="IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ALLOWED" desc="In Geolocation setting subpage (under Privacy Controls), Dropdown list item to set system-wide geolocation access level. ">
+    Apps, websites and system services with the location permission can use your location. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+  </message>
+  <message name="IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ONLY_ALLOWED_FOR_SYSTEM" desc="In Geolocation setting subpage (under Privacy Controls), Dropdown list item to set system-wide geolocation access level. Blocks access to all clients but system-services (such as PWAs or Android apps).">
+    Only system services can use your location. However, your location may still be visible to apps and websites through your IP address. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+  </message>
+  <message name="IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_DISALLOWED" desc="In Geolocation setting subpage (under Privacy Controls), Dropdown list item to set system-wide geolocation access level. Blocks geolocation access to every geolocation client (including system services).">
+    Nothing can use your location. However, your location may still be visible to apps and websites through your IP address. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+  </message>
   <message name="IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCURACY_TOGGLE_TEXT" desc="In Geolocation setting advance subpage (under Privacy Controls), text description for the toggle settings that controls location accuracy toggle.">
     <!-- TODO(vsomani): Figure out how we can add break in the below line. -->
     Google’s location service improves location accuracy by using Wi-Fi, mobile networks, and sensors to help estimate your location. Google may collect location data periodically and use this data in an anonymous way to improve location accuracy and location-based services.
@@ -6659,16 +6675,16 @@
     Blocked. Schedule is currently set to <ph name="SUNRISE_TIME">$1<ex>6am</ex></ph> - <ph name="SUNSET_TIME">$2<ex>6pm</ex></ph> and can only be updated manually.
   </message>
   <message name="IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_AUTOMATIC_TIME_ZONE_NAME" desc="The name label of the 'Automatic time zone' service displayed in the System services section of the privacy hub 'Location access' subpage.">
-    Automatic time zone
+    Automatic time zone based on Wi-Fi or mobile networks
   </message>
   <message name="IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_SUNSET_SCHEDULE_NAME" desc="The name label of the 'Sunset schedule' service displayed in the System services section of the privacy hub 'Location access' subpage.">
-    Sunset schedule
+    Automatic sunset schedule
   </message>
   <message name="IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_LOCAL_WEATHER_NAME" desc="The name label of the 'Local weather' service displayed in the System services section of the privacy hub 'Location access' subpage.">
     Local weather
   </message>
   <message name="IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_DARK_THEME_NAME" desc="The name label of the 'Wallpaper Dark Mode' service displayed in the System services section of the privacy hub 'Location access' subpage.">
-    Dark theme
+    Automatic light/dark theme
   </message>
   <message name="IDS_OS_SETTINGS_PRIVACY_HUB_CAMERA_TOGGLE_NO_CAMERA_CONNECTED_TOOLTIP_TEXT" desc="Text displayed in the privacy hub subpage camera toggle button tooltip when no camera is connected to the device.">
     No camera connected
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ALLOWED.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ALLOWED.png.sha1
new file mode 100644
index 0000000..f5845ed
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ALLOWED.png.sha1
@@ -0,0 +1 @@
+f95ffa8b1a9856e2ea2fb38ff1bc679261faec19
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_DISALLOWED.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_DISALLOWED.png.sha1
new file mode 100644
index 0000000..060a719
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_DISALLOWED.png.sha1
@@ -0,0 +1 @@
+19bf3d678d2d733530264d8c11a0861983fadd84
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ONLY_ALLOWED_FOR_SYSTEM.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ONLY_ALLOWED_FOR_SYSTEM.png.sha1
new file mode 100644
index 0000000..9d097cd
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ONLY_ALLOWED_FOR_SYSTEM.png.sha1
@@ -0,0 +1 @@
+115426467a8c62773b40abb9708876fbfd878836
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_AUTOMATIC_TIME_ZONE_NAME.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_AUTOMATIC_TIME_ZONE_NAME.png.sha1
index b4495002..54219ec 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_AUTOMATIC_TIME_ZONE_NAME.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_AUTOMATIC_TIME_ZONE_NAME.png.sha1
@@ -1 +1 @@
-39e0d3c32319833da92c95730612de2a38431b94
\ No newline at end of file
+27314fa794ac96ed3f9a67d2f11c230c9d84b75a
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_DARK_THEME_NAME.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_DARK_THEME_NAME.png.sha1
index b69d5eb..533c9769 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_DARK_THEME_NAME.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_DARK_THEME_NAME.png.sha1
@@ -1 +1 @@
-64c4dfaedcdd7f1a401205b847965d496831ceba
\ No newline at end of file
+58a0ffef7bbf2150aa10e75c48e6d7d27da39a3b
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_SUNSET_SCHEDULE_NAME.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_SUNSET_SCHEDULE_NAME.png.sha1
index b4495002..3dda817 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_SUNSET_SCHEDULE_NAME.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_SYSTEM_SERVICES_SUNSET_SCHEDULE_NAME.png.sha1
@@ -1 +1 @@
-39e0d3c32319833da92c95730612de2a38431b94
\ No newline at end of file
+7e51623eaaad27265032a4db455ccb7061895977
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_INSTANT_HOTSPOT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_INSTANT_HOTSPOT.png.sha1
new file mode 100644
index 0000000..061f7a79
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_INSTANT_HOTSPOT.png.sha1
@@ -0,0 +1 @@
+cf3cb9273273e208dad168f62f29da24b1ac8d3b
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIVACY_HUB_GEOLOCATION_DIALOG_BODY.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIVACY_HUB_GEOLOCATION_DIALOG_BODY.png.sha1
index 83fa3068..ed04e89 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIVACY_HUB_GEOLOCATION_DIALOG_BODY.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRIVACY_HUB_GEOLOCATION_DIALOG_BODY.png.sha1
@@ -1 +1 @@
-342de82e01660861b64d056de1aba012fca2f383
\ No newline at end of file
+b7bf516e9d541559b6e3ffa614f5e235dd41a9f6
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8dbe62b..8a9a57c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -145,8 +145,6 @@
     "accessibility/accessibility_labels_service_factory.h",
     "accessibility/accessibility_state_utils.cc",
     "accessibility/accessibility_state_utils.h",
-    "accessibility/embedded_a11y_extension_loader.cc",
-    "accessibility/embedded_a11y_extension_loader.h",
     "accessibility/page_colors.cc",
     "accessibility/page_colors.h",
     "accessibility/page_colors_factory.cc",
@@ -1338,8 +1336,6 @@
     "privacy/privacy_metrics_service.h",
     "privacy/privacy_metrics_service_factory.cc",
     "privacy/privacy_metrics_service_factory.h",
-    "privacy_sandbox/privacy_sandbox_notice_confirmation.cc",
-    "privacy_sandbox/privacy_sandbox_notice_confirmation.h",
     "privacy_sandbox/privacy_sandbox_policy_handler.cc",
     "privacy_sandbox/privacy_sandbox_policy_handler.h",
     "privacy_sandbox/privacy_sandbox_service.h",
@@ -1903,6 +1899,8 @@
 
   if (!is_android) {
     sources += [
+      "accessibility/embedded_a11y_extension_loader.cc",
+      "accessibility/embedded_a11y_extension_loader.h",
       "password_manager/password_manager_settings_service_impl.cc",
       "password_manager/password_manager_settings_service_impl.h",
       "picture_in_picture/auto_pip_setting_helper.cc",
@@ -3447,6 +3445,7 @@
       "//chrome/browser/content_extraction/android:jni_headers",
       "//chrome/browser/device_reauth/android:jni_headers",
       "//chrome/browser/download/internal/android",
+      "//chrome/browser/facilitated_payments/ui/android",
       "//chrome/browser/feed/android:jni_headers",
       "//chrome/browser/feedback/android",
       "//chrome/browser/feedback/android:jni_headers",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 975430d..4a8a44e4 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -9613,7 +9613,7 @@
 
     {"pwa-universal-install-ui", flag_descriptions::kPwaUniversalInstallUiName,
      flag_descriptions::kPwaUniversalInstallUiDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kPwaUniversalInstallUi)},
+     FEATURE_VALUE_TYPE(webapps::features::kPwaUniversalInstallUi)},
 #endif  // BUILDFLAG(IS_ANDROID)
     {"autofill-enable-ranking-formula-address-profiles",
      flag_descriptions::kAutofillEnableRankingFormulaAddressProfilesName,
@@ -11204,6 +11204,15 @@
      FEATURE_VALUE_TYPE(blink::features::kLowLatencyWebGLImageChromium)},
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    {"ash-forest-feature", flag_descriptions::kForestFeatureName,
+     flag_descriptions::kForestFeatureDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kForestFeature)},
+    {"ash-forest-feature-key", flag_descriptions::kForestKeyName,
+     flag_descriptions::kForestKeyDescription, kOsCrOS,
+     STRING_VALUE_TYPE(ash::switches::kForestFeatureKey, "")},
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/accessibility/embedded_a11y_extension_loader.cc b/chrome/browser/accessibility/embedded_a11y_extension_loader.cc
index 6329d81..eee35d6 100644
--- a/chrome/browser/accessibility/embedded_a11y_extension_loader.cc
+++ b/chrome/browser/accessibility/embedded_a11y_extension_loader.cc
@@ -4,11 +4,80 @@
 
 #include "chrome/browser/accessibility/embedded_a11y_extension_loader.h"
 
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/public/browser/browser_accessibility_state.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_file_task_runner.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension_l10n_util.h"
+#include "extensions/common/file_util.h"
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chrome/browser/lacros/embedded_a11y_manager_lacros.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+namespace {
+
+std::optional<base::Value::Dict> LoadManifestOnFileThread(
+    const base::FilePath& path,
+    const base::FilePath::CharType* manifest_filename,
+    bool localize) {
+  CHECK(extensions::GetExtensionFileTaskRunner()->RunsTasksInCurrentSequence());
+  std::string error;
+  auto manifest =
+      extensions::file_util::LoadManifest(path, manifest_filename, &error);
+  if (!manifest) {
+    LOG(ERROR) << "Can't load " << path.Append(manifest_filename).AsUTF8Unsafe()
+               << ": " << error;
+    return std::nullopt;
+  }
+  if (localize) {
+    // This is only called for Lacros component extensions which are loaded
+    // from a read-only rootfs partition, so it is safe to set
+    // |gzip_permission| to kAllowForTrustedSource.
+    bool localized = extension_l10n_util::LocalizeExtension(
+        path, &manifest.value(),
+        extension_l10n_util::GzippedMessagesPermission::kAllowForTrustedSource,
+        &error);
+    CHECK(localized) << error;
+  }
+  return manifest;
+}
+
+extensions::ComponentLoader* GetComponentLoader(Profile* profile) {
+  auto* extension_system = extensions::ExtensionSystem::Get(profile);
+  if (!extension_system) {
+    // May be missing on the Lacros login profile.
+    return nullptr;
+  }
+  auto* extension_service = extension_system->extension_service();
+  if (!extension_service) {
+    return nullptr;
+  }
+  return extension_service->component_loader();
+}
+
+}  // namespace
+
+EmbeddedA11yExtensionLoader::ExtensionInfo::ExtensionInfo(
+    const std::string& extension_id,
+    const std::string& extension_path,
+    const base::FilePath::CharType* extension_manifest_file,
+    bool should_localize)
+    : extension_id(extension_id),
+      extension_path(extension_path),
+      extension_manifest_file(extension_manifest_file),
+      should_localize(should_localize) {}
+EmbeddedA11yExtensionLoader::ExtensionInfo::ExtensionInfo(
+    const ExtensionInfo& other) = default;
+EmbeddedA11yExtensionLoader::ExtensionInfo::ExtensionInfo(ExtensionInfo&&) =
+    default;
+EmbeddedA11yExtensionLoader::ExtensionInfo::~ExtensionInfo() = default;
 
 // static
 EmbeddedA11yExtensionLoader* EmbeddedA11yExtensionLoader::GetInstance() {
@@ -21,16 +90,192 @@
 
 EmbeddedA11yExtensionLoader::~EmbeddedA11yExtensionLoader() = default;
 
+void EmbeddedA11yExtensionLoader::Init() {
+  if (initialized_) {
+    return;
+  }
+
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  profile_manager_observation_.Observe(profile_manager);
+
+  // Observe all existing profiles.
+  std::vector<Profile*> profiles =
+      g_browser_process->profile_manager()->GetLoadedProfiles();
+  for (auto* profile : profiles) {
+    observed_profiles_.AddObservation(profile);
+  }
+  for (const auto& extension : extension_map_) {
+    UpdateAllProfiles(extension.first);
+  }
+  initialized_ = true;
+}
+
 void EmbeddedA11yExtensionLoader::InstallA11yHelperExtensionForReadingMode() {
   // TODO(crbug.com/324143642): Install a11y helper extension for all platforms.
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  EmbeddedA11yManagerLacros::GetInstance()->SetReadingModeEnabled(true);
+  InstallExtensionWithId(extension_misc::kEmbeddedA11yHelperExtensionId,
+                         extension_misc::kEmbeddedA11yHelperExtensionPath,
+                         extension_misc::kEmbeddedA11yHelperManifestFilename,
+                         /*should_localize=*/true);
 #endif
 }
 
 void EmbeddedA11yExtensionLoader::RemoveA11yHelperExtensionForReadingMode() {
   // TODO(crbug.com/324143642): Remove a11y helper extension for all platforms.
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  EmbeddedA11yManagerLacros::GetInstance()->SetReadingModeEnabled(false);
+  RemoveExtensionWithId(extension_misc::kEmbeddedA11yHelperExtensionId);
 #endif
 }
+
+void EmbeddedA11yExtensionLoader::InstallExtensionWithId(
+    const std::string& extension_id,
+    const std::string& extension_path,
+    const base::FilePath::CharType* manifest_name,
+    bool should_localize) {
+  if (extension_map_.contains(extension_id)) {
+    return;
+  }
+
+  ExtensionInfo new_extension = {extension_id, extension_path, manifest_name,
+                                 should_localize};
+  extension_map_.insert({extension_id, new_extension});
+  UpdateAllProfiles(extension_id);
+}
+
+void EmbeddedA11yExtensionLoader::RemoveExtensionWithId(
+    const std::string& extension_id) {
+  if (!extension_map_.contains(extension_id)) {
+    return;
+  }
+
+  extension_map_.erase(extension_id);
+  UpdateAllProfiles(extension_id);
+}
+
+void EmbeddedA11yExtensionLoader::AddExtensionChangedCallbackForTest(
+    base::RepeatingClosure callback) {
+  extension_installation_changed_callback_for_test_ = std::move(callback);
+}
+
+void EmbeddedA11yExtensionLoader::OnProfileWillBeDestroyed(Profile* profile) {
+  observed_profiles_.RemoveObservation(profile);
+}
+
+void EmbeddedA11yExtensionLoader::OnOffTheRecordProfileCreated(
+    Profile* off_the_record) {
+  observed_profiles_.AddObservation(off_the_record);
+  for (const auto& extension : extension_map_) {
+    UpdateProfile(off_the_record, extension.first);
+  }
+}
+
+void EmbeddedA11yExtensionLoader::OnProfileAdded(Profile* profile) {
+  observed_profiles_.AddObservation(profile);
+  for (const auto& extension : extension_map_) {
+    UpdateProfile(profile, extension.first);
+  }
+}
+
+void EmbeddedA11yExtensionLoader::OnProfileManagerDestroying() {
+  profile_manager_observation_.Reset();
+}
+
+void EmbeddedA11yExtensionLoader::UpdateAllProfiles(
+    const std::string& extension_id) {
+  std::vector<Profile*> profiles =
+      g_browser_process->profile_manager()->GetLoadedProfiles();
+  for (auto* profile : profiles) {
+    UpdateProfile(profile, extension_id);
+    if (profile->HasAnyOffTheRecordProfile()) {
+      const auto& otr_profiles = profile->GetAllOffTheRecordProfiles();
+      for (auto* otr_profile : otr_profiles) {
+        UpdateProfile(otr_profile, extension_id);
+      }
+    }
+  }
+}
+
+void EmbeddedA11yExtensionLoader::UpdateProfile(
+    Profile* profile,
+    const std::string& extension_id) {
+  if (extension_map_.contains(extension_id)) {
+    ExtensionInfo& extension = extension_map_.at(extension_id);
+    MaybeInstallExtension(profile, extension_id, extension.extension_path.get(),
+                          extension.extension_manifest_file,
+                          extension.should_localize);
+  } else {
+    MaybeRemoveExtension(profile, extension_id);
+  }
+}
+
+void EmbeddedA11yExtensionLoader::MaybeRemoveExtension(
+    Profile* profile,
+    const std::string& extension_id) {
+  auto* component_loader = GetComponentLoader(profile);
+  if (!component_loader || !component_loader->Exists(extension_id)) {
+    return;
+  }
+  component_loader->Remove(extension_id);
+  if (extension_installation_changed_callback_for_test_) {
+    extension_installation_changed_callback_for_test_.Run();
+  }
+}
+
+void EmbeddedA11yExtensionLoader::MaybeInstallExtension(
+    Profile* profile,
+    const std::string& extension_id,
+    const std::string& extension_path,
+    const base::FilePath::CharType* manifest_name,
+    bool should_localize) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  auto* component_loader = GetComponentLoader(profile);
+  if (!component_loader || component_loader->Exists(extension_id)) {
+    return;
+  }
+
+  base::FilePath resources_path;
+  if (!base::PathService::Get(chrome::DIR_RESOURCES, &resources_path)) {
+    NOTREACHED();
+  }
+
+  base::FilePath::StringType common_path;
+#if BUILDFLAG(IS_WIN)
+  common_path = base::UTF8ToWide(extension_path);
+#else
+  common_path = extension_path;
+#endif
+
+  auto path = resources_path.Append(common_path);
+
+  extensions::GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&LoadManifestOnFileThread, path, manifest_name,
+                     /*localize=*/should_localize),
+      base::BindOnce(&EmbeddedA11yExtensionLoader::InstallExtension,
+                     weak_ptr_factory_.GetWeakPtr(), component_loader, path,
+                     extension_id));
+}
+
+void EmbeddedA11yExtensionLoader::InstallExtension(
+    extensions::ComponentLoader* component_loader,
+    const base::FilePath& path,
+    const std::string& extension_id,
+    std::optional<base::Value::Dict> manifest) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (component_loader->Exists(extension_id)) {
+    // Because this is async and called from another thread, it's possible we
+    // already installed the extension. Don't try and reinstall in that case.
+    // This may happen on init, for example, when ash a11y feature state and
+    // new profiles are loaded all at the same time.
+    return;
+  }
+
+  CHECK(manifest) << "Unable to load extension manifest for extension "
+                  << extension_id;
+  std::string actual_id =
+      component_loader->Add(std::move(manifest.value()), path);
+  CHECK_EQ(actual_id, extension_id);
+  if (extension_installation_changed_callback_for_test_) {
+    extension_installation_changed_callback_for_test_.Run();
+  }
+}
diff --git a/chrome/browser/accessibility/embedded_a11y_extension_loader.h b/chrome/browser/accessibility/embedded_a11y_extension_loader.h
index 9c4e87e..602c04c9 100644
--- a/chrome/browser/accessibility/embedded_a11y_extension_loader.h
+++ b/chrome/browser/accessibility/embedded_a11y_extension_loader.h
@@ -7,6 +7,18 @@
 
 #include "base/memory/singleton.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_multi_source_observation.h"
+#include "base/scoped_observation.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_manager_observer.h"
+#include "chrome/browser/profiles/profile_observer.h"
+
+namespace extensions {
+class ComponentLoader;
+}
+
+class Profile;
 
 ///////////////////////////////////////////////////////////////////////////////
 // EmbeddedA11yExtensionLoader
@@ -16,24 +28,104 @@
 // incognito) for Chrome Accessibility services and features on all platforms
 // except Lacros, where it just informs EmbeddedA11yHelperLacros.
 //
-class EmbeddedA11yExtensionLoader {
+class EmbeddedA11yExtensionLoader : public ProfileObserver,
+                                    public ProfileManagerObserver {
  public:
+  // Simple struct to hold information about each extension installed on all
+  // profiles.
+  struct ExtensionInfo {
+    ExtensionInfo(const std::string& extension_id,
+                  const std::string& extension_path,
+                  const base::FilePath::CharType* extension_manifest_file,
+                  bool should_localize);
+    ExtensionInfo(const ExtensionInfo& other);
+    ExtensionInfo(ExtensionInfo&&);
+    ExtensionInfo& operator=(const ExtensionInfo&);
+    ExtensionInfo& operator=(ExtensionInfo&&);
+    ~ExtensionInfo();
+
+    // The id of the extension.
+    const raw_ref<const std::string> extension_id;
+
+    // The path to the extension manifest file.
+    const raw_ref<const std::string> extension_path;
+
+    // The name of the extension manifest file.
+    const base::FilePath::CharType* extension_manifest_file;
+
+    // Whether the extension should be localized or not.
+    bool should_localize;
+  };
   // Gets the current instance of EmbeddedA11yExtensionLoader. There
   // should be one of these across all profiles.
   static EmbeddedA11yExtensionLoader* GetInstance();
 
   EmbeddedA11yExtensionLoader();
-  virtual ~EmbeddedA11yExtensionLoader();
+  ~EmbeddedA11yExtensionLoader() override;
   EmbeddedA11yExtensionLoader(EmbeddedA11yExtensionLoader&) = delete;
   EmbeddedA11yExtensionLoader& operator=(EmbeddedA11yExtensionLoader&) = delete;
 
+  // Should be called when the browser starts up.
+  void Init();
+
   // TODO(crbug.com/324143642): Observe the reading mode enabled/disabled state
   // in this class instead of informing EmbeddedA11yManagerLacros to
   // enable/disable reading mode.
   virtual void InstallA11yHelperExtensionForReadingMode();
   virtual void RemoveA11yHelperExtensionForReadingMode();
 
+  void InstallExtensionWithId(const std::string& extension_id,
+                              const std::string& extension_path,
+                              const base::FilePath::CharType* manifest_name,
+                              bool should_localize);
+  void RemoveExtensionWithId(const std::string& extension_id);
+
+  // We can't use extensions::ExtensionHostTestHelper as those require a
+  // background page, and these extensions do not have background pages.
+  void AddExtensionChangedCallbackForTest(base::RepeatingClosure callback);
+
  private:
+  // ProfileObserver:
+  void OnProfileWillBeDestroyed(Profile* profile) override;
+  void OnOffTheRecordProfileCreated(Profile* off_the_record) override;
+
+  // ProfileManagerObserver:
+  void OnProfileAdded(Profile* profile) override;
+  void OnProfileManagerDestroying() override;
+
+  void UpdateAllProfiles(const std::string& extension_id);
+  void UpdateProfile(Profile* profile, const std::string& extension_id);
+
+  // Removes the helper extension with `extension_id` from the given `profile`
+  // if it is installed.
+  void MaybeRemoveExtension(Profile* profile, const std::string& extension_id);
+
+  // Installs the helper extension with `extension_id` from the given `profile`
+  // if it isn't yet installed.
+  void MaybeInstallExtension(Profile* profile,
+                             const std::string& extension_id,
+                             const std::string& extension_path,
+                             const base::FilePath::CharType* manifest_name,
+                             bool should_localize);
+
+  // Installs the helper extension with the given `extension_id`, `manifest` and
+  // `path` using the given `component_loader` for some profile.
+  void InstallExtension(extensions::ComponentLoader* component_loader,
+                        const base::FilePath& path,
+                        const std::string& extension_id,
+                        std::optional<base::Value::Dict> manifest);
+
+  bool initialized_ = false;
+  // A map to store all accessibility helper extensions installed.
+  std::map<std::string, ExtensionInfo> extension_map_;
+
+  base::RepeatingClosure extension_installation_changed_callback_for_test_;
+
+  base::ScopedMultiSourceObservation<Profile, ProfileObserver>
+      observed_profiles_{this};
+  base::ScopedObservation<ProfileManager, ProfileManagerObserver>
+      profile_manager_observation_{this};
+
   base::WeakPtrFactory<EmbeddedA11yExtensionLoader> weak_ptr_factory_{this};
 
   friend struct base::DefaultSingletonTraits<EmbeddedA11yExtensionLoader>;
diff --git a/chrome/browser/accessibility/embedded_a11y_extension_loader_browsertest.cc b/chrome/browser/accessibility/embedded_a11y_extension_loader_browsertest.cc
index 503f406..fd64aa38 100644
--- a/chrome/browser/accessibility/embedded_a11y_extension_loader_browsertest.cc
+++ b/chrome/browser/accessibility/embedded_a11y_extension_loader_browsertest.cc
@@ -4,8 +4,16 @@
 
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/accessibility/embedded_a11y_extension_loader.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile_test_util.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/common/extensions/extension_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/extension_system.h"
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chrome/browser/lacros/embedded_a11y_manager_lacros.h"
@@ -19,27 +27,186 @@
       delete;
   EmbeddedA11yExtensionLoaderTest& operator=(
       const EmbeddedA11yExtensionLoaderTest&) = delete;
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    auto* embedded_a11y_extension_loader =
+        EmbeddedA11yExtensionLoader::GetInstance();
+    embedded_a11y_extension_loader->AddExtensionChangedCallbackForTest(
+        base::BindRepeating(
+            &EmbeddedA11yExtensionLoaderTest::OnExtensionChanged,
+            base::Unretained(this)));
+  }
+
+  void WaitForExtensionLoaded(Profile* profile,
+                              const std::string& extension_id) {
+    extensions::ComponentLoader* component_loader =
+        extensions::ExtensionSystem::Get(profile)
+            ->extension_service()
+            ->component_loader();
+
+    while (!component_loader->Exists(extension_id)) {
+      waiter_ = std::make_unique<base::RunLoop>();
+      waiter_->Run();
+    }
+
+    EXPECT_TRUE(component_loader->Exists(extension_id));
+  }
+
+  void WaitForExtensionUnloaded(Profile* profile,
+                                const std::string& extension_id) {
+    extensions::ComponentLoader* component_loader =
+        extensions::ExtensionSystem::Get(profile)
+            ->extension_service()
+            ->component_loader();
+    while (component_loader->Exists(extension_id)) {
+      waiter_ = std::make_unique<base::RunLoop>();
+      waiter_->Run();
+    }
+
+    EXPECT_FALSE(component_loader->Exists(extension_id));
+  }
+
+  void InstallAndWaitForExtensionLoaded(
+      Profile* profile,
+      const std::string& extension_id,
+      const std::string& extension_path,
+      const base::FilePath::CharType* manifest_name,
+      bool should_localize) {
+    auto* embedded_a11y_extension_loader =
+        EmbeddedA11yExtensionLoader::GetInstance();
+    embedded_a11y_extension_loader->InstallExtensionWithId(
+        extension_id, extension_path, manifest_name, should_localize);
+    WaitForExtensionLoaded(profile, extension_id);
+  }
+
+  void RemoveAndWaitForExtensionUnloaded(Profile* profile,
+                                         const std::string& extension_id) {
+    auto* embedded_a11y_extension_loader =
+        EmbeddedA11yExtensionLoader::GetInstance();
+    embedded_a11y_extension_loader->RemoveExtensionWithId(extension_id);
+    WaitForExtensionUnloaded(profile, extension_id);
+  }
+
+ private:
+  void OnExtensionChanged() {
+    if (waiter_ && waiter_->running()) {
+      waiter_->Quit();
+    }
+  }
+
+  std::unique_ptr<base::RunLoop> waiter_;
 };
 
+// TODO(b/324143642): test with non-Lacros extensions. Currently, the following
+// tests are tested with a Lacros extension embedded_a11y_helper_extension.
+// These tests should be tested with a cross-platform extension once the
+// extension is setup successfully.
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 IN_PROC_BROWSER_TEST_F(EmbeddedA11yExtensionLoaderTest,
-                       InstallsAndRemovesExtension) {
+                       InstallsAndRemovesExtensionForReadingMode) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  const auto& profiles = profile_manager->GetLoadedProfiles();
+  ASSERT_GT(profiles.size(), 0u);
+  Profile* profile = profiles[0];
+
   auto* embedded_a11y_extension_loader =
       EmbeddedA11yExtensionLoader::GetInstance();
-  EXPECT_FALSE(
-      EmbeddedA11yManagerLacros::GetInstance()->IsReadingModeEnabled());
-
   embedded_a11y_extension_loader->InstallA11yHelperExtensionForReadingMode();
-  EXPECT_TRUE(EmbeddedA11yManagerLacros::GetInstance()->IsReadingModeEnabled());
-  // Call InstallA11yHelperExtensionForReadingMode a second time, nothing bad
-  // happens.
-  embedded_a11y_extension_loader->InstallA11yHelperExtensionForReadingMode();
+  WaitForExtensionLoaded(profile,
+                         extension_misc::kEmbeddedA11yHelperExtensionId);
 
   embedded_a11y_extension_loader->RemoveA11yHelperExtensionForReadingMode();
-  EXPECT_FALSE(
-      EmbeddedA11yManagerLacros::GetInstance()->IsReadingModeEnabled());
-  // Call RemoveA11yHelperExtensionForReadingMode a second time, nothing bad
-  // happens.
+  WaitForExtensionUnloaded(profile,
+                           extension_misc::kEmbeddedA11yHelperExtensionId);
+}
+
+IN_PROC_BROWSER_TEST_F(EmbeddedA11yExtensionLoaderTest,
+                       InstallsRemovesAndReinstallsExtension) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  const auto& profiles = profile_manager->GetLoadedProfiles();
+  ASSERT_GT(profiles.size(), 0u);
+  Profile* profile = profiles[0];
+
+  InstallAndWaitForExtensionLoaded(
+      profile, extension_misc::kEmbeddedA11yHelperExtensionId,
+      extension_misc::kEmbeddedA11yHelperExtensionPath,
+      extension_misc::kEmbeddedA11yHelperManifestFilename,
+      /*should_localize=*/true);
+  RemoveAndWaitForExtensionUnloaded(
+      profile, extension_misc::kEmbeddedA11yHelperExtensionId);
+  InstallAndWaitForExtensionLoaded(
+      profile, extension_misc::kEmbeddedA11yHelperExtensionId,
+      extension_misc::kEmbeddedA11yHelperExtensionPath,
+      extension_misc::kEmbeddedA11yHelperManifestFilename,
+      /*should_localize=*/true);
+  RemoveAndWaitForExtensionUnloaded(
+      profile, extension_misc::kEmbeddedA11yHelperExtensionId);
+}
+
+IN_PROC_BROWSER_TEST_F(EmbeddedA11yExtensionLoaderTest,
+                       InstallsOnMultipleProfiles) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  size_t num_extra_profiles = 2;
+  for (size_t i = 0; i < num_extra_profiles; i++) {
+    // Create an additional profile.
+    base::FilePath path_profile =
+        profile_manager->GenerateNextProfileDirectoryPath();
+    profiles::testing::CreateProfileSync(profile_manager, path_profile);
+
+    // Open a browser window for the profile.
+    profiles::SwitchToProfile(path_profile, false);
+    content::RunAllTasksUntilIdle();
+  }
+
+  EXPECT_EQ(profile_manager->GetNumberOfProfiles(), num_extra_profiles + 1);
+  const auto& profiles = profile_manager->GetLoadedProfiles();
+
+  // Install extension for Reading Mode.
+  auto* embedded_a11y_extension_loader =
+      EmbeddedA11yExtensionLoader::GetInstance();
+  embedded_a11y_extension_loader->InstallA11yHelperExtensionForReadingMode();
+  for (auto* const profile : profiles) {
+    WaitForExtensionLoaded(profile,
+                           extension_misc::kEmbeddedA11yHelperExtensionId);
+  }
+
+  // Remove the extension.
   embedded_a11y_extension_loader->RemoveA11yHelperExtensionForReadingMode();
+  for (auto* const profile : profiles) {
+    WaitForExtensionUnloaded(profile,
+                             extension_misc::kEmbeddedA11yHelperExtensionId);
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(EmbeddedA11yExtensionLoaderTest,
+                       InstallsOnIncognitoProfile) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  Browser* incognito =
+      CreateIncognitoBrowser(profile_manager->GetPrimaryUserProfile());
+  content::RunAllTasksUntilIdle();
+
+  InstallAndWaitForExtensionLoaded(
+      incognito->profile(), extension_misc::kEmbeddedA11yHelperExtensionId,
+      extension_misc::kEmbeddedA11yHelperExtensionPath,
+      extension_misc::kEmbeddedA11yHelperManifestFilename,
+      /*should_localize=*/true);
+  RemoveAndWaitForExtensionUnloaded(
+      incognito->profile(), extension_misc::kEmbeddedA11yHelperExtensionId);
+}
+
+IN_PROC_BROWSER_TEST_F(EmbeddedA11yExtensionLoaderTest,
+                       InstallsOnGuestProfile) {
+  Browser* guest_browser = CreateGuestBrowser();
+  content::RunAllTasksUntilIdle();
+
+  InstallAndWaitForExtensionLoaded(
+      guest_browser->profile(), extension_misc::kEmbeddedA11yHelperExtensionId,
+      extension_misc::kEmbeddedA11yHelperExtensionPath,
+      extension_misc::kEmbeddedA11yHelperManifestFilename,
+      /*should_localize=*/true);
+  RemoveAndWaitForExtensionUnloaded(
+      guest_browser->profile(), extension_misc::kEmbeddedA11yHelperExtensionId);
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index a9f73ba5..01eb7e5 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -1975,6 +1975,28 @@
   return browser && browser->is_type_normal();
 }
 
+- (NSWindow*)targetWindowFromWindow:(NSWindow*)window {
+  NSWindow* targetWindow = window;
+
+  // In immersive fullscreen if `targetWindow` is a child (a popover or
+  // bubble) the browser window should handle the command. Walk up the
+  // window tree until the root window is found, this will be the browser
+  // window.
+  if (base::FeatureList::IsEnabled(features::kImmersiveFullscreen)) {
+    while (targetWindow.parentWindow) {
+      targetWindow = targetWindow.parentWindow;
+    }
+    return targetWindow;
+  }
+
+  // If `targetWindow` is a child (a popover or bubble) the parent should
+  // handle the command.
+  if (targetWindow.parentWindow) {
+    targetWindow = targetWindow.parentWindow;
+  }
+  return targetWindow;
+}
+
 - (void)updateMenuItemKeyEquivalents {
   id target = [NSApp targetForAction:@selector(performClose:)];
 
@@ -1989,12 +2011,8 @@
     targetWindow = base::apple::ObjCCast<NSWindow>(target);
   }
 
-  if (targetWindow != nil) {
-    // If `targetWindow` is a child (a popover or bubble) the parent should
-    // handle the command.
-    if ([targetWindow parentWindow] != nil) {
-      targetWindow = [targetWindow parentWindow];
-    }
+  if (targetWindow) {
+    targetWindow = [self targetWindowFromWindow:targetWindow];
   }
 
   NSMenuItem* closeTabMenuItem = [self closeTabMenuItem];
diff --git a/chrome/browser/app_controller_mac_unittest.mm b/chrome/browser/app_controller_mac_unittest.mm
index 5d541cf..31aa3297 100644
--- a/chrome/browser/app_controller_mac_unittest.mm
+++ b/chrome/browser/app_controller_mac_unittest.mm
@@ -18,6 +18,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -279,6 +280,32 @@
   CheckMenuItemsMatchBrowserWindow();
 }
 
+// Tests key equivalents for Close Window when target is a descendant of a
+// browser window.
+TEST_F(AppControllerKeyEquivalentTest,
+       UpdateMenuItemsForBrowserWindowDescendant) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kImmersiveFullscreen);
+
+  // Set up the browser window.
+  const NSRect kContentRect = NSMakeRect(0.0, 0.0, 10.0, 10.0);
+  NSWindow* browser_window =
+      [[FakeBrowserWindow alloc] initWithContentRect:kContentRect
+                                           styleMask:NSWindowStyleMaskClosable
+                                             backing:NSBackingStoreBuffered
+                                               defer:YES];
+
+  // Set up descendants.
+  NSWindow* child_window = [[NSWindow alloc] init];
+  [browser_window addChildWindow:child_window ordered:NSWindowAbove];
+  NSWindow* child_child_window = [[NSWindow alloc] init];
+  [child_window addChildWindow:child_child_window ordered:NSWindowAbove];
+
+  *TargetForAction() = child_child_window;
+
+  CheckMenuItemsMatchBrowserWindow();
+}
+
 // Tests key equivalents for Close Window when target is not a browser window.
 TEST_F(AppControllerKeyEquivalentTest, UpdateMenuItemsForNonBrowserWindow) {
   // Set up the window.
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc
index 9bf98326..c3f3355 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -474,6 +474,12 @@
   manager->Initialize(static_cast<int>(Sound::kStartup),
                       bundle.GetRawDataResource(IDR_SOUND_STARTUP_WAV),
                       media::AudioCodec::kPCM);
+  manager->Initialize(static_cast<int>(Sound::kLock),
+                      bundle.GetRawDataResource(IDR_SOUND_LOCK_WAV),
+                      media::AudioCodec::kPCM);
+  manager->Initialize(static_cast<int>(Sound::kUnlock),
+                      bundle.GetRawDataResource(IDR_SOUND_UNLOCK_WAV),
+                      media::AudioCodec::kPCM);
 
   if (VolumeAdjustSoundEnabled()) {
     manager->Initialize(static_cast<int>(Sound::kVolumeAdjust),
@@ -1848,6 +1854,20 @@
   SetActiveProfile();
 }
 
+void AccessibilityManager::OnSessionStateChanged() {
+  if (session_manager::SessionManager::Get()->session_state() ==
+      session_manager::SessionState::LOCKED) {
+    // Enter into the lock screen.
+    CHECK(!locked_);
+    locked_ = true;
+    PlayEarcon(Sound::kLock, PlaySoundOption::kOnlyIfSpokenFeedbackEnabled);
+  } else if (locked_) {
+    // Exit from the lock screen.
+    locked_ = false;
+    PlayEarcon(Sound::kUnlock, PlaySoundOption::kOnlyIfSpokenFeedbackEnabled);
+  }
+}
+
 void AccessibilityManager::SetActiveProfile() {
   Profile* profile = ProfileManager::GetActiveUserProfile();
   if (IsSigninBrowserContext(profile)) {
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.h b/chrome/browser/ash/accessibility/accessibility_manager.h
index 00d585e..13a1784 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.h
+++ b/chrome/browser/ash/accessibility/accessibility_manager.h
@@ -550,6 +550,7 @@
 
   // session_manager::SessionManagerObserver:
   void OnLoginOrLockScreenVisible() override;
+  void OnSessionStateChanged() override;
 
   // Sets the current profile using the active profile.
   void SetActiveProfile();
@@ -709,6 +710,9 @@
   // Whether the virtual keyboard was enabled before Switch Access loaded.
   bool was_vk_enabled_before_switch_access_ = false;
 
+  // Tracks whether or not on the locked screen currently.
+  bool locked_ = false;
+
   InstallFaceGazeAssetsCallback install_facegaze_assets_callback_;
 
   InstallPumpkinCallback install_pumpkin_callback_;
diff --git a/chrome/browser/ash/app_restore/full_restore_service.cc b/chrome/browser/ash/app_restore/full_restore_service.cc
index d82cb50c..6b06f8f 100644
--- a/chrome/browser/ash/app_restore/full_restore_service.cc
+++ b/chrome/browser/ash/app_restore/full_restore_service.cc
@@ -282,6 +282,9 @@
       MaybeInitiateAdminTemplateAutoLaunch();
       break;
     case RestoreOption::kDoNotRestore:
+      if (features::IsForestFeatureEnabled()) {
+        MaybeStartPineOverviewSession(/*last_session_crashed=*/false);
+      }
       ::full_restore::FullRestoreSaveHandler::GetInstance()->AllowSave();
       MaybeInitiateAdminTemplateAutoLaunch();
       return;
@@ -318,15 +321,10 @@
   // shutdown process.
   crashed_lock_.reset();
 
-  accelerator_controller_observer_.Reset();
-
   if (notification_ && !is_shut_down_) {
     NotificationDisplayService::GetForProfile(profile_)->Close(
         NotificationHandler::Type::TRANSIENT, notification_->id());
-  }
-
-  if (features::IsForestFeatureEnabled()) {
-    delegate_->MaybeEndPineOverviewSession();
+    accelerator_controller_observer_.Reset();
   }
 
   if (allow_save) {
@@ -472,8 +470,10 @@
 
 void FullRestoreService::MaybeShowRestoreNotification(const std::string& id,
                                                       bool& show_notification) {
-  if (!ShouldShowNotification())
+  if (!app_launch_handler_ || ::first_run::IsChromeFirstRun() ||
+      close_notification_) {
     return;
+  }
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           kForceFullRestoreAndSessionRestoreAfterCrash)) {
@@ -482,20 +482,23 @@
     return;
   }
 
+  const bool last_session_crashed = id == kRestoreForCrashNotificationId;
+  if (!app_launch_handler_->HasRestoreData()) {
+    if (features::IsForestFeatureEnabled()) {
+      MaybeStartPineOverviewSession(last_session_crashed);
+    }
+    return;
+  }
+  CHECK(app_launch_handler_->HasRestoreData());
+
   // If the system is restored from crash, create the crash lock for the browser
   // session restore to help set the browser saving flag.
   ExitTypeService* exit_type_service =
       ExitTypeService::GetInstanceForProfile(profile_);
-  const bool last_session_crashed = id == kRestoreForCrashNotificationId;
   if (last_session_crashed && exit_type_service) {
     crashed_lock_ = exit_type_service->CreateCrashedLock();
   }
 
-  if (auto* accelerator_controller = AcceleratorController::Get()) {
-    CHECK(!accelerator_controller_observer_.IsObserving());
-    accelerator_controller_observer_.Observe(accelerator_controller);
-  }
-
   if (Shell::HasInstance()) {
     Shell::Get()
         ->post_login_glanceables_metrics_reporter()
@@ -537,6 +540,12 @@
     return;
   }
 
+  // For forest, we will handle closing the dialog on the ash side.
+  if (auto* accelerator_controller = AcceleratorController::Get()) {
+    CHECK(!accelerator_controller_observer_.IsObserving());
+    accelerator_controller_observer_.Observe(accelerator_controller);
+  }
+
   message_center::RichNotificationData notification_data;
 
   message_center::ButtonInfo restore_button(
@@ -616,11 +625,6 @@
   }
 }
 
-bool FullRestoreService::ShouldShowNotification() const {
-  return app_launch_handler_ && app_launch_handler_->HasRestoreData() &&
-         !::first_run::IsChromeFirstRun() && !close_notification_;
-}
-
 void FullRestoreService::OnAppTerminating() {
   if (auto* arc_task_handler =
           app_restore::AppRestoreArcTaskHandler::GetForProfile(profile_)) {
@@ -658,12 +662,23 @@
                              all_session_windows, last_session_crashed));
 }
 
+void FullRestoreService::MaybeStartPineOverviewSession(
+    bool last_session_crashed) {
+  CHECK(features::IsForestFeatureEnabled());
+  delegate_->MaybeStartPineOverviewSession(CreatePineContentsData(
+      /*restore_data=*/nullptr,
+      /*all_session_windows=*/{}, last_session_crashed));
+}
+
 std::unique_ptr<PineContentsData> FullRestoreService::CreatePineContentsData(
     ::app_restore::RestoreData* restore_data,
     const std::vector<SessionWindows>& all_session_windows,
     bool last_session_crashed) {
   auto pine_contents_data = std::make_unique<PineContentsData>();
   pine_contents_data->last_session_crashed = last_session_crashed;
+  if (!restore_data) {
+    return pine_contents_data;
+  }
   pine_contents_data->restore_callback = base::BindOnce(
       &FullRestoreService::RestoreForForest, weak_ptr_factory_.GetWeakPtr());
   pine_contents_data->cancel_callback = base::BindOnce(
@@ -688,10 +703,14 @@
     for (const std::pair<const int,
                          std::unique_ptr<::app_restore::AppRestoreData>>&
              app_restore_data : launch_list) {
-      // For non browsers, the app id is sufficient for the UI we want to
-      // display.
+      const std::u16string stored_title =
+          app_restore_data.second->window_info.app_title.value_or(
+              std::u16string());
+
+      // For non browsers, the app id and title is sufficient for the UI we want
+      // to display.
       if (app_id != app_constants::kChromeAppId) {
-        pine_contents_data->apps_infos.emplace_back(app_id);
+        pine_contents_data->apps_infos.emplace_back(app_id, stored_title);
         continue;
       }
 
@@ -705,7 +724,7 @@
       // Default to using the app id if we cannot find the associated window for
       // whatever reason.
       if (!session_window) {
-        pine_contents_data->apps_infos.emplace_back(app_id);
+        pine_contents_data->apps_infos.emplace_back(app_id, stored_title);
         continue;
       }
 
@@ -720,7 +739,7 @@
         const std::string new_app_id =
             ::app_restore::GetAppIdFromAppName(app_name);
         pine_contents_data->apps_infos.emplace_back(
-            new_app_id.empty() ? app_id : new_app_id);
+            new_app_id.empty() ? app_id : new_app_id, stored_title);
         continue;
       }
 
diff --git a/chrome/browser/ash/app_restore/full_restore_service.h b/chrome/browser/ash/app_restore/full_restore_service.h
index f75b43cb..41f8585 100644
--- a/chrome/browser/ash/app_restore/full_restore_service.h
+++ b/chrome/browser/ash/app_restore/full_restore_service.h
@@ -153,10 +153,6 @@
   // Callback used when the pref |kRestoreAppsAndPagesPrefName| changes.
   void OnPreferenceChanged(const std::string& pref_name);
 
-  // Returns true if there are some restore data and this is not the first time
-  // Chrome is run. Otherwise, returns false.
-  bool ShouldShowNotification() const;
-
   void OnAppTerminating();
 
   // Callbacks for the pine dialog buttons.
@@ -174,6 +170,11 @@
   void OnGotAllSessions(bool last_session_crashed,
                         const std::vector<SessionWindows>& all_session_windows);
 
+  // Starts overview or the pine onboarding dialog when there is no restore
+  // data.
+  // TODO(sophiewen|sammiequon): Rename this and the `PineController` API.
+  void MaybeStartPineOverviewSession(bool last_session_crashed);
+
   // Constructs the object needed to show the pine dialog. It will be passed to
   // ash which will then use its contents to create and display the pine dialog.
   // `restore_data` is the data read from the full restore file.
diff --git a/chrome/browser/ash/app_restore/full_restore_service_unittest.cc b/chrome/browser/ash/app_restore/full_restore_service_unittest.cc
index e0445295a..02bcd03a 100644
--- a/chrome/browser/ash/app_restore/full_restore_service_unittest.cc
+++ b/chrome/browser/ash/app_restore/full_restore_service_unittest.cc
@@ -1072,13 +1072,17 @@
   EXPECT_TRUE(CanPerformRestore(account_id()));
 }
 
-// If the OS restore setting is 'Do not restore', after reboot, don't show the
-// pine dialog and verify the restore flag.
+// If the OS restore setting is 'Do not restore', after reboot, start a
+// zero-state pine session.
 TEST_F(ForestFullRestoreServiceTest, NotRestore) {
   SetRestoreOption(RestoreOption::kDoNotRestore);
   auto mock_delegate = std::make_unique<MockFullRestoreServiceDelegate>();
   EXPECT_CALL(*mock_delegate, MaybeStartPineOverviewSession(testing::_))
-      .Times(0);
+      .WillOnce([](std::unique_ptr<PineContentsData> data) {
+        ASSERT_TRUE(data);
+        EXPECT_TRUE(data->apps_infos.empty());
+      });
+
   CreateFullRestoreServiceForTesting(std::move(mock_delegate));
   VerifyRestoreInitSettingHistogram(RestoreOption::kDoNotRestore, 1);
   EXPECT_EQ(RestoreOption::kDoNotRestore, GetRestoreOption());
diff --git a/chrome/browser/ash/app_restore/pine_browsertest.cc b/chrome/browser/ash/app_restore/pine_browsertest.cc
index 35f62376..b254bbf 100644
--- a/chrome/browser/ash/app_restore/pine_browsertest.cc
+++ b/chrome/browser/ash/app_restore/pine_browsertest.cc
@@ -479,6 +479,58 @@
   EXPECT_FALSE(GetPineDialogRestoreButton());
 }
 
+IN_PROC_BROWSER_TEST_F(PineBrowserTest, PRE_RestoreOff) {
+  auto* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+  prefs->SetInteger(prefs::kRestoreAppsAndPagesPrefName,
+                    static_cast<int>(RestoreOption::kDoNotRestore));
+  prefs->SetBoolean(prefs::kShouldShowPineOnboarding, true);
+}
+
+// Tests that when Restore is off, we show the onboarding dialog.
+IN_PROC_BROWSER_TEST_F(PineBrowserTest, RestoreOff) {
+  // The first time after rebooting, we show the onboarding dialog.
+  auto* onboarding_dialog = PineTestApi().GetOnboardingDialog();
+  ASSERT_TRUE(onboarding_dialog);
+
+  // Press the accept button.
+  test::Click(onboarding_dialog->GetAcceptButtonForTesting(), /*flag=*/0);
+  views::test::WidgetDestroyedWaiter(onboarding_dialog->GetWidget()).Wait();
+  EXPECT_FALSE(PineTestApi().GetOnboardingDialog());
+
+  // Verify we have entered overview with no pine contents.
+  WaitForOverviewEnterAnimation();
+  EXPECT_FALSE(GetPineContentsView());
+
+  // Verify the restore pref is updated.
+  EXPECT_EQ(static_cast<int>(RestoreOption::kAskEveryTime),
+            ProfileManager::GetActiveUserProfile()->GetPrefs()->GetInteger(
+                prefs::kRestoreAppsAndPagesPrefName));
+}
+
+IN_PROC_BROWSER_TEST_F(PineBrowserTest, PRE_NoRestoreData) {
+  auto* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+  EXPECT_EQ(static_cast<int>(RestoreOption::kAskEveryTime),
+            prefs->GetInteger(prefs::kRestoreAppsAndPagesPrefName));
+  prefs->SetBoolean(prefs::kShouldShowPineOnboarding, true);
+}
+
+// Tests that when Restore is 'Ask every time' and there is no restore data, we
+// show the onboarding dialog.
+IN_PROC_BROWSER_TEST_F(PineBrowserTest, NoRestoreData) {
+  // The first time after rebooting, we show the onboarding dialog.
+  auto* onboarding_dialog = PineTestApi().GetOnboardingDialog();
+  ASSERT_TRUE(onboarding_dialog);
+
+  // Press the accept button.
+  test::Click(onboarding_dialog->GetAcceptButtonForTesting(), /*flag=*/0);
+  views::test::WidgetDestroyedWaiter(onboarding_dialog->GetWidget()).Wait();
+  EXPECT_FALSE(PineTestApi().GetOnboardingDialog());
+
+  // Verify we have entered overview with no pine contents.
+  WaitForOverviewEnterAnimation();
+  EXPECT_FALSE(GetPineContentsView());
+}
+
 IN_PROC_BROWSER_TEST_F(PineBrowserTest, PRE_Onboarding) {
   // The restore pref setting is 'Ask every time' by default.
   auto* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
@@ -499,12 +551,11 @@
 IN_PROC_BROWSER_TEST_F(PineBrowserTest, Onboarding) {
   // The first time after rebooting, we show the onboarding dialog.
   auto* onboarding_dialog = PineTestApi().GetOnboardingDialog();
-  EXPECT_TRUE(onboarding_dialog);
+  ASSERT_TRUE(onboarding_dialog);
 
   // Press the accept button.
   test::Click(onboarding_dialog->GetAcceptButtonForTesting(), /*flag=*/0);
   views::test::WidgetDestroyedWaiter(onboarding_dialog->GetWidget()).Wait();
-  base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(PineTestApi().GetOnboardingDialog());
 
   // Verify we have entered overview. The restore button will be null if
diff --git a/chrome/browser/ash/arc/input_overlay/ui/message_view.cc b/chrome/browser/ash/arc/input_overlay/ui/message_view.cc
index a590300..950f41d 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/message_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/message_view.cc
@@ -10,7 +10,9 @@
 #include "chrome/browser/ash/arc/input_overlay/ui/action_view.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/compositor/layer.h"
 #include "ui/gfx/color_utils.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/background.h"
@@ -111,6 +113,7 @@
 void MessageView::AddShadow() {
   view_shadow_ = std::make_unique<ash::ViewShadow>(this, kShadowElevation);
   view_shadow_->SetRoundedCornerRadius(kCornerRadius);
+  layer()->SetRoundedCornerRadius(gfx::RoundedCornersF(kCornerRadius));
 }
 
 BEGIN_METADATA(MessageView)
diff --git a/chrome/browser/ash/crosapi/crosapi_util.cc b/chrome/browser/ash/crosapi/crosapi_util.cc
index fb8943b..beeaf0d 100644
--- a/chrome/browser/ash/crosapi/crosapi_util.cc
+++ b/chrome/browser/ash/crosapi/crosapi_util.cc
@@ -52,6 +52,7 @@
 #include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/components/cdm_factory_daemon/mojom/browser_cdm_factory.mojom.h"
 #include "chromeos/components/in_session_auth/mojom/in_session_auth.mojom.h"
+#include "chromeos/components/mgs/managed_guest_session_utils.h"
 #include "chromeos/components/payments/mojom/payment_app.mojom.h"
 #include "chromeos/components/remote_apps/mojom/remote_apps.mojom.h"
 #include "chromeos/components/sensors/mojom/cros_sensor_service.mojom.h"
@@ -608,6 +609,28 @@
   }
 }
 
+mojom::SessionType GetSessionType() {
+  const user_manager::User* const user =
+      user_manager::UserManager::Get()->GetPrimaryUser();
+  switch (user->GetType()) {
+    case user_manager::UserType::kRegular:
+      return mojom::SessionType::kRegularSession;
+    case user_manager::UserType::kChild:
+      return mojom::SessionType::kChildSession;
+    case user_manager::UserType::kGuest:
+      return mojom::SessionType::kGuestSession;
+    case user_manager::UserType::kPublicAccount:
+      return mojom::SessionType::kPublicSession;
+    case user_manager::UserType::kKioskApp:
+      return mojom::SessionType::kAppKioskSession;
+    case user_manager::UserType::kArcKioskApp:
+      LOG(WARNING) << "Starting as ARC Kiosk App session.";
+      return mojom::SessionType::kRegularSession;
+    case user_manager::UserType::kWebKioskApp:
+      return mojom::SessionType::kWebKioskSession;
+  }
+}
+
 mojom::DeviceMode GetDeviceMode() {
   policy::DeviceMode mode = ash::InstallAttributes::Get()->GetMode();
   switch (mode) {
@@ -901,6 +924,9 @@
   params->is_orca_enabled = chromeos::features::IsOrcaEnabled();
 
   params->is_cros_mall_enabled = chromeos::features::IsCrosMallEnabled();
+
+  params->is_mahi_enabled = chromeos::features::IsMahiEnabled() &&
+                            ash::switches::IsMahiSecretKeyMatched();
 }
 
 template <typename BrowserParams>
@@ -909,7 +935,7 @@
   static_assert(std::is_same<mojom::BrowserInitParams, BrowserParams>() ||
                 std::is_same<mojom::BrowserPostLoginParams, BrowserParams>());
 
-  params->session_type = EnvironmentProvider::Get()->GetSessionType();
+  params->session_type = GetSessionType();
   params->default_paths = EnvironmentProvider::Get()->GetDefaultPaths();
 
   const std::optional<account_manager::Account> maybe_device_account =
diff --git a/chrome/browser/ash/crosapi/environment_provider.cc b/chrome/browser/ash/crosapi/environment_provider.cc
index 558c76b..1169daa 100644
--- a/chrome/browser/ash/crosapi/environment_provider.cc
+++ b/chrome/browser/ash/crosapi/environment_provider.cc
@@ -14,12 +14,10 @@
 #include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
 #include "chrome/browser/web_applications/preinstalled_web_app_config_utils.h"
 #include "chromeos/ash/components/dbus/cros_disks/cros_disks_client.h"
-#include "chromeos/components/mgs/managed_guest_session_utils.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
 #include "chromeos/crosapi/mojom/policy_namespace.mojom.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
-#include "components/user_manager/user_type.h"
 #include "crypto/nss_util_internal.h"
 
 namespace crosapi {
@@ -33,29 +31,6 @@
 EnvironmentProvider::EnvironmentProvider() = default;
 EnvironmentProvider::~EnvironmentProvider() = default;
 
-mojom::SessionType EnvironmentProvider::GetSessionType() {
-  const user_manager::User* const user =
-      user_manager::UserManager::Get()->GetPrimaryUser();
-  const Profile* const profile =
-      ash::ProfileHelper::Get()->GetProfileByUser(user);
-  if (profile->IsGuestSession()) {
-    return mojom::SessionType::kGuestSession;
-  }
-  if (chromeos::IsManagedGuestSession()) {
-    return mojom::SessionType::kPublicSession;
-  }
-  if (user->GetType() == user_manager::UserType::kWebKioskApp) {
-    return mojom::SessionType::kWebKioskSession;
-  }
-  if (user->GetType() == user_manager::UserType::kKioskApp) {
-    return mojom::SessionType::kAppKioskSession;
-  }
-  if (user->GetType() == user_manager::UserType::kChild) {
-    return mojom::SessionType::kChildSession;
-  }
-  return mojom::SessionType::kRegularSession;
-}
-
 mojom::DefaultPathsPtr EnvironmentProvider::GetDefaultPaths() {
   mojom::DefaultPathsPtr default_paths = mojom::DefaultPaths::New();
   // The default paths belong to ash's primary user profile. Lacros does not
diff --git a/chrome/browser/ash/crosapi/environment_provider.h b/chrome/browser/ash/crosapi/environment_provider.h
index ab5cf04c..5781530f 100644
--- a/chrome/browser/ash/crosapi/environment_provider.h
+++ b/chrome/browser/ash/crosapi/environment_provider.h
@@ -18,8 +18,6 @@
   EnvironmentProvider(const EnvironmentProvider&) = delete;
   EnvironmentProvider& operator=(const EnvironmentProvider&) = delete;
 
-  crosapi::mojom::SessionType GetSessionType();
-
   // Returns the default paths, such as Downloads, Documents (MyFiles) and the
   // mount point for Drive. These are provided by ash because they are part of
   // the device account, not the Lacros profile.
diff --git a/chrome/browser/ash/crosapi/native_theme_service_ash.cc b/chrome/browser/ash/crosapi/native_theme_service_ash.cc
index 5804812..fa071ff 100644
--- a/chrome/browser/ash/crosapi/native_theme_service_ash.cc
+++ b/chrome/browser/ash/crosapi/native_theme_service_ash.cc
@@ -58,6 +58,7 @@
   auto info = mojom::NativeThemeInfo::New();
   const ui::NativeTheme* theme = ui::NativeTheme::GetInstanceForNativeUi();
   info->dark_mode = theme->ShouldUseDarkColors();
+  info->caret_blink_interval = theme->GetCaretBlinkInterval();
 
   if (!chromeos::features::IsJellyEnabled()) {
     return info;
diff --git a/chrome/browser/ash/extended_updates/extended_updates_controller.cc b/chrome/browser/ash/extended_updates/extended_updates_controller.cc
index e4b9ab76..52de6dd8 100644
--- a/chrome/browser/ash/extended_updates/extended_updates_controller.cc
+++ b/chrome/browser/ash/extended_updates/extended_updates_controller.cc
@@ -4,10 +4,74 @@
 
 #include "chrome/browser/ash/extended_updates/extended_updates_controller.h"
 
+#include "ash/constants/ash_features.h"
+#include "chrome/browser/ash/ownership/owner_settings_service_ash.h"
+#include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
+#include "chrome/browser/ash/settings/cros_settings.h"
+#include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "components/ownership/owner_settings_service.h"
+
 namespace ash {
 
-bool ShouldRequestExtendedUpdatesOptIn() {
+namespace {
+
+// Returns true if the user has the ability to opt in the device.
+bool HasOptInAbility(ownership::OwnerSettingsService* owner_settings) {
+  // Only owner user can opt in.
+  // By extension, only unmanaged devices can opt in.
+  if (!owner_settings || !owner_settings->IsOwner()) {
+    return false;
+  }
+
+  // Check feature enablement after other checks to reduce noise due to how
+  // finch experiment is recorded.
+  if (!ash::features::IsExtendedUpdatesOptInFeatureEnabled()) {
+    return false;
+  }
+
+  // Only eligible if not already opted in.
+  return !IsExtendedUpdatesOptedIn();
+}
+
+}  // namespace
+
+bool IsExtendedUpdatesOptInEligible(content::BrowserContext* context,
+                                    const ExtendedUpdatesParams& params) {
+  // Valid date range is between extended date and eol date.
+  // Extended date is expected to be before eol date.
+  // Also, not eligible if opt-in is not required.
+  if (params.eol_passed || !params.extended_date_passed ||
+      !params.opt_in_required) {
+    return false;
+  }
+
+  return IsExtendedUpdatesOptInEligible(context);
+}
+
+bool IsExtendedUpdatesOptInEligible(content::BrowserContext* context) {
+  auto* owner_settings =
+      OwnerSettingsServiceAshFactory::GetForBrowserContext(context);
+  return HasOptInAbility(owner_settings);
+}
+
+bool IsExtendedUpdatesOptedIn() {
+  bool value;
+  if (CrosSettings::Get()->GetBoolean(kDeviceExtendedAutoUpdateEnabled,
+                                      &value)) {
+    return value;
+  }
   return false;
 }
 
+bool OptInExtendedUpdates(content::BrowserContext* context) {
+  auto* owner_settings =
+      OwnerSettingsServiceAshFactory::GetForBrowserContext(context);
+  if (!HasOptInAbility(owner_settings)) {
+    return false;
+  }
+
+  // TODO(b/329513970): Add metrics.
+  return owner_settings->SetBoolean(kDeviceExtendedAutoUpdateEnabled, true);
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/extended_updates/extended_updates_controller.h b/chrome/browser/ash/extended_updates/extended_updates_controller.h
index 5185e729..4c82663 100644
--- a/chrome/browser/ash/extended_updates/extended_updates_controller.h
+++ b/chrome/browser/ash/extended_updates/extended_updates_controller.h
@@ -5,9 +5,43 @@
 #ifndef CHROME_BROWSER_ASH_EXTENDED_UPDATES_EXTENDED_UPDATES_CONTROLLER_H_
 #define CHROME_BROWSER_ASH_EXTENDED_UPDATES_EXTENDED_UPDATES_CONTROLLER_H_
 
+namespace content {
+class BrowserContext;
+}  // namespace content
+
 namespace ash {
 
-bool ShouldRequestExtendedUpdatesOptIn();
+// Params struct used as input to extended updates eligibility check function.
+// |eol_passed| is true if the device passed its auto update expiration date.
+// |extended_date_passed| is true if the device passed its extended update date.
+// |opt_in_required| is true if the device requires user opt-in to receive
+// extended updates.
+struct ExtendedUpdatesParams {
+  bool eol_passed = false;
+  bool extended_date_passed = false;
+  bool opt_in_required = false;
+};
+
+// Whether the device is eligible to opt-in for extended updates.
+// This depends on multiple criteria, e.g. whether opt-in is required,
+// being within the allowed time window, the user type, whether the device
+// is already opted in.
+// |context| is the Profile of the current user.
+bool IsExtendedUpdatesOptInEligible(content::BrowserContext* context,
+                                    const ExtendedUpdatesParams& params);
+
+// Whether the device is eligible to opt-in for extended updates.
+// This version assumes the values in ExtendedUpdatesParams are eligible.
+// TODO(b/330230644): Consolidate with above function.
+bool IsExtendedUpdatesOptInEligible(content::BrowserContext* context);
+
+// Whether the device is opted in for receiving extended updates.
+bool IsExtendedUpdatesOptedIn();
+
+// Opts the device into receiving extended updates.
+// Returns true if the operation succeeded.
+// The caller should check for eligibility before calling this.
+bool OptInExtendedUpdates(content::BrowserContext* context);
 
 }  // namespace ash
 
diff --git a/chrome/browser/ash/extended_updates/extended_updates_controller_unittest.cc b/chrome/browser/ash/extended_updates/extended_updates_controller_unittest.cc
index c9dc5b50..49e11adc 100644
--- a/chrome/browser/ash/extended_updates/extended_updates_controller_unittest.cc
+++ b/chrome/browser/ash/extended_updates/extended_updates_controller_unittest.cc
@@ -4,12 +4,147 @@
 
 #include "chrome/browser/ash/extended_updates/extended_updates_controller.h"
 
+#include "ash/constants/ash_features.h"
+#include "chrome/browser/ash/ownership/owner_settings_service_ash.h"
+#include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
+#include "chrome/browser/ash/settings/device_settings_test_helper.h"
+#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
+#include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
+#include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "components/account_id/account_id.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
 
-TEST(ExtendedUpdatesControllerTest, ShouldRequestExtendedUpdatesOptIn) {
-  EXPECT_FALSE(ShouldRequestExtendedUpdatesOptIn());
+class ExtendedUpdatesControllerTest : public DeviceSettingsTestBase {
+ public:
+  ExtendedUpdatesControllerTest() = default;
+  ExtendedUpdatesControllerTest(const ExtendedUpdatesControllerTest&) = delete;
+  ExtendedUpdatesControllerTest& operator=(
+      const ExtendedUpdatesControllerTest&) = delete;
+  ~ExtendedUpdatesControllerTest() override = default;
+
+  void SetUp() override {
+    DeviceSettingsTestBase::SetUp();
+
+    feature_list_.InitAndEnableFeature(features::kExtendedUpdatesOptInFeature);
+
+    owner_key_util_->ImportPrivateKeyAndSetPublicKey(
+        device_policy_->GetSigningKey());
+    InitOwner(
+        AccountId::FromUserEmail(device_policy_->policy_data().username()),
+        true);
+    FlushDeviceSettings();
+  }
+
+ protected:
+  ExtendedUpdatesParams MakeEligibleParams() const {
+    return ExtendedUpdatesParams{
+        .eol_passed = false,
+        .extended_date_passed = true,
+        .opt_in_required = true,
+    };
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+  ScopedTestingCrosSettings cros_settings_;
+  ash::ScopedStubInstallAttributes test_install_attributes_;
+};
+
+TEST_F(ExtendedUpdatesControllerTest, IsExtendedUpdatesOptInEligible_Eligible) {
+  EXPECT_TRUE(
+      IsExtendedUpdatesOptInEligible(profile_.get(), MakeEligibleParams()));
+}
+
+TEST_F(ExtendedUpdatesControllerTest,
+       IsExtendedUpdatesOptInEligible_FeatureDisabled) {
+  feature_list_.Reset();
+  feature_list_.InitAndDisableFeature(features::kExtendedUpdatesOptInFeature);
+  EXPECT_FALSE(
+      IsExtendedUpdatesOptInEligible(profile_.get(), MakeEligibleParams()));
+}
+
+TEST_F(ExtendedUpdatesControllerTest, IsExtendedUpdatesOptInEligible_PastEol) {
+  auto params = MakeEligibleParams();
+  params.eol_passed = true;
+  EXPECT_FALSE(IsExtendedUpdatesOptInEligible(profile_.get(), params));
+}
+
+TEST_F(ExtendedUpdatesControllerTest,
+       IsExtendedUpdatesOptInEligible_BeforeExtendedDate) {
+  auto params = MakeEligibleParams();
+  params.extended_date_passed = false;
+  EXPECT_FALSE(IsExtendedUpdatesOptInEligible(profile_.get(), params));
+}
+
+TEST_F(ExtendedUpdatesControllerTest,
+       IsExtendedUpdatesOptInEligible_OptInNotRequired) {
+  auto params = MakeEligibleParams();
+  params.opt_in_required = false;
+  EXPECT_FALSE(IsExtendedUpdatesOptInEligible(profile_.get(), params));
+}
+
+TEST_F(ExtendedUpdatesControllerTest,
+       IsExtendedUpdatesOptInEligible_AlreadyOptedIn) {
+  OwnerSettingsServiceAshFactory::GetForBrowserContext(profile_.get())
+      ->SetBoolean(kDeviceExtendedAutoUpdateEnabled, true);
+  EXPECT_FALSE(
+      IsExtendedUpdatesOptInEligible(profile_.get(), MakeEligibleParams()));
+}
+
+TEST_F(ExtendedUpdatesControllerTest, IsExtendedUpdatesOptInEligible_NotOwner) {
+  cros_settings_.device_settings()->SetCurrentUserIsOwner(false);
+  EXPECT_FALSE(
+      IsExtendedUpdatesOptInEligible(profile_.get(), MakeEligibleParams()));
+}
+
+TEST_F(ExtendedUpdatesControllerTest,
+       IsExtendedUpdatesOptInEligible_IsManaged) {
+  test_install_attributes_.Get()->SetCloudManaged("fake_domain", "fake_id");
+  EXPECT_FALSE(
+      IsExtendedUpdatesOptInEligible(profile_.get(), MakeEligibleParams()));
+}
+
+TEST_F(ExtendedUpdatesControllerTest,
+       IsExtendedUpdatesOptedIn_NotOptedInByDefault) {
+  EXPECT_FALSE(IsExtendedUpdatesOptedIn());
+}
+
+TEST_F(ExtendedUpdatesControllerTest, IsExtendedUpdatesOptedIn_OptedIn) {
+  OwnerSettingsServiceAshFactory::GetForBrowserContext(profile_.get())
+      ->SetBoolean(kDeviceExtendedAutoUpdateEnabled, true);
+  EXPECT_TRUE(IsExtendedUpdatesOptedIn());
+}
+
+TEST_F(ExtendedUpdatesControllerTest, OptInExtendedUpdates_Success) {
+  EXPECT_FALSE(IsExtendedUpdatesOptedIn());
+  EXPECT_TRUE(OptInExtendedUpdates(profile_.get()));
+  EXPECT_TRUE(IsExtendedUpdatesOptedIn());
+}
+
+TEST_F(ExtendedUpdatesControllerTest, OptInExtendedUpdates_FeatureDisabled) {
+  feature_list_.Reset();
+  feature_list_.InitAndDisableFeature(features::kExtendedUpdatesOptInFeature);
+  EXPECT_FALSE(OptInExtendedUpdates(profile_.get()));
+  EXPECT_FALSE(IsExtendedUpdatesOptedIn());
+}
+
+TEST_F(ExtendedUpdatesControllerTest, OptInExtendedUpdates_AlreadyOptedIn) {
+  EXPECT_TRUE(OptInExtendedUpdates(profile_.get()));
+  EXPECT_FALSE(OptInExtendedUpdates(profile_.get()));
+  EXPECT_TRUE(IsExtendedUpdatesOptedIn());
+}
+
+TEST_F(ExtendedUpdatesControllerTest, OptInExtendedUpdates_NotOwner) {
+  cros_settings_.device_settings()->SetCurrentUserIsOwner(false);
+  EXPECT_FALSE(OptInExtendedUpdates(profile_.get()));
+  EXPECT_FALSE(IsExtendedUpdatesOptedIn());
+}
+
+TEST_F(ExtendedUpdatesControllerTest, OptInExtendedUpdates_IsManaged) {
+  test_install_attributes_.Get()->SetCloudManaged("fake_domain", "fake_id");
+  EXPECT_FALSE(OptInExtendedUpdates(profile_.get()));
+  EXPECT_FALSE(IsExtendedUpdatesOptedIn());
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index 6df5a7a..e489c1e 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/ash/file_manager/copy_or_move_io_task_policy_impl.h"
 #include "chrome/browser/ash/file_manager/file_manager_browsertest_base.h"
 #include "chrome/browser/ash/file_manager/file_manager_browsertest_utils.h"
+#include "chrome/browser/ash/file_manager/file_manager_test_util.h"
 #include "chrome/browser/ash/file_manager/io_task.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
@@ -211,11 +212,8 @@
  protected:
   // extensions::ExtensionApiTest:
   void SetUpOnMainThread() override {
-    extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
-    extensions::ExtensionService* service =
-        extensions::ExtensionSystem::Get(browser()->profile())
-            ->extension_service();
-    service->component_loader()->AddDefaultComponentExtensions(false);
+    file_manager::test::AddDefaultComponentExtensionsOnMainThread(
+        browser()->profile());
 
     embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory());
     ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/ash/file_manager/file_manager_test_util.cc b/chrome/browser/ash/file_manager/file_manager_test_util.cc
index 59132ec0..0c0f1988 100644
--- a/chrome/browser/ash/file_manager/file_manager_test_util.cc
+++ b/chrome/browser/ash/file_manager/file_manager_test_util.cc
@@ -138,6 +138,19 @@
   extensions::ExtensionService* service =
       extensions::ExtensionSystem::Get(profile)->extension_service();
   service->component_loader()->AddDefaultComponentExtensions(false);
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  // QuickOffice loads from rootfs at /usr/share/chromeos-assets/quickoffce
+  // which does not exist on bots for tests, so load test version.
+  base::FilePath data_dir;
+  CHECK(base::PathService::Get(chrome::DIR_TEST_DATA, &data_dir));
+  base::RunLoop run_loop;
+  service->component_loader()->AddComponentFromDirWithManifestFilename(
+      data_dir.Append("chromeos/file_manager/quickoffice"),
+      extension_misc::kQuickOfficeComponentExtensionId,
+      extensions::kManifestFilename, extensions::kManifestFilename,
+      run_loop.QuitClosure());
+  run_loop.Run();
+#endif
   // AddDefaultComponentExtensions() is normally invoked during
   // ExtensionService::Init() which also invokes UninstallMigratedExtensions().
   // Invoke it here as well, otherwise migrated extensions will remain installed
diff --git a/chrome/browser/ash/file_manager/file_tasks_browsertest.cc b/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
index d4e3d18..c211885 100644
--- a/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
@@ -287,6 +287,16 @@
 
     lacros_waiter_.emplace(crosapi::TestControllerAsh::Get()
                                ->GetStandaloneBrowserTestController());
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+    // QuickOffice loads from rootfs at /usr/share/chromeos-assets/quickoffce
+    // which does not exist on bots for tests, so load test version.
+    base::FilePath data_dir;
+    CHECK(base::PathService::Get(chrome::DIR_TEST_DATA, &data_dir));
+    lacros_waiter_->InstallComponentExtension(
+        data_dir.Append("chromeos/file_manager/quickoffice").value(),
+        extension_misc::kQuickOfficeComponentExtensionId);
+#endif
   }
 
   std::string InstallExtension(const char* path) override {
diff --git a/chrome/browser/ash/floating_workspace/floating_workspace_service.cc b/chrome/browser/ash/floating_workspace/floating_workspace_service.cc
index 3277462..ea369da 100644
--- a/chrome/browser/ash/floating_workspace/floating_workspace_service.cc
+++ b/chrome/browser/ash/floating_workspace/floating_workspace_service.cc
@@ -692,22 +692,6 @@
   }
 }
 
-bool FloatingWorkspaceService::ShouldUploadFloatingWorkspaceTemplate() {
-  bool initial_capture = !previously_captured_desk_template_;
-  bool first_download_is_completed =
-      first_uptodate_download_timeticks_.has_value();
-  // Initialize this to false. This should only be switched if the download is
-  // completed and there has been user activity since then.
-  bool has_user_activity_since_last_download = false;
-  if (first_download_is_completed) {
-    has_user_activity_since_last_download =
-        first_uptodate_download_timeticks_.value() <=
-        ui::UserActivityDetector::Get()->last_activity_time();
-  }
-  return (first_download_is_completed &&
-          (initial_capture || has_user_activity_since_last_download));
-}
-
 void FloatingWorkspaceService::OnTemplateCaptured(
     std::optional<DesksClient::DeskActionError> error,
     std::unique_ptr<DeskTemplate> desk_template) {
@@ -741,12 +725,10 @@
   }
   // If it successfully captured desk, remove old entry and record new uuid only
   // if the user was active from when the sync cycle is finished to now.
-  // Upload the captured floating workspace template on two conditions:
-  // 1) If this is the first capture after a template launch or
-  // 2) If the current desk is different and the user has been active since last
-  // capture.
   if (!IsCurrentDeskSameAsPrevious(desk_template.get()) &&
-      ShouldUploadFloatingWorkspaceTemplate()) {
+      (first_uptodate_download_timeticks_.has_value() &&
+       first_uptodate_download_timeticks_.value() <=
+           ui::UserActivityDetector::Get()->last_activity_time())) {
     UploadFloatingWorkspaceTemplateToDeskModel(std::move(desk_template));
   }
 }
diff --git a/chrome/browser/ash/floating_workspace/floating_workspace_service.h b/chrome/browser/ash/floating_workspace/floating_workspace_service.h
index 7d7a688..928649a 100644
--- a/chrome/browser/ash/floating_workspace/floating_workspace_service.h
+++ b/chrome/browser/ash/floating_workspace/floating_workspace_service.h
@@ -261,13 +261,6 @@
   // connection.
   void OnNetworkStateOrSyncServiceStateChanged();
 
-  // Checks to make sure we should upload the captured floating workspace
-  // template. Upload the captured floating workspace template on two
-  // conditions: 1) If this is the first capture after a template launch or 2)
-  // If the current desk is different and the user has been active since last
-  // capture.
-  bool ShouldUploadFloatingWorkspaceTemplate();
-
   const raw_ptr<Profile> profile_;
 
   const floating_workspace_util::FloatingWorkspaceVersion version_;
diff --git a/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc b/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
index 6f7fd64e..48c4995 100644
--- a/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
+++ b/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
@@ -1492,31 +1492,9 @@
   task_environment().FastForwardBy(
       ash::features::kFloatingWorkspaceV2PeriodicJobIntervalInSeconds.Get() +
       base::Seconds(1));
-  // Initial capture should be uploaded.
-  EXPECT_TRUE(floating_workspace_service->GetLatestFloatingWorkspaceTemplate());
-  // Make second workspace desk.
-  const base::Time second_captured_template_creation_time = base::Time::Now();
-  std::unique_ptr<DeskTemplate> second_captured_floating_workspace_template =
-      MakeTestFloatingWorkspaceDeskTemplate(
-          template_name, second_captured_template_creation_time);
 
-  // Create new restore data different than 1st captured one.
-  std::unique_ptr<app_restore::RestoreData> restore_data =
-      CreateRestoreData(std::vector<int>(11, 1));
-  second_captured_floating_workspace_template->set_desk_restore_data(
-      std::move(restore_data));
-  // Set the 2nd template to be captured.
-  mock_desks_client()->SetCapturedDeskTemplate(
-      std::move(second_captured_floating_workspace_template));
-  // Fast forward by capture interval capture a second time.
-  task_environment().FastForwardBy(
-      ash::features::kFloatingWorkspaceV2PeriodicJobIntervalInSeconds.Get() +
-      base::Seconds(1));
-  // Second capture should not be uploaded if there were no user activity since
-  // last capture.
-  ASSERT_TRUE(floating_workspace_service->GetLatestFloatingWorkspaceTemplate());
-  EXPECT_TRUE(floating_workspace_service->GetLatestFloatingWorkspaceTemplate()
-                  ->created_time() != second_captured_template_creation_time);
+  EXPECT_FALSE(
+      floating_workspace_service->GetLatestFloatingWorkspaceTemplate());
 }
 
 TEST_F(FloatingWorkspaceServiceV2Test,
@@ -1642,48 +1620,6 @@
             creation_time);
 }
 
-TEST_F(FloatingWorkspaceServiceV2Test, NoCaptureIfDownloadIsNotDone) {
-  PopulateAppsCache();
-  const std::string template_name = "floating_workspace_template";
-  base::RunLoop loop;
-  fake_desk_sync_service()->GetDeskModel()->AddOrUpdateEntry(
-      MakeTestFloatingWorkspaceDeskTemplate(template_name, base::Time::Now()),
-      base::BindLambdaForTesting(
-          [&](desks_storage::DeskModel::AddOrUpdateEntryStatus status,
-              std::unique_ptr<ash::DeskTemplate> new_entry) {
-            EXPECT_EQ(desks_storage::DeskModel::AddOrUpdateEntryStatus::kOk,
-                      status);
-            loop.Quit();
-          }));
-  loop.Run();
-
-  CreateFloatingWorkspaceServiceForTesting(profile());
-  auto* floating_workspace_service =
-      FloatingWorkspaceService::GetForProfile(profile());
-  floating_workspace_service->Init(test_sync_service(),
-                                   fake_desk_sync_service());
-  const std::string captured_template_name =
-      "captured_floating_workspace_template";
-
-  std::unique_ptr<DeskTemplate> floating_workspace_template =
-      MakeTestFloatingWorkspaceDeskTemplate(captured_template_name,
-                                            base::Time::Now());
-  mock_desks_client()->SetCapturedDeskTemplate(
-      std::move(floating_workspace_template));
-  test_sync_service()->SetDownloadStatusFor(
-      {syncer::ModelType::WORKSPACE_DESK},
-      syncer::SyncService::ModelTypeDownloadStatus::kWaitingForUpdates);
-  test_sync_service()->FireStateChanged();
-  user_activity_detector()->set_last_activity_time_for_test(
-      base::TimeTicks::Now() + base::Milliseconds(1));
-  EXPECT_FALSE(mock_desks_client()->restored_desk_template());
-
-  ASSERT_TRUE(floating_workspace_service->GetLatestFloatingWorkspaceTemplate());
-  EXPECT_TRUE(floating_workspace_service->GetLatestFloatingWorkspaceTemplate()
-                  ->template_name() !=
-              base::UTF8ToUTF16(captured_template_name));
-}
-
 TEST_F(FloatingWorkspaceServiceV2Test,
        CaptureFloatingWorkspaceTemplateOnSystemTrayVisible) {
   PopulateAppsCache();
diff --git a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
index ef33478..f3944dc6 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
@@ -378,15 +378,18 @@
 };
 
 // Requesting state keys hangs forever, but that should not matter because we're
-// running on a "no chrome" device.
-class EnrollmentOnNoChrome : public EnrollmentEmbeddedPolicyServerBase {
+// running on reven.
+class EnrollmentOnRevenWithNoStateKeysResponse
+    : public EnrollmentEmbeddedPolicyServerBase {
  public:
-  EnrollmentOnNoChrome() = default;
+  EnrollmentOnRevenWithNoStateKeysResponse() = default;
 
-  EnrollmentOnNoChrome(const EnrollmentOnNoChrome&) = delete;
-  EnrollmentOnNoChrome& operator=(const EnrollmentOnNoChrome&) = delete;
+  EnrollmentOnRevenWithNoStateKeysResponse(
+      const EnrollmentOnRevenWithNoStateKeysResponse&) = delete;
+  EnrollmentOnRevenWithNoStateKeysResponse& operator=(
+      const EnrollmentOnRevenWithNoStateKeysResponse&) = delete;
 
-  ~EnrollmentOnNoChrome() override = default;
+  ~EnrollmentOnRevenWithNoStateKeysResponse() override = default;
 
   // EnrollmentEmbeddedPolicyServerBase:
   void SetUpInProcessBrowserTestFixture() override {
@@ -394,18 +397,24 @@
     // Session manager client is initialized by DeviceStateMixin.
     FakeSessionManagerClient::Get()->set_state_keys_handling(
         FakeSessionManagerClient::ServerBackedStateKeysHandling::kNoResponse);
-    // Devices that are marked 'nochrome' do not have Forced Re-Enrollment (FRE)
-    // enabled. If FRE is not enabled, then we don't request and obtain state
-    // keys.
+    // reven devices are also marked 'nochrome' which is important because it
+    // disables Forced Re-Enrollment (FRE). If FRE is enabled, state keys are
+    // required.
     fake_statistics_provider_.SetMachineStatistic(
         system::kFirmwareTypeKey, system::kFirmwareTypeValueNonchrome);
     // When using a fresh ScopedFakeStatisticsProvider we also need to configure
-    // a few entries (serial number, machine model). This can be done for us by
-    // ConfigureFakeStatisticsForZeroTouch().
+    // a few entries (serial number, machine model).
+    // ConfigureFakeStatisticsForZeroTouch does that for us.
     policy_server_.ConfigureFakeStatisticsForZeroTouch(
         &fake_statistics_provider_);
   }
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    EnrollmentEmbeddedPolicyServerBase::SetUpCommandLine(command_line);
+
+    command_line->AppendSwitch(switches::kRevenBranding);
+  }
+
  private:
   system::ScopedFakeStatisticsProvider fake_statistics_provider_;
 };
@@ -434,8 +443,9 @@
 
 // The test case is the same as
 // EnrollmentEmbeddedPolicyServerBase.ManualEnrollment but the environment is
-// different (simulates state keys not being available).
-IN_PROC_BROWSER_TEST_F(EnrollmentOnNoChrome, ManualEnrollment) {
+// different (simulate reven board, simulate state keys not being available).
+IN_PROC_BROWSER_TEST_F(EnrollmentOnRevenWithNoStateKeysResponse,
+                       ManualEnrollment) {
   TriggerEnrollmentAndSignInSuccessfully();
 
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
@@ -444,45 +454,6 @@
   EXPECT_TRUE(InstallAttributes::Get()->IsCloudManaged());
 }
 
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-// Only run Flex tests on Google branded browsers, since FRE is not enabled
-// otherwise, and state keys are not requested unless FRE is enabled.
-// We prefer this to forcing FRE to always be on, because that code path
-// short circuits the code that will actually run on devices.
-
-// Enrollment on Flex. Flex devices are "no chrome" but support some features
-// like FRE that other "no chrome" devices don't.
-class EnrollmentOnFlex : public EnrollmentOnNoChrome {
- public:
-  EnrollmentOnFlex() = default;
-
-  EnrollmentOnFlex(const EnrollmentOnFlex&) = delete;
-  EnrollmentOnFlex& operator=(const EnrollmentOnFlex&) = delete;
-
-  ~EnrollmentOnFlex() override = default;
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    EnrollmentEmbeddedPolicyServerBase::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kRevenBranding);
-    command_line->AppendSwitchASCII(
-        switches::kEnterpriseEnableForcedReEnrollmentOnFlex,
-        policy::AutoEnrollmentTypeChecker::kForcedReEnrollmentAlways);
-  }
-};
-
-// Simple manual enrollment on Flex.
-IN_PROC_BROWSER_TEST_F(EnrollmentOnFlex, ManualEnrollment) {
-  TriggerEnrollmentAndSignInSuccessfully();
-
-  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
-  test::OobeJS().ExpectTrue("Oobe.isEnrollmentSuccessfulForTest()");
-  EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
-  EXPECT_TRUE(InstallAttributes::Get()->IsCloudManaged());
-}
-
-#endif  // GOOGLE_CHROME_BRANDING
-
-// The test case is the same as
 // Device policy blocks dev mode and this is not prohibited by a command-line
 // flag.
 IN_PROC_BROWSER_TEST_F(EnrollmentEmbeddedPolicyServerBase,
diff --git a/chrome/browser/ash/login/lock/screen_locker.cc b/chrome/browser/ash/login/lock/screen_locker.cc
index 2e7ec4e0..02bf7c1 100644
--- a/chrome/browser/ash/login/lock/screen_locker.cc
+++ b/chrome/browser/ash/login/lock/screen_locker.cc
@@ -23,7 +23,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
-#include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/login/helper.h"
 #include "chrome/browser/ash/login/lock/views_screen_locker.h"
 #include "chrome/browser/ash/login/login_auth_recorder.h"
@@ -47,7 +46,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
-#include "chromeos/ash/components/audio/sounds.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/components/dbus/biod/constants.pb.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
@@ -67,9 +65,7 @@
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
-#include "services/audio/public/cpp/sounds/sounds_manager.h"
 #include "services/device/public/mojom/fingerprint.mojom-shared.h"
-#include "ui/base/resource/resource_bundle.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/image/image.h"
@@ -182,17 +178,8 @@
   CHECK(!screen_locker_);
   screen_locker_ = this;
 
-  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
-  audio::SoundsManager* manager = audio::SoundsManager::Get();
-  manager->Initialize(static_cast<int>(Sound::kLock),
-                      bundle.GetRawDataResource(IDR_SOUND_LOCK_WAV),
-                      media::AudioCodec::kPCM);
-  manager->Initialize(static_cast<int>(Sound::kUnlock),
-                      bundle.GetRawDataResource(IDR_SOUND_UNLOCK_WAV),
-                      media::AudioCodec::kPCM);
   content::GetDeviceService().BindFingerprint(
       fp_service_.BindNewPipeAndPassReceiver());
-
   fp_service_->AddFingerprintObserver(
       fingerprint_observer_receiver_.BindNewPipeAndPassRemote());
 
@@ -518,9 +505,6 @@
   }
 
   views_screen_locker_->OnAshLockAnimationFinished();
-
-  AccessibilityManager::Get()->PlayEarcon(
-      Sound::kLock, PlaySoundOption::kOnlyIfSpokenFeedbackEnabled);
 }
 
 user_manager::UserList ScreenLocker::GetUsersToShow() const {
@@ -660,9 +644,6 @@
   }
   VLOG(1) << "Deleting ScreenLocker " << screen_locker_;
 
-  AccessibilityManager::Get()->PlayEarcon(
-      Sound::kUnlock, PlaySoundOption::kOnlyIfSpokenFeedbackEnabled);
-
   delete screen_locker_;
   screen_locker_ = nullptr;
 }
diff --git a/chrome/browser/ash/login/lock/screen_locker_unittest.cc b/chrome/browser/ash/login/lock/screen_locker_unittest.cc
index eae9a4f..b690c30 100644
--- a/chrome/browser/ash/login/lock/screen_locker_unittest.cc
+++ b/chrome/browser/ash/login/lock/screen_locker_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/functional/callback_helpers.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
-#include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/lock_screen_apps/state_controller.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
@@ -30,9 +29,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 "chromeos/ash/components/audio/cras_audio_handler.h"
 #include "chromeos/ash/components/cryptohome/system_salt_getter.h"
-#include "chromeos/ash/components/dbus/audio/cras_audio_client.h"
 #include "chromeos/ash/components/dbus/biod/biod_client.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
 #include "chromeos/ash/components/dbus/userdataauth/cryptohome_misc_client.h"
@@ -46,13 +43,8 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
-#include "content/public/browser/audio_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
-#include "media/audio/test_audio_thread.h"
-#include "services/audio/public/cpp/sounds/audio_stream_handler.h"
-#include "services/audio/public/cpp/sounds/sounds_manager.h"
-#include "services/audio/public/cpp/sounds/test_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ime/ash/mock_input_method_manager_impl.h"
 
@@ -78,7 +70,6 @@
   void SetUp() override {
     ConciergeClient::InitializeFake(/*fake_cicerone_client=*/nullptr);
     BiodClient::InitializeFake();
-    CrasAudioClient::InitializeFake();
     chromeos::TpmManagerClient::InitializeFake();
     CryptohomeMiscClient::InitializeFake();
     UserDataAuthClient::InitializeFake();
@@ -112,16 +103,9 @@
     // Initialize AssistantBrowserDelegate:
     assistant_delegate_ = std::make_unique<AssistantBrowserDelegateImpl>();
 
-    // Initialize AccessibilityManager and dependencies:
-    observer_ = std::make_unique<audio::TestObserver>((base::DoNothing()));
-    audio::AudioStreamHandler::SetObserverForTesting(observer_.get());
-
-    audio::SoundsManager::Create(content::GetAudioServiceStreamFactoryBinder());
     input_method::InputMethodManager::Initialize(
         // Owned by InputMethodManager
         new input_method::MockInputMethodManagerImpl());
-    CrasAudioHandler::InitializeForTesting();
-    AccessibilityManager::Initialize();
 
     // Initialize ScreenLocker dependencies:
     SystemSaltGetter::Initialize();
@@ -147,12 +131,7 @@
 
   void TearDown() override {
     SystemSaltGetter::Shutdown();
-    AccessibilityManager::Shutdown();
-    CrasAudioHandler::Shutdown();
     input_method::InputMethodManager::Shutdown();
-    audio::SoundsManager::Shutdown();
-    audio::AudioStreamHandler::SetObserverForTesting(nullptr);
-    observer_.reset();
     assistant_delegate_.reset();
 
     session_controller_client_.reset();
@@ -166,7 +145,6 @@
     UserDataAuthClient::Shutdown();
     CryptohomeMiscClient::Shutdown();
     chromeos::TpmManagerClient::Shutdown();
-    CrasAudioClient::Shutdown();
     BiodClient::Shutdown();
     ConciergeClient::Shutdown();
   }
@@ -202,8 +180,6 @@
   std::unique_ptr<SessionControllerClientImpl> session_controller_client_;
   std::unique_ptr<AssistantBrowserDelegateImpl> assistant_delegate_;
   SessionTerminationManager session_termination_manager_;
-
-  std::unique_ptr<audio::TestObserver> observer_;
 };
 
 // Chrome notifies Ash when screen is locked. Ash is responsible for suspending
diff --git a/chrome/browser/ash/login/quickstart_controller.cc b/chrome/browser/ash/login/quickstart_controller.cc
index b9441368..be3bea76 100644
--- a/chrome/browser/ash/login/quickstart_controller.cc
+++ b/chrome/browser/ash/login/quickstart_controller.cc
@@ -205,6 +205,13 @@
   CHECK(bootstrap_controller_);
   QS_LOG(INFO) << "Aborting flow: " << reason;
 
+  // If user proceeds with enrollment, allow source device to gracefully close
+  // connection and show "setup complete" UI.
+  if (reason == QuickStartController::AbortFlowReason::ENTERPRISE_ENROLLMENT) {
+    bootstrap_controller_->OnSetupComplete();
+    return;
+  }
+
   bootstrap_controller_->CloseOpenConnections(
       ConnectionClosedReasonFromAbortFlowReason(reason));
   bootstrap_controller_->StopAdvertising();
diff --git a/chrome/browser/ash/mahi/mahi_manager_impl.cc b/chrome/browser/ash/mahi/mahi_manager_impl.cc
index ed34f9f..9b74c51 100644
--- a/chrome/browser/ash/mahi/mahi_manager_impl.cc
+++ b/chrome/browser/ash/mahi/mahi_manager_impl.cc
@@ -239,7 +239,7 @@
       base::UTF16ToUTF8(latest_summary_).c_str());
 
   base::Value::Dict ai_metadata;
-  ai_metadata.Set("from_chromeos", "true");
+  ai_metadata.Set("from_mahi", "true");
 
   chrome::ShowFeedbackPage(
       /*browser=*/chrome::FindBrowserWithActiveWindow(),
@@ -247,7 +247,7 @@
       /*description_placeholder_text=*/
       base::UTF16ToUTF8(
           l10n_util::GetStringUTF16(IDS_SEA_PEN_FEEDBACK_PLACEHOLDER)),
-      /*category_tag=*/std::string(),
+      /*category_tag=*/"mahi",
       /*extra_diagnostics=*/std::string(),
       /*autofill_metadata=*/base::Value::Dict(), std::move(ai_metadata));
 }
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
index 5d97ae48..d0c11af 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
@@ -531,12 +531,7 @@
   EXPECT_TRUE(manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
   VerifyPolicyPopulated();
 
-  // Trigger a policy refresh - this triggers a policy update.
-  DeviceManagementService::JobForTesting policy_job;
-  DeviceManagementService::JobConfiguration::JobType job_type;
-  EXPECT_CALL(job_creation_handler_, OnJobCreation)
-      .WillOnce(DoAll(device_management_service_.CaptureJobType(&job_type),
-                      SaveArg<0>(&policy_job)));
+  EXPECT_CALL(job_creation_handler_, OnJobCreation).Times(0);
   AllowUninterestingRemoteCommandFetches();
 
   EXPECT_FALSE(manager_->GetManagedSessionService());
@@ -545,9 +540,9 @@
 
   InitDeviceCloudPolicyInitializer();
 
-  // Status uploader for reporting on enrolled devices is created for any
-  // could managed device.
-  EXPECT_TRUE(manager_->GetStatusUploader());
+  // Status uploader for reporting on enrolled devices is only created on
+  // connect call.
+  EXPECT_FALSE(manager_->GetStatusUploader());
   // Managed session service and reporters are created when notified by
   // |DeviceCloudPolicyInitializer| that the policy store is ready.
   EXPECT_TRUE(manager_->GetManagedSessionService());
diff --git a/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc b/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc
index befd374..10e49581 100644
--- a/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc
+++ b/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc
@@ -7,12 +7,12 @@
 #include <memory>
 #include <utility>
 
+#include "ash/constants/ash_switches.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash.h"
 #include "chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.h"
 #include "chrome/browser/ash/policy/core/device_cloud_policy_store_ash.h"
-#include "chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/common/chrome_content_client.h"
@@ -122,12 +122,15 @@
     return;
   }
 
-  // Devices that are able to use FRE want to wait for state keys, while others
-  // can proceed immediately.
+  // Currently reven devices don't support server-backed state keys, but they
+  // also don't support FRE/AutoRE so don't block initialization of device
+  // policy on state keys being available on reven.
+  // TODO(b/208705225): Remove this special case when reven supports state keys.
+  const bool allow_init_without_state_keys = ash::switches::IsRevenBranding();
+
   // TODO(b/181140445): If we had a separate state keys upload request to DM
   // Server we could drop the `state_keys_broker_->available()` requirement.
-  if (state_keys_broker_->available() ||
-      !AutoEnrollmentTypeChecker::IsFREEnabled()) {
+  if (allow_init_without_state_keys || state_keys_broker_->available()) {
     StartConnection(CreateClient(enterprise_service_));
   }
 }
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
index d7e14977..2d34130 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
@@ -267,10 +267,12 @@
     return;
   }
 
-  // If the device does not support FRE, it doesn't need state keys.
-  if (!AutoEnrollmentTypeChecker::IsFREEnabled()) {
-    LOG(WARNING)
-        << "Skipping state keys since FRE is not enabled on this device.";
+  // Currently reven devices don't support server-backed state keys, but they
+  // also don't support FRE/AutoRE so don't block enrollment on the
+  // availability of state keys.
+  // TODO(b/208705225): Remove this special case when reven supports state keys.
+  if (ash::switches::IsRevenBranding()) {
+    LOG(WARNING) << "Skipping state keys.";
     HandleStateKeysResult({});
     return;
   }
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_mocha_test_base.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_mocha_test_base.cc
index d8972f5..4586630 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_mocha_test_base.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_mocha_test_base.cc
@@ -28,9 +28,6 @@
       {
           ::ash::features::kFeatureManagementTimeOfDayScreenSaver,
           ::ash::features::kFeatureManagementTimeOfDayWallpaper,
-          ::manta::features::kMantaService,
-          ::ash::features::kSeaPen,
-          ::ash::features::kFeatureManagementSeaPen,
       },
       {});
   set_test_loader_host(
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_base.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_base.cc
index e27e69e..dfb891d 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_base.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_base.cc
@@ -262,6 +262,7 @@
 
   base::Value::Dict ai_metadata;
   ai_metadata.Set("from_chromeos", "true");
+  ai_metadata.Set("is_feature_sea_pen", "true");
   ai_metadata.Set("template_id", metadata->log_id);
 
   base::Value::List options;
@@ -276,6 +277,9 @@
   std::string options_json;
   base::JSONWriter::Write(options, &options_json);
   ai_metadata.Set("template_options", std::move(options_json));
+  ai_metadata.Set(
+      "generation_seed",
+      base::NumberToString(static_cast<int32_t>(metadata->generation_seed)));
 
   base::RecordAction(base::UserMetricsAction("SeaPen_FeedbackPressed"));
   chrome::ShowFeedbackPage(
diff --git a/chrome/browser/ash/wallpaper_handlers/mock_sea_pen_fetcher.cc b/chrome/browser/ash/wallpaper_handlers/mock_sea_pen_fetcher.cc
index ded45ee..d85837f 100644
--- a/chrome/browser/ash/wallpaper_handlers/mock_sea_pen_fetcher.cc
+++ b/chrome/browser/ash/wallpaper_handlers/mock_sea_pen_fetcher.cc
@@ -15,11 +15,27 @@
 #include "base/task/sequenced_task_runner.h"
 #include "components/manta/manta_status.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/image/image_unittest_util.h"
 
 namespace wallpaper_handlers {
 
 namespace {
 
+SkBitmap CreateBitmap() {
+  return gfx::test::CreateBitmap(1, SkColorSetARGB(255, 31, 63, 127));
+}
+
+// Used in `FetchWallpaper` to create a fake JPEG image.
+std::string CreateJpgBytes() {
+  SkBitmap bitmap = CreateBitmap();
+  std::vector<unsigned char> data;
+
+  gfx::JPEGCodec::Encode(bitmap, /*quality=*/100, &data);
+  return std::string(data.begin(), data.end());
+}
+
 std::vector<ash::SeaPenImage> MakeFakeImageResults() {
   std::vector<ash::SeaPenImage> image_results;
   for (uint32_t i = 1; i < 5; i++) {
@@ -50,7 +66,7 @@
              const ash::personalization_app::mojom::SeaPenQueryPtr& query,
              OnFetchWallpaperComplete callback) {
             std::move(callback).Run(
-                ash::SeaPenImage(image.jpg_bytes, image.id));
+                ash::SeaPenImage(CreateJpgBytes(), image.id));
           });
 }
 
diff --git a/chrome/browser/browser_process_platform_part_mac.mm b/chrome/browser/browser_process_platform_part_mac.mm
index 4e0bc85..21a0e00 100644
--- a/chrome/browser/browser_process_platform_part_mac.mm
+++ b/chrome/browser/browser_process_platform_part_mac.mm
@@ -13,7 +13,7 @@
 #include "chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_browser_application_mac.h"
-#include "services/device/public/cpp/geolocation/system_geolocation_source_mac.h"
+#include "services/device/public/cpp/geolocation/system_geolocation_source_apple.h"
 
 BrowserProcessPlatformPart::BrowserProcessPlatformPart() = default;
 
@@ -67,8 +67,8 @@
 
   if (!device::GeolocationSystemPermissionManager::GetInstance()) {
     device::GeolocationSystemPermissionManager::SetInstance(
-        device::SystemGeolocationSourceMac::
-            CreateGeolocationSystemPermissionManagerOnMac());
+        device::SystemGeolocationSourceApple::
+            CreateGeolocationSystemPermissionManager());
   }
 }
 
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 74899e2..c47b66f 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -229,9 +229,6 @@
         <include name="IDR_CHROME_URLS_DISABLED_PAGE_HTML" file="resources\chromeos\chrome_urls_disabled_page\app.html" type="BINDATA" />
         <include name="IDR_ECHO_MANIFEST" file="resources\chromeos\echo\manifest.json" type="BINDATA" />
         <include name="IDR_INCOGNITO_NAVIGATION_BLOCKED_PAGE_HTML" file="resources\chromeos\enterprise\incognito_navigation_blocked_page\incognito_blocked.html" flattenhtml="true" type="BINDATA" />
-        <if expr="_google_chrome">
-          <include name="IDR_QUICKOFFICE_MANIFEST" file="resources\chromeos\quickoffice\manifest.json" type="BINDATA" />
-        </if>
       </if>
       <if expr="_google_chrome">
         <include name="IDR_PREF_HASH_SEED_BIN" file="resources\settings\internal\pref_hash_seed.bin" type="BINDATA" />
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 48133b3..1d9bfb1 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -324,6 +324,11 @@
 #include "chrome/browser/offline_pages/offline_page_info_handler.h"
 #endif
 
+#if BUILDFLAG(ENABLE_OOP_PRINTING)
+#include "chrome/browser/printing/prefs_util.h"
+#include "chrome/browser/printing/printing_init.h"
+#endif
+
 #if BUILDFLAG(ENABLE_PLUGINS)
 #include "chrome/browser/plugins/plugin_prefs.h"
 #endif
@@ -1713,7 +1718,13 @@
 
 #if BUILDFLAG(ENABLE_PRINTING)
   printing::InitializeProcessForPrinting();
+
+#if BUILDFLAG(ENABLE_OOP_PRINTING)
+  if (printing::ShouldEarlyStartPrintBackendService()) {
+    printing::EarlyStartPrintBackendService();
+  }
 #endif
+#endif  // BUILDFLAG(ENABLE_PRINTING)
 
   HandleTestParameters(*base::CommandLine::ForCurrentProcess());
 
diff --git a/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.cc b/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.cc
index fd38d22..9d09e68 100644
--- a/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.cc
+++ b/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.cc
@@ -9,6 +9,7 @@
 #include "base/functional/callback.h"
 #include "chrome/browser/chromeos/mahi/mahi_browser_util.h"
 #include "chromeos/components/mahi/public/mojom/content_extraction.mojom.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/crosapi/mojom/mahi.mojom.h"
 #include "content/public/browser/service_process_host.h"
 
@@ -23,8 +24,10 @@
     base::RepeatingCallback<void(const base::UnguessableToken&, bool)>
         distillable_check_callback)
     : distillable_check_callback_(std::move(distillable_check_callback)) {
-  SetUpContentExtractionService();
-  EnsureServiceIsConnected();
+  if (chromeos::features::IsMahiEnabled()) {
+    SetUpContentExtractionService();
+    EnsureServiceIsConnected();
+  }
 }
 
 MahiContentExtractionDelegate::~MahiContentExtractionDelegate() = default;
diff --git a/chrome/browser/chromeos/mahi/mahi_tab_helper_unittest.cc b/chrome/browser/chromeos/mahi/mahi_tab_helper_unittest.cc
index 5bcc7a93..8bff5e7c 100644
--- a/chrome/browser/chromeos/mahi/mahi_tab_helper_unittest.cc
+++ b/chrome/browser/chromeos/mahi/mahi_tab_helper_unittest.cc
@@ -5,8 +5,10 @@
 #include "chrome/browser/chromeos/mahi/mahi_tab_helper.h"
 
 #include <memory>
+#include <utility>
 
-#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/chromeos/mahi/mahi_web_contents_manager.h"
 #include "chrome/browser/chromeos/mahi/test/mock_mahi_web_contents_manager.h"
 #include "chrome/browser/chromeos/mahi/test/scoped_mahi_web_contents_manager_for_testing.h"
@@ -18,6 +20,14 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/test/scoped_feature_list.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/startup/browser_init_params.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 namespace mahi {
 
 using testing::_;
@@ -27,7 +37,14 @@
  protected:
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
+#if BUILDFLAG(IS_CHROMEOS_ASH)
     scoped_feature_list_.InitAndEnableFeature(chromeos::features::kMahi);
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+    crosapi::mojom::BrowserInitParamsPtr init_params =
+        chromeos::BrowserInitParams::GetForTests()->Clone();
+    init_params->is_mahi_enabled = true;
+    chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
+#endif
     scoped_mahi_web_contents_manager_ =
         std::make_unique<ScopedMahiWebContentsManagerForTesting>(
             &mock_mahi_web_contents_manager_);
@@ -47,7 +64,9 @@
     ChromeRenderViewHostTestHarness::TearDown();
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   base::test::ScopedFeatureList scoped_feature_list_;
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   MockMahiWebContentsManager mock_mahi_web_contents_manager_;
   std::unique_ptr<ScopedMahiWebContentsManagerForTesting>
diff --git a/chrome/browser/chromeos/mahi/mahi_web_contents_manager_browsertest.cc b/chrome/browser/chromeos/mahi/mahi_web_contents_manager_browsertest.cc
index 25c32f2d..3193f69 100644
--- a/chrome/browser/chromeos/mahi/mahi_web_contents_manager_browsertest.cc
+++ b/chrome/browser/chromeos/mahi/mahi_web_contents_manager_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include "base/callback_list.h"
 #include "base/run_loop.h"
@@ -13,6 +14,8 @@
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/unguessable_token.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/chromeos/mahi/mahi_browser_util.h"
 #include "chrome/browser/chromeos/mahi/test/fake_mahi_web_contents_manager.h"
 #include "chrome/browser/chromeos/mahi/test/mock_mahi_crosapi.h"
@@ -33,6 +36,14 @@
 #include "ui/gfx/image/image_skia.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/test/scoped_feature_list.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/startup/browser_init_params.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 namespace mahi {
 
 namespace {
@@ -50,7 +61,9 @@
 class MahiWebContentsManagerBrowserTest : public InProcessBrowserTest {
  public:
   MahiWebContentsManagerBrowserTest() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
     scoped_feature_list_.InitWithFeatures({chromeos::features::kMahi}, {});
+#endif
   }
   ~MahiWebContentsManagerBrowserTest() override = default;
 
@@ -82,6 +95,15 @@
   }
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
+  void CreatedBrowserMainParts(
+      content::BrowserMainParts* browser_main_parts) override {
+    crosapi::mojom::BrowserInitParamsPtr init_params =
+        chromeos::BrowserInitParams::GetForTests()->Clone();
+    init_params->is_mahi_enabled = true;
+    chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
+    InProcessBrowserTest::CreatedBrowserMainParts(browser_main_parts);
+  }
+
   bool IsServiceAvailable() const {
     chromeos::LacrosService* lacros_service = chromeos::LacrosService::Get();
     return lacros_service &&
@@ -98,7 +120,9 @@
     EXPECT_TRUE(AddTabAtIndex(0, GURL(kUrl), ui::PAGE_TRANSITION_TYPED));
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   base::test::ScopedFeatureList scoped_feature_list_;
+#endif
 
   testing::StrictMock<MockMahiCrosapi> browser_delegate_;
   mojo::Receiver<crosapi::mojom::MahiBrowserDelegate> receiver_{
diff --git a/chrome/browser/companion/core/utils.cc b/chrome/browser/companion/core/utils.cc
index 5e5c9cc5..d00b246 100644
--- a/chrome/browser/companion/core/utils.cc
+++ b/chrome/browser/companion/core/utils.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/companion/core/utils.h"
 
+#include <string_view>
+
 #include "base/containers/fixed_flat_set.h"
 #include "chrome/browser/companion/core/constants.h"
 #include "chrome/browser/companion/core/features.h"
@@ -144,9 +146,8 @@
   }
 
   static constexpr auto chrome_domain_allowlists =
-      base::MakeFixedFlatSet<base::StringPiece>(
-          {"chrome://settings/syncSetup"});
-  base::StringPiece url_string(url.spec());
+      base::MakeFixedFlatSet<std::string_view>({"chrome://settings/syncSetup"});
+  std::string_view url_string(url.spec());
 
   if (!url.SchemeIsHTTPOrHTTPS() &&
       !chrome_domain_allowlists.contains(url_string)) {
diff --git a/chrome/browser/companion/visual_query/visual_query_classifier_host.cc b/chrome/browser/companion/visual_query/visual_query_classifier_host.cc
index c73396dd..3bfe0da 100644
--- a/chrome/browser/companion/visual_query/visual_query_classifier_host.cc
+++ b/chrome/browser/companion/visual_query/visual_query_classifier_host.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/companion/visual_query/visual_query_classifier_host.h"
 
 #include <optional>
+#include <string_view>
 
 #include "base/base64.h"
 #include "base/metrics/histogram_functions.h"
@@ -39,7 +40,7 @@
     return std::nullopt;
   }
 
-  base::StringPiece mime_subtype = "jpg";
+  std::string_view mime_subtype = "jpg";
   std::string result = "data:image/";
   result.append(mime_subtype.begin(), mime_subtype.end());
   result.append(";base64,");
diff --git a/chrome/browser/controlled_frame/controlled_frame_menu_icon_loader_unittest.cc b/chrome/browser/controlled_frame/controlled_frame_menu_icon_loader_unittest.cc
index e8dc8f7..c72fbc7 100644
--- a/chrome/browser/controlled_frame/controlled_frame_menu_icon_loader_unittest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_menu_icon_loader_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/controlled_frame/controlled_frame_menu_icon_loader.h"
 
 #include "base/test/bind.h"
+#include "base/test/gmock_expected_support.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
 #include "cc/test/pixel_comparator.h"
@@ -12,32 +13,27 @@
 #include "chrome/browser/extensions/context_menu_matcher.h"
 #include "chrome/browser/extensions/menu_manager.h"
 #include "chrome/browser/extensions/menu_manager_factory.h"
-#include "chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h"
-#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_install_source.h"
+#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/fake_web_contents_manager.h"
 #include "chrome/browser/web_applications/test/web_app_icon_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_test.h"
-#include "chrome/browser/web_applications/web_app.h"
-#include "chrome/browser/web_applications/web_app_install_info.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
-#include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "chrome/common/chrome_features.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/gfx/favicon_size.h"
-#include "url/origin.h"
 
 namespace controlled_frame {
 
 namespace {
-constexpr base::StringPiece kManifestPath =
-    "/.well-known/_generated_install_page.html";
-constexpr base::StringPiece kIconPath = "/icon.png";
+constexpr base::StringPiece kIconPath = "/icon2.png";
 const int kTestWebViewInstanceId = 1;
 }  // namespace
 
@@ -53,48 +49,22 @@
   void SetUp() override {
     WebAppTest::SetUp();
     web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
-    web_app::IsolatedWebAppUrlInfo url_info =
-        CreateIsolatedWebApp(kDevAppOriginUrl);
-    NavigateAndCommit(kDevAppOriginUrl);
-  }
 
-  web_app::IsolatedWebAppUrlInfo CreateIsolatedWebApp(const GURL& url) {
-    const base::expected<web_app::IsolatedWebAppUrlInfo, std::string> url_info =
-        web_app::IsolatedWebAppUrlInfo::Create(url);
-    CHECK(url_info.has_value());
-    SetUpPageAndIconStates(*url_info);
-    base::test::TestFuture<
-        base::expected<web_app::InstallIsolatedWebAppCommandSuccess,
-                       web_app::InstallIsolatedWebAppCommandError>>
-        future;
-    fake_provider().scheduler().InstallIsolatedWebApp(
-        *url_info,
-        web_app::IsolatedWebAppInstallSource::FromDevUi(
-            web_app::IwaSourceProxy(url::Origin::Create(GURL(url)))),
-        /*expected_version=*/base::Version("1.0.0"),
-        /*optional_keep_alive=*/nullptr,
-        /*optional_profile_keep_alive=*/nullptr, future.GetCallback());
-    auto install_result = future.Take();
-    EXPECT_TRUE(install_result.has_value()) << install_result.error();
-    return *url_info;
-  }
+    app_ = web_app::IsolatedWebAppBuilder(web_app::ManifestBuilder())
+               .AddIconAsPng(kIconPath, web_app::CreateSquareIcon(
+                                            gfx::kFaviconSize, SK_ColorRED))
+               .BuildBundle();
+    app_->TrustSigningKey();
+    app_->FakeInstallPageState(profile());
+    ASSERT_OK_AND_ASSIGN(web_app::IsolatedWebAppUrlInfo url_info,
+                         app_->Install(profile()));
+    app_origin_url_ = url_info.origin().GetURL();
 
-  void SetUpPageAndIconStates(const web_app::IsolatedWebAppUrlInfo& url_info) {
-    GURL application_url = url_info.origin().GetURL();
-    auto& page_state = web_contents_manager().GetOrCreatePageState(
-        application_url.Resolve(kManifestPath));
-    page_state.url_load_result = web_app::WebAppUrlLoader::Result::kUrlLoaded;
-    page_state.error_code = webapps::InstallableStatusCode::NO_ERROR_DETECTED;
+    web_app::SimulateIsolatedWebAppNavigation(web_contents(), app_origin_url_);
 
-    page_state.manifest_url = CreateDefaultManifestURL(application_url);
-    page_state.valid_manifest_for_web_app = true;
-    page_state.manifest_before_default_processing =
-        CreateDefaultManifest(application_url);
-
-    auto& icon_state = web_contents_manager().GetOrCreateIconState(
-        application_url.Resolve(kIconPath));
-    icon_state.bitmaps = {
-        web_app::CreateSquareIcon(gfx::kFaviconSize, SK_ColorRED)};
+    CHECK_EQ(
+        web_contents()->GetPrimaryMainFrame()->GetWebExposedIsolationLevel(),
+        content::WebExposedIsolationLevel::kIsolatedApplication);
   }
 
   // Creates and returns a menu manager.
@@ -123,41 +93,18 @@
         extensions::MenuItem::ContextList(extensions::MenuItem::LAUNCHER));
   }
 
-  GURL CreateDefaultManifestURL(const GURL& application_url) {
-    return application_url.Resolve("/manifest.webmanifest");
-  }
-
-  blink::mojom::ManifestPtr CreateDefaultManifest(const GURL& application_url) {
-    auto manifest = blink::mojom::Manifest::New();
-    manifest->id = application_url.DeprecatedGetOriginAsURL();
-    manifest->scope = application_url.Resolve("/");
-    manifest->start_url = application_url.Resolve("/ix.html");
-    manifest->display = web_app::DisplayMode::kStandalone;
-    manifest->short_name = u"test short manifest name";
-    manifest->version = u"1.0.0";
-
-    blink::Manifest::ImageResource icon;
-    icon.src = application_url.Resolve(kIconPath);
-    icon.purpose = {blink::mojom::ManifestImageResource_Purpose::ANY};
-    icon.type = u"image/png";
-    icon.sizes = {gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)};
-    manifest->icons.push_back(icon);
-
-    return manifest;
-  }
+  GURL app_origin_url() { return app_origin_url_; }
 
   web_app::FakeWebContentsManager& web_contents_manager() {
     return static_cast<web_app::FakeWebContentsManager&>(
         fake_provider().web_contents_manager());
   }
 
- protected:
-  const GURL kDevAppOriginUrl = GURL(
-      "isolated-app://"
-      "aerugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaac");
-
  private:
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
   base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<web_app::ScopedBundledIsolatedWebApp> app_;
+  GURL app_origin_url_;
 };
 
 TEST_F(ControlledFrameMenuIconLoaderTest, LoadGetAndRemoveIcon) {
@@ -259,7 +206,7 @@
 
   web_app::FakeWebContentsManager::FakeIconState& icon_state =
       web_contents_manager().GetOrCreateIconState(
-          kDevAppOriginUrl.Resolve(kIconPath));
+          app_origin_url().Resolve(kIconPath));
   ASSERT_EQ(1u, icon_state.bitmaps.size());
   EXPECT_TRUE(cc::MatchesBitmap(icon_state.bitmaps[0], icon.AsBitmap(),
                                 cc::ExactPixelComparator()));
diff --git a/chrome/browser/dips/dips_bounce_detector_browsertest.cc b/chrome/browser/dips/dips_bounce_detector_browsertest.cc
index ed7d73c..d4628103 100644
--- a/chrome/browser/dips/dips_bounce_detector_browsertest.cc
+++ b/chrome/browser/dips/dips_bounce_detector_browsertest.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/files/file_path.h"
@@ -159,7 +160,7 @@
 void AppendSitesInReport(std::vector<std::string>* reports,
                          const std::set<std::string>& sites) {
   reports->push_back(base::JoinString(
-      std::vector<base::StringPiece>(sites.begin(), sites.end()), ", "));
+      std::vector<std::string_view>(sites.begin(), sites.end()), ", "));
 }
 
 std::vector<url::Origin> GetOrigins(const AttributionData& data) {
diff --git a/chrome/browser/dips/dips_bounce_detector_unittest.cc b/chrome/browser/dips/dips_bounce_detector_unittest.cc
index 5f9a9ae..636a1ef 100644
--- a/chrome/browser/dips/dips_bounce_detector_unittest.cc
+++ b/chrome/browser/dips/dips_bounce_detector_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/dips/dips_bounce_detector.h"
 
+#include <string_view>
 #include <tuple>
 
 #include "base/functional/bind.h"
@@ -110,7 +111,7 @@
     }
 
     reported_sites_.push_back(base::JoinString(
-        std::vector<base::StringPiece>(sites.begin(), sites.end()), ", "));
+        std::vector<std::string_view>(sites.begin(), sites.end()), ", "));
   }
 
   void OnSiteStorageAccessed(const GURL& first_party_url,
diff --git a/chrome/browser/dips/dips_database.cc b/chrome/browser/dips/dips_database.cc
index 871b7c7..c12d321 100644
--- a/chrome/browser/dips/dips_database.cc
+++ b/chrome/browser/dips/dips_database.cc
@@ -8,6 +8,7 @@
 #include <limits>
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/files/file_path.h"
@@ -925,7 +926,7 @@
                     "last_web_authn_assertion_time FROM bounces "
                     "WHERE site IN(",
                     base::JoinString(
-                        std::vector<base::StringPiece>(sites.size(), "?"), ","),
+                        std::vector<std::string_view>(sites.size(), "?"), ","),
                     ")"})
           .c_str()));
 
@@ -1041,7 +1042,7 @@
   SCOPED_UMA_HISTOGRAM_TIMER("Privacy.DIPS.Database.Operation.RemoveRowsTime");
 
   const std::string site_list =
-      base::JoinString(std::vector<base::StringPiece>(sites.size(), "?"), ",");
+      base::JoinString(std::vector<std::string_view>(sites.size(), "?"), ",");
 
   if (table == DIPSDatabaseTable::kBounces) {
     sql::Statement statement(db_->GetUniqueStatement(
@@ -1464,7 +1465,7 @@
   }
 
   std::string placeholders =
-      base::JoinString(std::vector<base::StringPiece>(sites.size(), "?"), ",");
+      base::JoinString(std::vector<std::string_view>(sites.size(), "?"), ",");
 
   if ((type & DIPSEventRemovalType::kStorage) ==
       DIPSEventRemovalType::kStorage) {
diff --git a/chrome/browser/dips/dips_test_utils.cc b/chrome/browser/dips/dips_test_utils.cc
index be72fa1..fd81eae 100644
--- a/chrome/browser/dips/dips_test_utils.cc
+++ b/chrome/browser/dips/dips_test_utils.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/dips/dips_test_utils.h"
 
+#include <string_view>
+
 #include "base/test/bind.h"
 #include "chrome/browser/dips/dips_cleanup_service_factory.h"
 #include "chrome/browser/dips/dips_service_factory.h"
@@ -54,7 +56,7 @@
 
 bool NavigateToSetCookie(content::WebContents* web_contents,
                          const net::EmbeddedTestServer* server,
-                         base::StringPiece host,
+                         std::string_view host,
                          bool is_secure_cookie_set,
                          bool is_ad_tagged) {
   std::string relative_url = "/set-cookie?name=value";
diff --git a/chrome/browser/dips/dips_test_utils.h b/chrome/browser/dips/dips_test_utils.h
index a12654a..070eb64 100644
--- a/chrome/browser/dips/dips_test_utils.h
+++ b/chrome/browser/dips/dips_test_utils.h
@@ -7,6 +7,7 @@
 
 #include <iosfwd>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/run_loop.h"
@@ -110,7 +111,7 @@
 // OnCookiesAccessed() to be called.
 bool NavigateToSetCookie(content::WebContents* web_contents,
                          const net::EmbeddedTestServer* server,
-                         base::StringPiece host,
+                         std::string_view host,
                          bool is_secure_cookie_set,
                          bool is_ad_tagged);
 
diff --git a/chrome/browser/dips/dips_utils.cc b/chrome/browser/dips/dips_utils.cc
index 1fb8a3b62..242e9d4 100644
--- a/chrome/browser/dips/dips_utils.cc
+++ b/chrome/browser/dips/dips_utils.cc
@@ -5,9 +5,9 @@
 #include "chrome/browser/dips/dips_utils.h"
 
 #include <algorithm>
+#include <string_view>
 
 #include "base/feature_list.h"
-#include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "chrome/browser/profiles/profile_selections.h"
 #include "chrome/browser/tpcd/heuristics/opener_heuristic_tab_helper.h"
@@ -71,7 +71,7 @@
 
 // SiteDataAccessType:
 
-base::StringPiece SiteDataAccessTypeToString(SiteDataAccessType type) {
+std::string_view SiteDataAccessTypeToString(SiteDataAccessType type) {
   switch (type) {
     case SiteDataAccessType::kUnknown:
       return "Unknown";
@@ -96,7 +96,7 @@
                 : DIPSCookieMode::kBlock3PC;
 }
 
-base::StringPiece GetHistogramSuffix(DIPSCookieMode mode) {
+std::string_view GetHistogramSuffix(DIPSCookieMode mode) {
   // Any changes here need to be reflected in DIPSCookieMode in
   // tools/metrics/histograms/metadata/others/histograms.xml
   switch (mode) {
@@ -106,7 +106,7 @@
       return ".OffTheRecord_Block3PC";
   }
   DCHECK(false) << "Invalid DIPSCookieMode";
-  return base::StringPiece();
+  return std::string_view();
 }
 
 const char* DIPSCookieModeToString(DIPSCookieMode mode) {
@@ -123,7 +123,7 @@
 }
 
 // DIPSRedirectType:
-base::StringPiece GetHistogramPiece(DIPSRedirectType type) {
+std::string_view GetHistogramPiece(DIPSRedirectType type) {
   // Any changes here need to be reflected in
   // tools/metrics/histograms/metadata/privacy/histograms.xml
   switch (type) {
@@ -133,7 +133,7 @@
       return "Server";
   }
   DCHECK(false) << "Invalid DIPSRedirectType";
-  return base::StringPiece();
+  return std::string_view();
 }
 
 const char* DIPSRedirectTypeToString(DIPSRedirectType type) {
diff --git a/chrome/browser/dips/dips_utils.h b/chrome/browser/dips/dips_utils.h
index 2f804619..d9204b88 100644
--- a/chrome/browser/dips/dips_utils.h
+++ b/chrome/browser/dips/dips_utils.h
@@ -7,9 +7,9 @@
 
 #include <optional>
 #include <ostream>
+#include <string_view>
 
 #include "base/files/file_path.h"
-#include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "components/content_settings/browser/page_specific_content_settings.h"
 #include "content/public/browser/navigation_handle.h"
@@ -68,7 +68,7 @@
   return (op == CookieOperation::kChange ? SiteDataAccessType::kWrite
                                          : SiteDataAccessType::kRead);
 }
-base::StringPiece SiteDataAccessTypeToString(SiteDataAccessType type);
+std::string_view SiteDataAccessTypeToString(SiteDataAccessType type);
 std::ostream& operator<<(std::ostream& os, SiteDataAccessType access_type);
 
 constexpr SiteDataAccessType operator|(SiteDataAccessType lhs,
@@ -81,7 +81,7 @@
 enum class DIPSCookieMode { kBlock3PC, kOffTheRecord_Block3PC };
 
 DIPSCookieMode GetDIPSCookieMode(bool is_otr);
-base::StringPiece GetHistogramSuffix(DIPSCookieMode mode);
+std::string_view GetHistogramSuffix(DIPSCookieMode mode);
 const char* DIPSCookieModeToString(DIPSCookieMode mode);
 std::ostream& operator<<(std::ostream& os, DIPSCookieMode mode);
 
@@ -121,7 +121,7 @@
 // DIPSRedirectType:
 enum class DIPSRedirectType { kClient, kServer };
 
-base::StringPiece GetHistogramPiece(DIPSRedirectType type);
+std::string_view GetHistogramPiece(DIPSRedirectType type);
 const char* DIPSRedirectTypeToString(DIPSRedirectType type);
 std::ostream& operator<<(std::ostream& os, DIPSRedirectType type);
 
diff --git a/chrome/browser/download/bubble/download_bubble_accessible_alerts_map.cc b/chrome/browser/download/bubble/download_bubble_accessible_alerts_map.cc
index 8ca51ed..80b59c3 100644
--- a/chrome/browser/download/bubble/download_bubble_accessible_alerts_map.cc
+++ b/chrome/browser/download/bubble/download_bubble_accessible_alerts_map.cc
@@ -119,11 +119,6 @@
   return to_announce;
 }
 
-void DownloadBubbleAccessibleAlertsMap::ClearAnnouncedTimeForContentId(
-    const ContentId& content_id) {
-  last_alerted_times_.erase(content_id);
-}
-
 void DownloadBubbleAccessibleAlertsMap::GarbageCollect() {
   std::erase_if(unannounced_alerts_,
                 [](const auto& kv) { return kv.second.IsStale(); });
diff --git a/chrome/browser/download/bubble/download_bubble_accessible_alerts_map.h b/chrome/browser/download/bubble/download_bubble_accessible_alerts_map.h
index 5c1ae3a..25d3c153 100644
--- a/chrome/browser/download/bubble/download_bubble_accessible_alerts_map.h
+++ b/chrome/browser/download/bubble/download_bubble_accessible_alerts_map.h
@@ -87,11 +87,6 @@
   // removed from `unannounced_alerts_`.
   std::vector<std::u16string> TakeAlertsForAnnouncement();
 
-  // Clears any last-announced time for the content id. Called when a download
-  // is finished or removed, to prevent memory usage from growing unboundedly.
-  void ClearAnnouncedTimeForContentId(
-      const offline_items_collection::ContentId& content_id);
-
   // Iterates over both maps and garbage collects stale alerts and
   // last-alerted times that were too long ago.
   void GarbageCollect();
diff --git a/chrome/browser/download/bubble/download_bubble_accessible_alerts_map_unittest.cc b/chrome/browser/download/bubble/download_bubble_accessible_alerts_map_unittest.cc
index 1149c8b..dc473f97 100644
--- a/chrome/browser/download/bubble/download_bubble_accessible_alerts_map_unittest.cc
+++ b/chrome/browser/download/bubble/download_bubble_accessible_alerts_map_unittest.cc
@@ -275,34 +275,6 @@
   EXPECT_THAT(map().unannounced_alerts_for_testing(), IsEmpty());
 }
 
-TEST_F(DownloadBubbleAccessibleAlertsMapTest, ClearAnnouncedTimeForContentId) {
-  base::Time now = base::Time::Now();
-  EXPECT_TRUE(map().MaybeAddAccessibleAlert(
-      CreateTestContentId("download1"), Alert{Urgency::kAlertSoon, u"alert1"}));
-  EXPECT_TRUE(map().MaybeAddAccessibleAlert(
-      CreateTestContentId("download2"), Alert{Urgency::kAlertSoon, u"alert2"}));
-  EXPECT_THAT(map().unannounced_alerts_for_testing(),
-              UnorderedElementsAre(
-                  Pair(CreateTestContentId("download1"),
-                       AllOf(Field(&Alert::urgency, Urgency::kAlertSoon),
-                             Field(&Alert::text, u"alert1"))),
-                  Pair(CreateTestContentId("download2"),
-                       AllOf(Field(&Alert::urgency, Urgency::kAlertSoon),
-                             Field(&Alert::text, u"alert2")))));
-  std::vector<std::u16string> to_announce = map().TakeAlertsForAnnouncement();
-  EXPECT_THAT(to_announce, UnorderedElementsAre(u"alert1", u"alert2"));
-  EXPECT_THAT(
-      map().last_alerted_times_for_testing(),
-      UnorderedElementsAre(Pair(CreateTestContentId("download1"), now),
-                           Pair(CreateTestContentId("download2"), now)));
-  EXPECT_THAT(map().unannounced_alerts_for_testing(), IsEmpty());
-
-  map().ClearAnnouncedTimeForContentId(CreateTestContentId("download2"));
-  EXPECT_THAT(
-      map().last_alerted_times_for_testing(),
-      UnorderedElementsAre(Pair(CreateTestContentId("download1"), now)));
-}
-
 TEST_F(DownloadBubbleAccessibleAlertsMapTest, GarbageCollect) {
   base::Time now = base::Time::Now();
   EXPECT_TRUE(map().MaybeAddAccessibleAlert(
diff --git a/chrome/browser/download/bubble/download_bubble_update_service.cc b/chrome/browser/download/bubble/download_bubble_update_service.cc
index a824c86..ac0f5f7 100644
--- a/chrome/browser/download/bubble/download_bubble_update_service.cc
+++ b/chrome/browser/download/bubble/download_bubble_update_service.cc
@@ -20,6 +20,8 @@
 #include "chrome/browser/download/bubble/download_bubble_update_service_factory.h"
 #include "chrome/browser/download/bubble/download_bubble_utils.h"
 #include "chrome/browser/download/bubble/download_display_controller.h"
+#include "chrome/browser/download/download_core_service.h"
+#include "chrome/browser/download/download_core_service_factory.h"
 #include "chrome/browser/download/download_crx_util.h"
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_item_web_app_data.h"
@@ -46,6 +48,8 @@
 using ::offline_items_collection::ContentId;
 using ::offline_items_collection::OfflineContentProvider;
 using ::offline_items_collection::OfflineItem;
+using ::offline_items_collection::OfflineItemState;
+using DownloadState = download::DownloadItem::DownloadState;
 using DownloadUIModelPtr = DownloadUIModel::DownloadUIModelPtr;
 using ItemSortKey = DownloadBubbleUpdateService::ItemSortKey;
 template <typename Id, typename Item>
@@ -310,6 +314,13 @@
   return nullptr;
 }
 
+DownloadBubbleUpdateService::CacheManager*
+DownloadBubbleUpdateService::GetExistingCacheForWebApp(
+    const webapps::AppId& app_id) {
+  return const_cast<CacheManager*>(
+      std::as_const(*this).GetExistingCacheForWebApp(app_id));
+}
+
 DownloadBubbleUpdateService::CacheManager&
 DownloadBubbleUpdateService::GetCacheForItem(download::DownloadItem* item) {
   auto* web_app_data = DownloadItemWebAppData::Get(item);
@@ -329,11 +340,27 @@
   return cache_managers;
 }
 
+void DownloadBubbleUpdateService::ObserveDownloadHistory() {
+  // If OTR, this is the original profile. Otherwise, this is just the profile
+  // itself.
+  Profile* profile = profile_->GetOriginalProfile();
+  if (DownloadCoreService* dcs =
+          DownloadCoreServiceFactory::GetForBrowserContext(profile);
+      dcs && dcs->GetDownloadHistory()) {
+    download_history_observation_.Observe(dcs->GetDownloadHistory());
+  }
+}
+
 void DownloadBubbleUpdateService::Initialize(
     content::DownloadManager* manager) {
   CHECK(manager);
   CHECK(!download_item_notifier_);
 
+  // This is safe to do here because we know the DownloadManager has been
+  // created by now. If we did this earlier, then it might trigger early
+  // initialization of the DownloadManager and ChromeDownloadManagerDelegate.
+  ObserveDownloadHistory();
+
   // Assume we have an original profile and it has an OTR profile.
   // If the original profile's DownloadBubbleUpdateService is Initialize()'d
   // already when this function is invoked on the OTR profile's
@@ -712,6 +739,27 @@
   return ProgressInfo{};
 }
 
+std::vector<std::u16string> DownloadBubbleUpdateService::CacheManager::
+    TakeAccessibleAlertsForAnnouncement() {
+  std::vector<std::u16string> to_announce =
+      accessible_alerts_.TakeAlertsForAnnouncement();
+  accessible_alerts_.GarbageCollect();
+  return to_announce;
+}
+
+std::vector<std::u16string>
+DownloadBubbleUpdateService::TakeAccessibleAlertsForAnnouncement(
+    const webapps::AppId* web_app_id) {
+  if (web_app_id == nullptr) {
+    return main_cache_.TakeAccessibleAlertsForAnnouncement();
+  }
+  if (CacheManager* cache = GetExistingCacheForWebApp(*web_app_id);
+      cache != nullptr) {
+    return cache->TakeAccessibleAlertsForAnnouncement();
+  }
+  return std::vector<std::u16string>();
+}
+
 void DownloadBubbleUpdateService::OnDownloadCreated(
     content::DownloadManager* manager,
     download::DownloadItem* item) {
@@ -740,7 +788,9 @@
         kCrxShowNewItemDelay);
     return;
   }
-  GetCacheForItem(item).MaybeAddDownloadItemToCache(item, /*is_new=*/true);
+  GetCacheForItem(item).MaybeAddDownloadItemToCache(
+      item, /*is_new=*/true,
+      /*maybe_add_alert=*/download_history_loaded_);
   // NotifyWindowsOfDownloadItemAdded() is called from
   // DownloadBubbleUIControllerDelegate for new non-crx downloads.
 }
@@ -759,7 +809,9 @@
   download::DownloadItem* item =
       download_item_notifier_->GetManager()->GetDownloadByGuid(guid);
   if (item && !item->IsDone()) {
-    GetCacheForItem(item).MaybeAddDownloadItemToCache(item, /*is_new=*/true);
+    GetCacheForItem(item).MaybeAddDownloadItemToCache(
+        item, /*is_new=*/true,
+        /*maybe_add_alert=*/download_history_loaded_);
     NotifyWindowsOfDownloadItemAdded(item);
   }
   size_t erased = delayed_crx_guids_.erase(guid);
@@ -825,7 +877,9 @@
     download::DownloadItem* item) {
   bool cache_was_at_max = IsDownloadItemCacheAtMax();
   bool removed_item = RemoveDownloadItemFromCache(item);
-  bool added_back_at_end = MaybeAddDownloadItemToCache(item, /*is_new=*/false);
+  bool added_back_at_end = MaybeAddDownloadItemToCache(
+      item, /*is_new=*/false,
+      /*maybe_add_alert=*/update_service_->download_history_loaded());
   if (cache_was_at_max && removed_item && added_back_at_end) {
     CHECK_EQ(download_items_.size(), GetNumItemsToCache());
     const ItemSortKey& last_key =
@@ -895,7 +949,8 @@
     return;
   }
   for (const OfflineItem& item : items) {
-    main_cache_.MaybeAddOfflineItemToCache(item, /*is_new=*/true);
+    main_cache_.MaybeAddOfflineItemToCache(item, /*is_new=*/true,
+                                           /*maybe_add_alert=*/true);
   }
 
   for (Browser* browser : chrome::FindAllBrowsersWithProfile(profile_)) {
@@ -968,7 +1023,8 @@
     const OfflineItem& item) {
   bool cache_was_at_max = IsOfflineItemCacheAtMax();
   bool removed_item = RemoveOfflineItemFromCache(GetItemId(item));
-  bool added_back_to_end = MaybeAddOfflineItemToCache(item, /*is_new=*/false);
+  bool added_back_to_end = MaybeAddOfflineItemToCache(item, /*is_new=*/false,
+                                                      /*maybe_add_alert=*/true);
   if (cache_was_at_max && removed_item && added_back_to_end) {
     CHECK_EQ(offline_items_.size(), GetNumItemsToCache());
     const ItemSortKey& last_key = std::prev(GetLastIter(offline_items_))->first;
@@ -981,22 +1037,40 @@
   main_cache_.DropAllOfflineItems();
 }
 
+void DownloadBubbleUpdateService::OnHistoryQueryComplete() {
+  download_history_loaded_ = true;
+}
+
+void DownloadBubbleUpdateService::OnDownloadHistoryDestroyed() {
+  download_history_observation_.Reset();
+}
+
 bool DownloadBubbleUpdateService::CacheManager::MaybeAddDownloadItemToCache(
     download::DownloadItem* item,
-    bool is_new) {
-  DownloadItemModel model(item);
+    bool is_new,
+    bool maybe_add_alert) {
+  DownloadItemModel model(
+      item, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>());
   if (!ShouldIncludeModel(&model, GetCutoffTime())) {
     return false;
   }
   if (is_new && model.ShouldNotifyUI()) {
     model.SetActionedOn(false);
   }
+  if (maybe_add_alert) {
+    // Garbage collect accessible alerts before we add another item because this
+    // can be called after a long time since the last alert activity.
+    accessible_alerts_.GarbageCollect();
+    accessible_alerts_.MaybeAddAccessibleAlert(
+        model.GetContentId(), GetAccessibleAlertForModel(model));
+  }
   return AddItemToCacheImpl(item, download_items_, download_items_iter_map_);
 }
 
 bool DownloadBubbleUpdateService::CacheManager::MaybeAddOfflineItemToCache(
     const OfflineItem& item,
-    bool is_new) {
+    bool is_new,
+    bool maybe_add_alert) {
   CHECK(update_service_->IsMainCache(*this));
   if (update_service_->IsProfileOtr() != item.is_off_the_record) {
     return false;
@@ -1011,13 +1085,22 @@
     return false;
   }
 
-  OfflineItemModel model(update_service_->GetOfflineManager(), item);
+  OfflineItemModel model(
+      update_service_->GetOfflineManager(), item,
+      std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>());
   if (!ShouldIncludeModel(&model, GetCutoffTime())) {
     return false;
   }
   if (is_new && model.ShouldNotifyUI()) {
     model.SetActionedOn(false);
   }
+  if (maybe_add_alert) {
+    // Garbage collect accessible alerts before we add another item because this
+    // can be called after a long time since the last alert activity.
+    accessible_alerts_.GarbageCollect();
+    accessible_alerts_.MaybeAddAccessibleAlert(
+        model.GetContentId(), GetAccessibleAlertForModel(model));
+  }
 
   return AddItemToCacheImpl(item, offline_items_, offline_items_iter_map_);
 }
@@ -1132,7 +1215,8 @@
     if (GetSortKey(item) < last_key) {
       continue;
     }
-    GetCacheForItem(item).MaybeAddDownloadItemToCache(item, /*is_new=*/false);
+    GetCacheForItem(item).MaybeAddDownloadItemToCache(
+        item, /*is_new=*/false, /*maybe_add_alert=*/false);
   }
 }
 
@@ -1158,7 +1242,8 @@
     if (GetSortKey(item) < last_key) {
       continue;
     }
-    main_cache_.MaybeAddOfflineItemToCache(item, /*is_new=*/false);
+    main_cache_.MaybeAddOfflineItemToCache(item, /*is_new=*/false,
+                                           /*maybe_add_alert=*/false);
   }
 }
 
@@ -1173,7 +1258,8 @@
     cache->DropAllDownloadItems();
   }
   for (download::DownloadItem* item : GetAllDownloadItems()) {
-    GetCacheForItem(item).MaybeAddDownloadItemToCache(item, /*is_new=*/false);
+    GetCacheForItem(item).MaybeAddDownloadItemToCache(
+        item, /*is_new=*/false, /*maybe_add_alert=*/false);
   }
 }
 
@@ -1201,7 +1287,8 @@
     const std::vector<OfflineItem>& all_items) {
   main_cache_.DropAllOfflineItems();
   for (const OfflineItem& item : all_items) {
-    main_cache_.MaybeAddOfflineItemToCache(item, /*is_new=*/false);
+    main_cache_.MaybeAddOfflineItemToCache(item, /*is_new=*/false,
+                                           /*maybe_add_alert=*/false);
   }
   offline_items_initialized_ = true;
   for (auto& callback : offline_item_callbacks_) {
diff --git a/chrome/browser/download/bubble/download_bubble_update_service.h b/chrome/browser/download/bubble/download_bubble_update_service.h
index 28d8c388..c1e1cedd 100644
--- a/chrome/browser/download/bubble/download_bubble_update_service.h
+++ b/chrome/browser/download/bubble/download_bubble_update_service.h
@@ -15,8 +15,10 @@
 #include "base/scoped_observation.h"
 #include "base/time/time.h"
 #include "base/types/optional_ref.h"
+#include "chrome/browser/download/bubble/download_bubble_accessible_alerts_map.h"
 #include "chrome/browser/download/bubble/download_bubble_display_info.h"
 #include "chrome/browser/download/bubble/download_display_controller.h"
+#include "chrome/browser/download/download_history.h"
 #include "chrome/browser/download/download_ui_model.h"
 #include "chrome/browser/ui/download/download_display.h"
 #include "components/download/content/public/all_download_item_notifier.h"
@@ -41,7 +43,8 @@
 class DownloadBubbleUpdateService
     : public KeyedService,
       public download::AllDownloadItemNotifier::Observer,
-      public offline_items_collection::OfflineContentProvider::Observer {
+      public offline_items_collection::OfflineContentProvider::Observer,
+      public DownloadHistory::Observer {
  public:
   // Defines sort priority for items.
   struct ItemSortKey {
@@ -110,9 +113,20 @@
   virtual DownloadDisplay::ProgressInfo GetProgressInfo(
       const webapps::AppId* web_app_id) const;
 
+  // Gets a list of accessible alerts that have built up since the last time
+  // they were taken. May return an empty vector. Removes those alerts from
+  // `accessible_alerts_`. If |web_app_id| is non-null, the results are limited
+  // to downloads initiated by the specified web app, otherwise the results are
+  // limited to downloads initiated by normal Chrome windows.
+  std::vector<std::u16string> TakeAccessibleAlertsForAnnouncement(
+      const webapps::AppId* web_app_id);
+
   // Notifies the appropriate browser windows that a download item was added.
   void NotifyWindowsOfDownloadItemAdded(download::DownloadItem* item);
 
+  // Sets up the download history observation.
+  void ObserveDownloadHistory();
+
   // Initializes AllDownloadItemNotifier for the current profile, and
   // initializes caches. This is called when the manager is ready, to signal
   // that the DownloadBubbleUpdateService should begin tracking downloads. This
@@ -155,6 +169,10 @@
                          update_delta) override;
   void OnContentProviderGoingDown() override;
 
+  // DownloadHistory::Observer:
+  void OnHistoryQueryComplete() override;
+  void OnDownloadHistoryDestroyed() override;
+
   OfflineItemModelManager* GetOfflineManager() const;
 
   bool IsProfileOtr() const;
@@ -203,6 +221,8 @@
     return *original_download_item_notifier_;
   }
 
+  bool download_history_loaded() const { return download_history_loaded_; }
+
   base::WeakPtr<DownloadBubbleUpdateService> GetWeakPtr() {
     return weak_factory_.GetWeakPtr();
   }
@@ -210,9 +230,9 @@
  private:
   // Encapsulates the caching functionality of DownloadBubbleUpdateService.
   // Holds two caches, one for DownloadItems and one for OfflineItems, and their
-  // associated indexes and aggregate info (AllDownloadUIModelsInfo). Represents
-  // one "namespace" of items for the download bubble, corresponding to either a
-  // single web app or all the regular (non-app) Chrome browsers.
+  // associated indexes and aggregate info (DownloadBubbleDisplayInfo).
+  // Represents one "namespace" of items for the download bubble, corresponding
+  // to either a single web app or all the regular (non-app) Chrome browsers.
   class CacheManager {
    public:
     template <typename Item>
@@ -262,17 +282,25 @@
     // same name.
     DownloadDisplay::ProgressInfo GetProgressInfo() const;
 
+    // See comments on the public DownloadBubbleUpdateService method of the
+    // same name.
+    std::vector<std::u16string> TakeAccessibleAlertsForAnnouncement();
+
     // Adds an item to the cache if it is recent enough and meets other criteria
     // for showing in the bubble. If adding an item makes the map size exceed
     // the maximum, this removes excess items from the end of the map. Returns
     // whether the item was stored as the last item in the map. If |item| was
     // already in the cache, this does nothing. |is_new| is whether the item is
     // a newly added item (as opposed to an updated one). May mark the item
-    // model as not-actioned-on if the item is new.
-    bool MaybeAddDownloadItemToCache(download::DownloadItem* item, bool is_new);
+    // model as not-actioned-on if the item is new. May add an accessible alert
+    // if `maybe_add_alert` is true.
+    bool MaybeAddDownloadItemToCache(download::DownloadItem* item,
+                                     bool is_new,
+                                     bool maybe_add_alert);
     bool MaybeAddOfflineItemToCache(
         const offline_items_collection::OfflineItem& item,
-        bool is_new);
+        bool is_new,
+        bool maybe_add_alert);
 
     // Updates an item by removing it and reinserting it in the cache. May
     // kick off backfilling of the cache.
@@ -404,6 +432,9 @@
     // Holds the latest info about all models, relevant to the display state of
     // the download toolbar icon.
     DownloadBubbleDisplayInfo display_info_;
+
+    // Holds the latest batch of accessible alerts since the last update.
+    DownloadBubbleAccessibleAlertsMap accessible_alerts_;
   };
 
  public:
@@ -434,6 +465,7 @@
   // returns nullptr).
   const CacheManager* GetExistingCacheForWebApp(
       const webapps::AppId& app_id) const;
+  CacheManager* GetExistingCacheForWebApp(const webapps::AppId& app_id);
 
   // Finds the appropriate CacheManager for a download item, creating one if it
   // doesn't exist.
@@ -499,6 +531,17 @@
   // A separate cache for each web app.
   std::map<webapps::AppId, CacheManager> web_app_caches_;
 
+  // Observes download history so we can keep track of when updates from history
+  // occur, to ignore them for the purposes of adding accessible alerts.
+  // Note: Incognito profiles do not have download histories. For incognito
+  // profiles, this observation corresponds to the original profile, because
+  // downloads for the original profile show up in the incognito window download
+  // bubble. For normal profiles, it is for the profile itself.
+  // Note: No such observer exists for OfflineItems.
+  bool download_history_loaded_ = false;
+  base::ScopedObservation<DownloadHistory, DownloadHistory::Observer>
+      download_history_observation_{this};
+
   // Observes the offline content provider.
   base::ScopedObservation<
       offline_items_collection::OfflineContentProvider,
diff --git a/chrome/browser/download/bubble/download_bubble_update_service_unittest.cc b/chrome/browser/download/bubble/download_bubble_update_service_unittest.cc
index 929cae1..2434eb0 100644
--- a/chrome/browser/download/bubble/download_bubble_update_service_unittest.cc
+++ b/chrome/browser/download/bubble/download_bubble_update_service_unittest.cc
@@ -10,9 +10,14 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/download/bubble/download_bubble_display_info.h"
 #include "chrome/browser/download/bubble/download_bubble_update_service_factory.h"
+#include "chrome/browser/download/chrome_download_manager_delegate.h"
+#include "chrome/browser/download/download_core_service.h"
+#include "chrome/browser/download/download_core_service_factory.h"
+#include "chrome/browser/download/download_core_service_impl.h"
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_item_web_app_data.h"
 #include "chrome/browser/download/download_ui_model.h"
+#include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
 #include "chrome/browser/profiles/profile_key.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -20,6 +25,7 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/mock_download_item.h"
+#include "components/keyed_service/core/service_access_type.h"
 #include "components/offline_items_collection/core/offline_item.h"
 #include "components/offline_items_collection/core/offline_item_state.h"
 #include "components/offline_items_collection/core/test_support/mock_offline_content_provider.h"
@@ -37,10 +43,14 @@
 using ::offline_items_collection::OfflineItem;
 using ::offline_items_collection::OfflineItemState;
 using ::testing::_;
+using ::testing::AllOf;
+using ::testing::IsEmpty;
 using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::ReturnRefOfCopy;
+using ::testing::UnorderedElementsAre;
 using ::testing::WithArg;
+using Alert = DownloadBubbleAccessibleAlertsMap::Alert;
 using DownloadState = download::DownloadItem::DownloadState;
 using DownloadUIModelPtrVector =
     std::vector<DownloadUIModel::DownloadUIModelPtr>;
@@ -58,6 +68,11 @@
   }
 }
 
+// Matcher that works with substrings of u16string.
+MATCHER_P(HasSubstr16, substring, "") {
+  return arg.find(substring) != std::u16string::npos;
+}
+
 void RemoveDownloadItemsObserver(
     std::vector<std::unique_ptr<NiceMockDownloadItem>>& download_items,
     download::DownloadItem::Observer& observer) {
@@ -66,6 +81,22 @@
   }
 }
 
+std::unique_ptr<KeyedService> SetUpDownloadCoreService(
+    content::DownloadManager* download_manager,
+    content::BrowserContext* context) {
+  Profile* profile = static_cast<Profile*>(context);
+  auto dcs = std::make_unique<DownloadCoreServiceImpl>(profile);
+  auto cdmd = std::make_unique<ChromeDownloadManagerDelegate>(profile);
+  cdmd->SetDownloadManager(download_manager);
+  dcs->SetDownloadManagerDelegateForTesting(std::move(cdmd));
+  auto download_history = std::make_unique<DownloadHistory>(
+      download_manager, std::make_unique<DownloadHistory::HistoryAdapter>(
+                            HistoryServiceFactory::GetInstance()->GetForProfile(
+                                profile, ServiceAccessType::EXPLICIT_ACCESS)));
+  dcs->SetDownloadHistoryForTesting(std::move(download_history));
+  return dcs;
+}
+
 class DownloadBubbleUpdateServiceTest : public testing::Test {
  public:
   DownloadBubbleUpdateServiceTest()
@@ -76,47 +107,63 @@
       const DownloadBubbleUpdateServiceTest&) = delete;
   ~DownloadBubbleUpdateServiceTest() override = default;
 
-  raw_ptr<NiceMock<content::MockDownloadManager>> SetUpDownloadManager(
-      Profile* profile) {
+  std::unique_ptr<NiceMock<content::MockDownloadManager>>
+  SetUpDownloadManager() {
     auto download_manager =
         std::make_unique<NiceMock<content::MockDownloadManager>>();
-    raw_ptr<NiceMock<content::MockDownloadManager>> manager =
-        download_manager.get();
-    EXPECT_CALL(*download_manager, GetBrowserContext())
-        .WillRepeatedly(Return(profile));
     EXPECT_CALL(*download_manager, RemoveObserver(_)).WillRepeatedly(Return());
     // Default case for when no download exists with the requested guid.
     EXPECT_CALL(*download_manager, GetDownloadByGuid(_))
         .WillRepeatedly(Return(nullptr));
-    profile->SetDownloadManagerForTesting(std::move(download_manager));
-    return manager;
+    return download_manager;
   }
 
-  // Pass a null |download_manager| to avoid registering the download manager.
-  std::unique_ptr<KeyedService> InitUpdateService(
-      NiceMock<content::MockDownloadManager>* download_manager,
-      DownloadBubbleUpdateService* update_service,
+  std::unique_ptr<KeyedService> CreateUpdateService(
+      DownloadBubbleUpdateService* preexisting_update_service,
       content::BrowserContext* context) {
     // Unregister the observer from the previous instance of the update service.
     // See note below in TearDown() for why this is necessary.
-    if (update_service) {
+    if (preexisting_update_service) {
       RemoveDownloadItemsObserver(
           download_items_,
-          update_service->download_item_notifier_for_testing());
+          preexisting_update_service->download_item_notifier_for_testing());
     }
     auto service = std::make_unique<DownloadBubbleUpdateService>(
         Profile::FromBrowserContext(context));
-    if (download_manager) {
-      service->Initialize(download_manager);
-    }
-    task_environment_.RunUntilIdle();
     return service;
   }
 
+  void InitializeUpdateService() {
+    update_service_->Initialize(download_manager_);
+    // Run the offline content provider initialization.
+    task_environment_.RunUntilIdle();
+  }
+
   void SetUp() override {
     ASSERT_TRUE(testing_profile_manager_.SetUp());
-    profile_ = testing_profile_manager_.CreateTestingProfile(kProfileName);
-    download_manager_ = SetUpDownloadManager(profile_);
+    // The order of these initialization steps is important. The DownloadManager
+    // must be created and set on the DownloadCoreService before setting up the
+    // history service and calling DownloadBubbleUpdateService::Initialize.
+    // Otherwise GetDownloadHistory will cause Initialize() to be called a
+    // second time, violating invariants assumed by the Update Service.
+    std::unique_ptr<NiceMock<content::MockDownloadManager>> download_manager =
+        SetUpDownloadManager();
+    download_manager_ = download_manager.get();
+    TestingProfile::TestingFactories testing_factories;
+    testing_factories.emplace_back(HistoryServiceFactory::GetInstance(),
+                                   HistoryServiceFactory::GetDefaultFactory());
+    testing_factories.emplace_back(
+        DownloadCoreServiceFactory::GetInstance(),
+        base::BindRepeating(&SetUpDownloadCoreService, download_manager_));
+    profile_ = testing_profile_manager_.CreateTestingProfile(
+        kProfileName, std::move(testing_factories));
+    profile_->SetDownloadManagerForTesting(std::move(download_manager));
+    EXPECT_CALL(*download_manager_, GetBrowserContext())
+        .WillRepeatedly(Return(profile_));
+
+    download_core_service_ =
+        DownloadCoreServiceFactory::GetInstance()->GetForBrowserContext(
+            profile_);
 
     offline_content_provider_ = std::make_unique<
         NiceMock<offline_items_collection::MockOfflineContentProvider>>();
@@ -128,9 +175,9 @@
             ->SetTestingFactoryAndUse(
                 profile_,
                 base::BindRepeating(
-                    &DownloadBubbleUpdateServiceTest::InitUpdateService,
-                    base::Unretained(this), download_manager_.get(),
-                    update_service_)));
+                    &DownloadBubbleUpdateServiceTest::CreateUpdateService,
+                    base::Unretained(this), update_service_)));
+    InitializeUpdateService();
   }
 
   void TearDown() override {
@@ -143,7 +190,13 @@
           download_items_,
           update_service_->download_item_notifier_for_testing());
     }
+    // This must be shut down before the DCS is destroyed.
+    download_core_service_->GetDownloadManagerDelegate()->Shutdown();
     offline_content_provider_.reset();
+    update_service_ = nullptr;
+    download_manager_ = nullptr;
+    download_core_service_ = nullptr;
+    profile_ = nullptr;
     testing_profile_manager_.DeleteTestingProfile(kProfileName);
   }
 
@@ -193,9 +246,16 @@
     } else {
       EXPECT_CALL(item, GetEndTime()).WillRepeatedly(Return(base::Time()));
     }
+    base::FilePath::StringType filename;
+#if BUILDFLAG(IS_WIN)
+    filename = base::UTF8ToWide(guid);
+#else
+    filename = guid;
+#endif
     EXPECT_CALL(item, GetTargetFilePath())
-        .WillRepeatedly(
-            ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
+        .WillRepeatedly(ReturnRefOfCopy(base::FilePath(filename)));
+    EXPECT_CALL(item, GetFileNameToReportUser())
+        .WillRepeatedly(Return(base::FilePath(filename)));
     int received_bytes =
         state == download::DownloadItem::IN_PROGRESS ? 50 : 100;
     EXPECT_CALL(item, GetReceivedBytes())
@@ -280,10 +340,19 @@
       OfflineItem item;
       item.state = states[i];
       item.id.id = ids[i];
+      item.title = ids[i];
       item.creation_time = start_times[i];
+      if (states[i] != OfflineItemState::COMPLETE) {
+        item.time_remaining_ms = 1000 * 60;
+        item.progress.value = 50;
+        item.progress.max = 100;
+        item.progress.unit =
+            offline_items_collection::OfflineItemProgressUnit::PERCENTAGE;
+      }
       new_items.push_back(item);
       offline_items_.push_back(std::move(item));
     }
+    offline_content_provider_->SetItems(new_items);
     offline_content_provider_->NotifyOnItemsAdded(new_items);
   }
 
@@ -308,6 +377,7 @@
       offline_content_provider_;
   raw_ptr<DownloadBubbleUpdateService, DanglingUntriaged> update_service_ =
       nullptr;
+  raw_ptr<DownloadCoreService> download_core_service_ = nullptr;
 };
 
 TEST_F(DownloadBubbleUpdateServiceTest, PopulatesCaches) {
@@ -327,12 +397,16 @@
   EXPECT_EQ(models[0]->GetContentId().id, "in_progress_active_download");
   EXPECT_EQ(models[1]->GetContentId().id, "in_progress_paused_download");
   EXPECT_EQ(models[2]->GetContentId().id, "completed_download");
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              UnorderedElementsAre(HasSubstr16(u"in_progress_active_download"),
+                                   HasSubstr16(u"in_progress_paused_download"),
+                                   HasSubstr16(u"completed_download")));
 
   // Recreate the update service to check that it pulls in the existing
   // download items upon initialization.
-  auto service =
-      InitUpdateService(download_manager_, update_service_, profile_);
+  auto service = CreateUpdateService(update_service_, profile_);
   update_service_ = static_cast<DownloadBubbleUpdateService*>(service.get());
+  InitializeUpdateService();
 
   EXPECT_TRUE(
       update_service_->GetAllModelsToDisplay(models, /*web_app_id=*/nullptr));
@@ -359,6 +433,13 @@
   EXPECT_EQ(models[3]->GetContentId().id, "in_progress_paused_offline_item");
   EXPECT_EQ(models[4]->GetContentId().id, "completed_download");
   EXPECT_EQ(models[5]->GetContentId().id, "completed_offline_item");
+  // Only have alerts for the new offline items, not the download items that
+  // were preexisting when this service instance was initialized.
+  EXPECT_THAT(
+      update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+      UnorderedElementsAre(HasSubstr16(u"in_progress_active_offline_item"),
+                           HasSubstr16(u"in_progress_paused_offline_item"),
+                           HasSubstr16(u"completed_offline_item")));
 
   // Manually clean up the second service instance to avoid UAF.
   RemoveDownloadItemsObserver(
@@ -373,12 +454,15 @@
                    /*observe=*/false);
   // Manually notify the service of the new download rather than going through
   // the observer update notification in InitDownloadItem().
-  update_service_->OnDownloadCreated(download_manager_, &GetDownloadItem(0));
+  update_service_->OnDownloadCreated(download_manager_.get(),
+                                     &GetDownloadItem(0));
   DownloadUIModelPtrVector models;
   EXPECT_TRUE(
       update_service_->GetAllModelsToDisplay(models, /*web_app_id=*/nullptr));
   ASSERT_EQ(models.size(), 1u);
   EXPECT_EQ(models[0]->GetContentId().id, "new_download");
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              UnorderedElementsAre(HasSubstr16(u"new_download")));
 }
 
 TEST_F(DownloadBubbleUpdateServiceTest, DelaysCrx) {
@@ -395,12 +479,16 @@
       update_service_->GetAllModelsToDisplay(models, /*web_app_id=*/nullptr));
   // The crx download does not show up immediately.
   EXPECT_EQ(models.size(), 0u);
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              IsEmpty());
 
   // Updates are also withheld.
   UpdateDownloadItem(0, DownloadState::IN_PROGRESS, /*is_paused=*/true);
   EXPECT_TRUE(
       update_service_->GetAllModelsToDisplay(models, /*web_app_id=*/nullptr));
   EXPECT_EQ(models.size(), 0u);
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              IsEmpty());
 
   task_environment_.FastForwardBy(base::Seconds(2));
 
@@ -409,6 +497,8 @@
       update_service_->GetAllModelsToDisplay(models, /*web_app_id=*/nullptr));
   ASSERT_EQ(models.size(), 1u);
   EXPECT_EQ(models[0]->GetContentId().id, "in_progress_crx");
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              UnorderedElementsAre(HasSubstr16(u"in_progress_crx")));
 }
 
 TEST_F(DownloadBubbleUpdateServiceTest, EvictsExcessItemsAndBackfills) {
@@ -442,6 +532,12 @@
   EXPECT_EQ(models[0]->GetContentId().id, "in_progress_active_download");
   EXPECT_EQ(models[1]->GetContentId().id, "in_progress_paused_download");
   EXPECT_EQ(models[2]->GetContentId().id, "completed_download_newer");
+  // Alerts for all the downloads are included.
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              UnorderedElementsAre(HasSubstr16(u"in_progress_active_download"),
+                                   HasSubstr16(u"in_progress_paused_download"),
+                                   HasSubstr16(u"completed_download_older"),
+                                   HasSubstr16(u"completed_download_newer")));
 
   // Remove an item. The previously evicted item should come back via backfill.
   RemoveDownloadItem(1);
@@ -453,6 +549,8 @@
   EXPECT_EQ(models[0]->GetContentId().id, "in_progress_active_download");
   EXPECT_EQ(models[1]->GetContentId().id, "completed_download_newer");
   EXPECT_EQ(models[2]->GetContentId().id, "completed_download_older");
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              IsEmpty());
 }
 
 TEST_F(DownloadBubbleUpdateServiceTest, BackfillsOnUpdate) {
@@ -479,6 +577,11 @@
   EXPECT_EQ(models[0]->GetContentId().id, "now");
   EXPECT_EQ(models[1]->GetContentId().id, "older");
   EXPECT_EQ(models[2]->GetContentId().id, "even_older");
+  // Alerts for all the downloads are included.
+  EXPECT_THAT(
+      update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+      UnorderedElementsAre(HasSubstr16(u"now"), HasSubstr16(u"older"),
+                           HasSubstr16(u"even_older"), HasSubstr16(u"oldest")));
 
   // Update the newest item to be paused, so it should sort behind all the other
   // items.
@@ -493,6 +596,9 @@
   EXPECT_EQ(models[0]->GetContentId().id, "older");
   EXPECT_EQ(models[1]->GetContentId().id, "even_older");
   EXPECT_EQ(models[2]->GetContentId().id, "oldest");
+  // Only the item that was updated has an alert.
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              UnorderedElementsAre(HasSubstr16(u"now")));
 }
 
 TEST_F(DownloadBubbleUpdateServiceTest, UpdatesOfflineItems) {
@@ -506,6 +612,9 @@
   ASSERT_EQ(models.size(), 1u);
   EXPECT_EQ(models[0]->GetContentId().id, "in_progress_active_offline_item");
   EXPECT_EQ(models[0]->GetState(), DownloadState::IN_PROGRESS);
+  EXPECT_THAT(
+      update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+      UnorderedElementsAre(HasSubstr16(u"in_progress_active_offline_item")));
 
   UpdateOfflineItem(0, OfflineItemState::COMPLETE);
   EXPECT_TRUE(
@@ -513,6 +622,10 @@
   ASSERT_EQ(models.size(), 1u);
   EXPECT_EQ(models[0]->GetContentId().id, "in_progress_active_offline_item");
   EXPECT_EQ(models[0]->GetState(), DownloadState::COMPLETE);
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              UnorderedElementsAre(
+                  AllOf(HasSubstr16(u"in_progress_active_offline_item"),
+                        HasSubstr16(u"complete"))));
 }
 
 TEST_F(DownloadBubbleUpdateServiceTest, RemovesOfflineItems) {
@@ -531,6 +644,11 @@
   EXPECT_EQ(models[0]->GetContentId().id, "in_progress_active_offline_item");
   EXPECT_EQ(models[1]->GetContentId().id, "in_progress_paused_offline_item");
   EXPECT_EQ(models[2]->GetContentId().id, "completed_offline_item");
+  EXPECT_THAT(
+      update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+      UnorderedElementsAre(HasSubstr16(u"in_progress_active_offline_item"),
+                           HasSubstr16(u"in_progress_paused_offline_item"),
+                           HasSubstr16(u"completed_offline_item")));
 
   offline_content_provider_->NotifyOnItemRemoved(models[0]->GetContentId());
   EXPECT_TRUE(
@@ -538,6 +656,9 @@
   ASSERT_EQ(models.size(), 2u);
   EXPECT_EQ(models[0]->GetContentId().id, "in_progress_paused_offline_item");
   EXPECT_EQ(models[1]->GetContentId().id, "completed_offline_item");
+  // No alert generated for removal.
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              IsEmpty());
 }
 
 TEST_F(DownloadBubbleUpdateServiceTest, DoesNotAddExpiredItems) {
@@ -548,6 +669,8 @@
   EXPECT_TRUE(
       update_service_->GetAllModelsToDisplay(models, /*web_app_id=*/nullptr));
   EXPECT_EQ(models.size(), 0u);
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              IsEmpty());
 }
 
 TEST_F(DownloadBubbleUpdateServiceTest, PrunesExpiredItems) {
@@ -570,6 +693,11 @@
   EXPECT_EQ(models[1]->GetContentId().id, "two_hours_ago_download");
   EXPECT_EQ(models[2]->GetContentId().id, "now_offline_item");
   EXPECT_EQ(models[3]->GetContentId().id, "two_hours_ago_offline_item");
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              UnorderedElementsAre(HasSubstr16(u"now_download"),
+                                   HasSubstr16(u"two_hours_ago_download"),
+                                   HasSubstr16(u"now_offline_item"),
+                                   HasSubstr16(u"two_hours_ago_offline_item")));
 
   // Fast forward so that the older items become too old.
   task_environment_.FastForwardBy(base::Hours(23));
@@ -580,6 +708,8 @@
   ASSERT_EQ(models.size(), 2u);
   EXPECT_EQ(models[0]->GetContentId().id, "now_download");
   EXPECT_EQ(models[1]->GetContentId().id, "now_offline_item");
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              IsEmpty());
 }
 
 TEST_F(DownloadBubbleUpdateServiceTest, DoesNotBackfillIfNotForced) {
@@ -721,14 +851,21 @@
   ASSERT_EQ(models.size(), 2u);
   EXPECT_EQ(models[0]->GetContentId().id, "non_app_download");
   EXPECT_EQ(models[1]->GetContentId().id, "offline_item");
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(nullptr),
+              UnorderedElementsAre(HasSubstr16(u"non_app_download"),
+                                   HasSubstr16(u"offline_item")));
 
   EXPECT_TRUE(update_service_->GetAllModelsToDisplay(models, &app_a_id));
   ASSERT_EQ(models.size(), 1u);
   EXPECT_EQ(models[0]->GetContentId().id, "app_a_download");
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(&app_a_id),
+              UnorderedElementsAre(HasSubstr16(u"app_a_download")));
 
   EXPECT_TRUE(update_service_->GetAllModelsToDisplay(models, &app_b_id));
   ASSERT_EQ(models.size(), 1u);
   EXPECT_EQ(models[0]->GetContentId().id, "app_b_download");
+  EXPECT_THAT(update_service_->TakeAccessibleAlertsForAnnouncement(&app_b_id),
+              UnorderedElementsAre(HasSubstr16(u"app_b_download")));
 }
 
 TEST_F(DownloadBubbleUpdateServiceTest, GetProgressInfo) {
@@ -893,7 +1030,7 @@
   EXPECT_FALSE(info.has_deep_scanning);
 }
 
-TEST_F(DownloadBubbleUpdateServiceTest, GetAllUIModelsInfoForWebApp) {
+TEST_F(DownloadBubbleUpdateServiceTest, GetDisplayInfoForWebApp) {
   base::Time now = base::Time::Now();
   base::Time two_hours_ago = now - base::Hours(2);
   webapps::AppId app_a_id = "app_a";
@@ -970,21 +1107,34 @@
 
   void SetUp() override {
     DownloadBubbleUpdateServiceTest::SetUp();
-
-    incognito_profile_ = profile_->GetOffTheRecordProfile(
-        Profile::OTRProfileID::CreateUniqueForTesting(),
-        /*create_if_needed=*/true);
-    incognito_download_manager_ = SetUpDownloadManager(incognito_profile_);
-    // Pass nullptr for the download_manager to delay RegisterDownloadManager()
-    // call for the test.
+    TestingProfile::Builder builder;
+    TestingProfile::TestingFactories testing_factories;
+    testing_factories.emplace_back(HistoryServiceFactory::GetInstance(),
+                                   HistoryServiceFactory::GetDefaultFactory());
+    testing_factories.emplace_back(
+        DownloadCoreServiceFactory::GetInstance(),
+        base::BindRepeating(&SetUpDownloadCoreService, download_manager_));
+    builder.AddTestingFactories(std::move(testing_factories));
+    incognito_profile_ = builder.BuildIncognito(profile_);
+    std::unique_ptr<NiceMock<content::MockDownloadManager>>
+        incognito_download_manager = SetUpDownloadManager();
+    incognito_download_manager_ = incognito_download_manager.get();
+    EXPECT_CALL(*incognito_download_manager_, GetBrowserContext())
+        .WillRepeatedly(Return(incognito_profile_));
+    incognito_profile_->SetDownloadManagerForTesting(
+        std::move(incognito_download_manager));
     incognito_update_service_ = static_cast<DownloadBubbleUpdateService*>(
         DownloadBubbleUpdateServiceFactory::GetInstance()
             ->SetTestingFactoryAndUse(
                 incognito_profile_,
                 base::BindRepeating(
-                    &DownloadBubbleUpdateServiceTest::InitUpdateService,
-                    base::Unretained(this), /*download_manager=*/nullptr,
-                    incognito_update_service_)));
+                    &DownloadBubbleUpdateServiceTest::CreateUpdateService,
+                    base::Unretained(this), incognito_update_service_)));
+    incognito_download_core_service_ =
+        DownloadCoreServiceFactory::GetInstance()->GetForBrowserContext(
+            incognito_profile_);
+    // Do not call Initialize here to delay registering the download manager for
+    // the test.
   }
 
   void TearDown() override {
@@ -994,6 +1144,11 @@
     RemoveDownloadItemsObserver(
         download_items_, incognito_update_service_
                              ->original_download_item_notifier_for_testing());
+    incognito_download_core_service_->GetDownloadManagerDelegate()->Shutdown();
+    incognito_update_service_ = nullptr;
+    incognito_download_manager_ = nullptr;
+    incognito_download_core_service_ = nullptr;
+    incognito_profile_ = nullptr;
     DownloadBubbleUpdateServiceTest::TearDown();
   }
 
@@ -1004,6 +1159,7 @@
   std::vector<std::unique_ptr<NiceMockDownloadItem>> incognito_download_items_;
   raw_ptr<DownloadBubbleUpdateService, DanglingUntriaged>
       incognito_update_service_ = nullptr;
+  raw_ptr<DownloadCoreService> incognito_download_core_service_ = nullptr;
 };
 
 // Tests that initializing an update service for an incognito profile sets both
@@ -1029,6 +1185,8 @@
   EXPECT_EQ(incognito_update_service_->GetDownloadManager(), nullptr);
 
   incognito_update_service_->Initialize(incognito_download_manager_);
+  // Run the offline content provider initialization.
+  task_environment_.RunUntilIdle();
   // Regular profile's update service's manager hasn't changed.
   EXPECT_EQ(update_service_->GetDownloadManager(), download_manager_);
   // Incognito profile's update service's manager now set correctly.
@@ -1107,6 +1265,8 @@
                    /*is_crx=*/false,
                    /*observe=*/false);
   incognito_update_service_->Initialize(incognito_download_manager_);
+  // Run the offline content provider initialization.
+  task_environment_.RunUntilIdle();
 
   DownloadUIModelPtrVector models;
   EXPECT_TRUE(incognito_update_service_->GetAllModelsToDisplay(
diff --git a/chrome/browser/download/bubble/download_bubble_utils.cc b/chrome/browser/download/bubble/download_bubble_utils.cc
index 575517a..c32a0cb1a 100644
--- a/chrome/browser/download/bubble/download_bubble_utils.cc
+++ b/chrome/browser/download/bubble/download_bubble_utils.cc
@@ -4,18 +4,24 @@
 
 #include "chrome/browser/download/bubble/download_bubble_utils.h"
 
+#include "base/containers/fixed_flat_map.h"
+#include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/download/download_ui_model.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/download/download_item_mode.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/download/public/common/download_danger_type.h"
 #include "components/download/public/common/download_item.h"
 #include "components/offline_items_collection/core/offline_item.h"
 #include "components/offline_items_collection/core/offline_item_state.h"
 #include "content/public/browser/download_item_utils.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "ui/base/l10n/l10n_util.h"
 
 base::Time GetItemStartTime(const download::DownloadItem* item) {
   return item->GetStartTime();
@@ -116,6 +122,66 @@
   return item.state == offline_items_collection::OfflineItemState::PAUSED;
 }
 
+DownloadBubbleAccessibleAlertsMap::Alert GetAccessibleAlertForModel(
+    const DownloadUIModel& model) {
+  using State = download::DownloadItem::DownloadState;
+  using Alert = DownloadBubbleAccessibleAlertsMap::Alert;
+  const download::DownloadItemMode mode =
+      download::GetDesiredDownloadItemMode(&model);
+  const State state = model.GetState();
+  const std::u16string filename =
+      model.GetFileNameToReportUser().LossyDisplayName();
+
+  switch (mode) {
+    case download::DownloadItemMode::kNormal: {
+      if (state == State::IN_PROGRESS && !model.IsPaused()) {
+        return Alert{
+            Alert::Urgency::kAlertWhenAppropriate,
+            model.GetInProgressAccessibleAlertText(),
+        };
+      }
+      static constexpr auto kMap = base::MakeFixedFlatMap<State, int>({
+          {State::INTERRUPTED, IDS_DOWNLOAD_FAILED_ACCESSIBLE_ALERT},
+          {State::COMPLETE, IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT},
+          {State::CANCELLED, IDS_DOWNLOAD_CANCELLED_ACCESSIBLE_ALERT},
+          // If state is IN_PROGRESS but we got here to the map lookup, the
+          // download is paused.
+          {State::IN_PROGRESS, IDS_DOWNLOAD_PAUSED_ACCESSIBLE_ALERT},
+      });
+      if (const auto* it = kMap.find(state); it != kMap.end()) {
+        return Alert{Alert::Urgency::kAlertSoon,
+                     l10n_util::GetStringFUTF16(it->second, filename)};
+      }
+      break;
+    }
+    case download::DownloadItemMode::kDangerous:
+    case download::DownloadItemMode::kMalicious: {
+      if (model.GetDangerType() ==
+          download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING) {
+        return Alert{Alert::Urgency::kAlertSoon,
+                     l10n_util::GetStringFUTF16(
+                         IDS_PROMPT_DEEP_SCANNING_ACCESSIBLE_ALERT, filename)};
+      }
+      size_t ignored;
+      return Alert{Alert::Urgency::kAlertSoon,
+                   model.GetWarningText(filename, &ignored)};
+    }
+    case download::DownloadItemMode::kInsecureDownloadWarn:
+    case download::DownloadItemMode::kInsecureDownloadBlock:
+      return Alert{
+          Alert::Urgency::kAlertSoon,
+          l10n_util::GetStringFUTF16(
+              IDS_PROMPT_DOWNLOAD_INSECURE_BLOCKED_ACCESSIBLE_ALERT, filename)};
+    case download::DownloadItemMode::kDeepScanning:
+      return Alert{Alert::Urgency::kAlertWhenAppropriate,
+                   l10n_util::GetStringFUTF16(
+                       IDS_DEEP_SCANNING_ACCESSIBLE_ALERT, filename)};
+  }
+
+  // An empty alert will not be added.
+  return Alert{Alert::Urgency::kAlertWhenAppropriate, u""};
+}
+
 Browser* FindBrowserToShowAnimation(download::DownloadItem* item,
                                     Profile* profile) {
   content::WebContents* web_contents =
diff --git a/chrome/browser/download/bubble/download_bubble_utils.h b/chrome/browser/download/bubble/download_bubble_utils.h
index ff68d466..6d1f4b31 100644
--- a/chrome/browser/download/bubble/download_bubble_utils.h
+++ b/chrome/browser/download/bubble/download_bubble_utils.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_UTILS_H_
 
 #include "base/time/time.h"
+#include "chrome/browser/download/bubble/download_bubble_accessible_alerts_map.h"
 #include "chrome/browser/download/download_ui_model.h"
 #include "components/offline_items_collection/core/offline_item.h"
 #include "components/webapps/common/web_app_id.h"
@@ -39,6 +40,11 @@
 bool IsItemPaused(const download::DownloadItem* item);
 bool IsItemPaused(const offline_items_collection::OfflineItem& item);
 
+// Gets the appropriate accessible alert based on the status of the download.
+// This should be called upon receiving an update for a download.
+DownloadBubbleAccessibleAlertsMap::Alert GetAccessibleAlertForModel(
+    const DownloadUIModel& model);
+
 // Finds the browser most appropriate to show the "download started" animation
 // in.
 Browser* FindBrowserToShowAnimation(download::DownloadItem* item,
diff --git a/chrome/browser/download/bubble/download_bubble_utils_unittest.cc b/chrome/browser/download/bubble/download_bubble_utils_unittest.cc
new file mode 100644
index 0000000..6e8b2edd
--- /dev/null
+++ b/chrome/browser/download/bubble/download_bubble_utils_unittest.cc
@@ -0,0 +1,148 @@
+// 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/download/bubble/download_bubble_utils.h"
+
+#include <string>
+
+#include "chrome/browser/download/bubble/download_bubble_accessible_alerts_map.h"
+#include "chrome/browser/download/download_item_model.h"
+#include "chrome/browser/download/download_ui_model.h"
+#include "components/download/public/common/download_danger_type.h"
+#include "components/download/public/common/download_interrupt_reasons.h"
+#include "components/download/public/common/mock_download_item.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::ReturnRefOfCopy;
+using Alert = DownloadBubbleAccessibleAlertsMap::Alert;
+using State = download::DownloadItem::DownloadState;
+
+std::unique_ptr<NiceMock<download::MockDownloadItem>> InitDownloadItem(
+    download::DownloadItem::DownloadState state,
+    download::DownloadDangerType danger_type =
+        download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
+  auto item = std::make_unique<NiceMock<download::MockDownloadItem>>();
+  EXPECT_CALL(*item, GetGuid())
+      .WillRepeatedly(ReturnRefOfCopy(std::string("1")));
+  EXPECT_CALL(*item, GetFileNameToReportUser())
+      .WillRepeatedly(
+          Return(base::FilePath(FILE_PATH_LITERAL("download.pdf"))));
+  EXPECT_CALL(*item, GetState()).WillRepeatedly(Return(state));
+  EXPECT_CALL(*item, IsDangerous())
+      .WillRepeatedly(
+          Return(danger_type != download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
+  EXPECT_CALL(*item, GetDangerType()).WillRepeatedly(Return(danger_type));
+  EXPECT_CALL(*item, PercentComplete()).WillRepeatedly(Return(50));
+
+  return item;
+}
+
+MATCHER_P2(MatchesAlert, alert_substring, urgency, "") {
+  return arg.text.find(alert_substring) != std::u16string::npos &&
+         arg.urgency == urgency;
+}
+
+MATCHER(AlertIsEmpty, "") {
+  return arg.text.empty();
+}
+
+TEST(DownloadBubbleUtilsTest, GetAccessibleAlertForModel_InProgressNormal) {
+  auto item = InitDownloadItem(State::IN_PROGRESS);
+  DownloadItemModel model{
+      item.get(), std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()};
+
+  Alert alert = GetAccessibleAlertForModel(model);
+  EXPECT_THAT(alert,
+              MatchesAlert(u"50%", Alert::Urgency::kAlertWhenAppropriate));
+}
+
+TEST(DownloadBubbleUtilsTest, GetAccessibleAlertForModel_InProgressPaused) {
+  auto item = InitDownloadItem(State::IN_PROGRESS);
+  EXPECT_CALL(*item, IsPaused()).WillRepeatedly(Return(true));
+  DownloadItemModel model{
+      item.get(), std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()};
+
+  Alert alert = GetAccessibleAlertForModel(model);
+  EXPECT_THAT(alert, MatchesAlert(u"paused", Alert::Urgency::kAlertSoon));
+}
+
+TEST(DownloadBubbleUtilsTest, GetAccessibleAlertForModel_Interrupted) {
+  auto item = InitDownloadItem(State::INTERRUPTED);
+  EXPECT_CALL(*item, GetLastReason())
+      .WillRepeatedly(Return(download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED));
+  DownloadItemModel model{
+      item.get(), std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()};
+
+  Alert alert = GetAccessibleAlertForModel(model);
+  EXPECT_THAT(alert, MatchesAlert(u"unsuccessful", Alert::Urgency::kAlertSoon));
+}
+
+TEST(DownloadBubbleUtilsTest, GetAccessibleAlertForModel_Complete) {
+  auto item = InitDownloadItem(State::COMPLETE);
+  DownloadItemModel model{
+      item.get(), std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()};
+
+  Alert alert = GetAccessibleAlertForModel(model);
+  EXPECT_THAT(alert, MatchesAlert(u"complete", Alert::Urgency::kAlertSoon));
+}
+
+TEST(DownloadBubbleUtilsTest, GetAccessibleAlertForModel_Cancelled) {
+  auto item = InitDownloadItem(State::CANCELLED);
+  DownloadItemModel model{
+      item.get(), std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()};
+
+  Alert alert = GetAccessibleAlertForModel(model);
+  EXPECT_THAT(alert, MatchesAlert(u"cancelled", Alert::Urgency::kAlertSoon));
+}
+
+TEST(DownloadBubbleUtilsTest, GetAccessibleAlertForModel_Dangerous) {
+  auto item = InitDownloadItem(
+      State::IN_PROGRESS, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT);
+  DownloadItemModel model{
+      item.get(), std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()};
+
+  Alert alert = GetAccessibleAlertForModel(model);
+  EXPECT_THAT(alert, MatchesAlert(u"dangerous", Alert::Urgency::kAlertSoon));
+}
+
+TEST(DownloadBubbleUtilsTest, GetAccessibleAlertForModel_PromptForScanning) {
+  auto item = InitDownloadItem(
+      State::IN_PROGRESS, download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING);
+  DownloadItemModel model{
+      item.get(), std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()};
+
+  Alert alert = GetAccessibleAlertForModel(model);
+  EXPECT_THAT(alert, MatchesAlert(u"scanning", Alert::Urgency::kAlertSoon));
+}
+
+TEST(DownloadBubbleUtilsTest, GetAccessibleAlertForModel_DeepScanning) {
+  auto item = InitDownloadItem(State::IN_PROGRESS,
+                               download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING);
+  EXPECT_CALL(*item, IsDangerous()).WillRepeatedly(Return(false));
+  DownloadItemModel model{
+      item.get(), std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()};
+
+  Alert alert = GetAccessibleAlertForModel(model);
+  EXPECT_THAT(alert, MatchesAlert(u"being scanned",
+                                  Alert::Urgency::kAlertWhenAppropriate));
+}
+
+TEST(DownloadBubbleUtilsTest, GetAccessibleAlertForModel_Insecure) {
+  auto item = InitDownloadItem(State::IN_PROGRESS);
+  EXPECT_CALL(*item, IsInsecure()).WillRepeatedly(Return(true));
+  DownloadItemModel model{
+      item.get(), std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()};
+
+  Alert alert = GetAccessibleAlertForModel(model);
+  EXPECT_THAT(alert, MatchesAlert(u"can't be downloaded securely",
+                                  Alert::Urgency::kAlertSoon));
+}
+
+}  // namespace
diff --git a/chrome/browser/engagement/important_sites_util.cc b/chrome/browser/engagement/important_sites_util.cc
index 7a0d3948..9d21aaf 100644
--- a/chrome/browser/engagement/important_sites_util.cc
+++ b/chrome/browser/engagement/important_sites_util.cc
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <string_view>
 #include <unordered_set>
 #include <utility>
 
@@ -363,7 +364,7 @@
 }
 
 std::string ImportantSitesUtil::GetRegisterableDomainOrIPFromHost(
-    base::StringPiece host) {
+    std::string_view host) {
   std::string registerable_domain =
       net::registry_controlled_domains::GetDomainAndRegistry(
           host, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
diff --git a/chrome/browser/engagement/important_sites_util.h b/chrome/browser/engagement/important_sites_util.h
index 4cd34fd..0a44047 100644
--- a/chrome/browser/engagement/important_sites_util.h
+++ b/chrome/browser/engagement/important_sites_util.h
@@ -8,6 +8,7 @@
 #include <optional>
 #include <set>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "build/build_config.h"
@@ -63,7 +64,7 @@
 
   static std::string GetRegisterableDomainOrIP(const GURL& url);
 
-  static std::string GetRegisterableDomainOrIPFromHost(base::StringPiece host);
+  static std::string GetRegisterableDomainOrIPFromHost(std::string_view host);
 
   static bool IsDialogDisabled(Profile* profile);
 
diff --git a/chrome/browser/extensions/api/commands/command_service.cc b/chrome/browser/extensions/api/commands/command_service.cc
index dbf840b..713b2c7 100644
--- a/chrome/browser/extensions/api/commands/command_service.cc
+++ b/chrome/browser/extensions/api/commands/command_service.cc
@@ -5,12 +5,12 @@
 #include "chrome/browser/extensions/api/commands/command_service.h"
 
 #include <memory>
+#include <string_view>
 #include <utility>
 #include <vector>
 
 #include "base/lazy_instance.h"
 #include "base/observer_list.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -303,7 +303,7 @@
       continue;
     std::optional<bool> global = it.second.GetDict().FindBool(kGlobal);
 
-    std::vector<base::StringPiece> tokens = base::SplitStringPiece(
+    std::vector<std::string_view> tokens = base::SplitStringPiece(
         shortcut, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
     CHECK(tokens.size() >= 2);
 
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
index 876661094..7af93f6 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
@@ -15,7 +16,6 @@
 #include "base/containers/flat_set.h"
 #include "base/feature_list.h"
 #include "base/ranges/algorithm.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -136,7 +136,7 @@
   std::vector<std::string> ime_list;
   std::string preferred_languages =
       prefs->GetString(language::prefs::kPreferredLanguages);
-  std::vector<base::StringPiece> enabled_languages =
+  std::vector<std::string_view> enabled_languages =
       base::SplitStringPiece(preferred_languages, ",", base::TRIM_WHITESPACE,
                              base::SPLIT_WANT_NONEMPTY);
 
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.cc b/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.cc
index 3658b23..ac017b36 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.cc
+++ b/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.h"
 
 #include <memory>
+#include <string_view>
 #include <utility>
 
 #include "base/functional/bind.h"
@@ -206,7 +207,7 @@
   g_native_messaging_host_timeout_override = nullptr;
 }
 
-bool IsValidConnectionId(const base::StringPiece connection_id) {
+bool IsValidConnectionId(const std::string_view connection_id) {
   return connection_id.size() <= 20 &&
          base::ContainsOnlyChars(
              connection_id,
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
index 4db3a8c..41ed7af 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
@@ -8,10 +8,10 @@
 
 #include <optional>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
-#include "base/strings/string_piece.h"
 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
@@ -266,7 +266,7 @@
     }
 
     api_pk::Match result_match;
-    base::StringPiece der_encoded_cert =
+    std::string_view der_encoded_cert =
         net::x509_util::CryptoBufferAsStringPiece(match->cert_buffer());
     result_match.certificate.assign(der_encoded_cert.begin(),
                                     der_encoded_cert.end());
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc
index 26eea5f1..8165e17 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc
@@ -6,11 +6,11 @@
 
 #include <array>
 #include <string>
+#include <string_view>
 
 #include "base/functional/bind.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
-#include "base/strings/string_piece.h"
 #include "chrome/browser/ash/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h"
 #include "chrome/browser/extensions/mixin_based_extension_apitest.h"
@@ -114,7 +114,7 @@
   if (enrollment_status() == EnrollmentStatus::ENROLLED) {
     ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetDeviceAffiliationIDs(
         &device_policy_test_helper_,
-        std::array{base::StringPiece(kAffiliationID)}));
+        std::array{std::string_view(kAffiliationID)}));
     device_policy_test_helper_.InstallOwnerKey();
     install_attributes_.Get()->SetCloudManaged(
         policy::PolicyBuilder::kFakeDomain,
@@ -125,7 +125,7 @@
     policy::UserPolicyBuilder user_policy;
     ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetUserAffiliationIDs(
         &user_policy, account_id_,
-        std::array{base::StringPiece(kAffiliationID)}));
+        std::array{std::string_view(kAffiliationID)}));
   }
 
   mock_policy_provider_.SetDefaultReturns(
diff --git a/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc b/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc
index 380bf28..6a59fe1 100644
--- a/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc
+++ b/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <memory>
 #include <optional>
+#include <string_view>
 #include <utility>
 
 #include "base/containers/contains.h"
@@ -167,14 +168,14 @@
     return;
   }
 
-  std::vector<base::StringPiece> der_cert_chain;
+  std::vector<std::string_view> der_cert_chain;
   for (const std::vector<uint8_t>& cert_der :
        details.server_certificate_chain) {
     if (cert_der.empty()) {
       std::move(callback).Run(platform_keys::kErrorInvalidX509Cert, 0, 0);
       return;
     }
-    der_cert_chain.push_back(base::StringPiece(
+    der_cert_chain.push_back(std::string_view(
         reinterpret_cast<const char*>(cert_der.data()), cert_der.size()));
   }
   scoped_refptr<net::X509Certificate> cert_chain(
diff --git a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc
index be1c228b..4a96ced 100644
--- a/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc
@@ -8,6 +8,7 @@
 
 #include <memory>
 #include <optional>
+#include <string_view>
 #include <utility>
 
 #include "ash/constants/ash_features.h"
@@ -20,7 +21,6 @@
 #include "base/no_destructor.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
-#include "base/strings/string_piece.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -71,7 +71,7 @@
 // The type of the test. Either based on Prefs or Cryptohome
 enum class TestType { kPrefs, kCryptohome };
 
-const base::StringPiece TestTypeStr(TestType type) {
+const std::string_view TestTypeStr(TestType type) {
   switch (type) {
     case TestType::kPrefs:
       return "PrefBased";
diff --git a/chrome/browser/extensions/api/side_panel/side_panel_api.cc b/chrome/browser/extensions/api/side_panel/side_panel_api.cc
index ddf09a7c..1184b50 100644
--- a/chrome/browser/extensions/api/side_panel/side_panel_api.cc
+++ b/chrome/browser/extensions/api/side_panel/side_panel_api.cc
@@ -101,12 +101,12 @@
   base::expected<bool, std::string> open_panel_result;
   if (params->options.tab_id) {
     open_panel_result = service->OpenSidePanelForTab(
-        *extension(), *params->options.tab_id, params->options.window_id,
-        include_incognito_information());
+        *extension(), browser_context(), *params->options.tab_id,
+        params->options.window_id, include_incognito_information());
   } else {
     CHECK(params->options.window_id);
     open_panel_result = service->OpenSidePanelForWindow(
-        *extension(), *params->options.window_id,
+        *extension(), browser_context(), *params->options.window_id,
         include_incognito_information());
   }
 
diff --git a/chrome/browser/extensions/api/side_panel/side_panel_service.cc b/chrome/browser/extensions/api/side_panel/side_panel_service.cc
index 317f4e1..8a0773e 100644
--- a/chrome/browser/extensions/api/side_panel/side_panel_service.cc
+++ b/chrome/browser/extensions/api/side_panel/side_panel_service.cc
@@ -201,6 +201,8 @@
 bool SidePanelService::OpenSidePanelOnIconClick(
     const ExtensionId& extension_id) {
   bool open_side_panel_on_icon_click = false;
+  // TODO(tjudkins): This should be taking in a browser context to read the pref
+  // on, rather than using the one the service was created with.
   ExtensionPrefs::Get(browser_context_)
       ->ReadPrefAsBoolean(extension_id, kOpenSidePanelOnIconClickPref,
                           &open_side_panel_on_icon_click);
@@ -210,6 +212,8 @@
 void SidePanelService::SetOpenSidePanelOnIconClick(
     const ExtensionId& extension_id,
     bool open_side_panel_on_icon_click) {
+  // TODO(tjudkins): This should be taking in a browser context to set the pref
+  // on, rather than using the one the service was created with.
   ExtensionPrefs::Get(browser_context_)
       ->SetBooleanPref(extension_id, kOpenSidePanelOnIconClickPref,
                        open_side_panel_on_icon_click);
@@ -217,11 +221,12 @@
 
 base::expected<bool, std::string> SidePanelService::OpenSidePanelForWindow(
     const Extension& extension,
+    content::BrowserContext* context,
     int window_id,
     bool include_incognito_information) {
   std::string error;
   Browser* browser = ExtensionTabUtil::GetBrowserInProfileWithId(
-      Profile::FromBrowserContext(browser_context_), window_id,
+      Profile::FromBrowserContext(context), window_id,
       include_incognito_information, &error);
   if (!browser) {
     return base::unexpected(error);
@@ -241,13 +246,14 @@
 
 base::expected<bool, std::string> SidePanelService::OpenSidePanelForTab(
     const Extension& extension,
+    content::BrowserContext* context,
     int tab_id,
     std::optional<int> window_id,
     bool include_incognito_information) {
   // First, find the corresponding tab.
   Browser* browser = nullptr;
   content::WebContents* web_contents = nullptr;
-  if (!ExtensionTabUtil::GetTabById(tab_id, browser_context_,
+  if (!ExtensionTabUtil::GetTabById(tab_id, context,
                                     include_incognito_information, &browser,
                                     nullptr, &web_contents, nullptr)) {
     return base::unexpected(
diff --git a/chrome/browser/extensions/api/side_panel/side_panel_service.h b/chrome/browser/extensions/api/side_panel/side_panel_service.h
index 76177c1..2a8ae9d7 100644
--- a/chrome/browser/extensions/api/side_panel/side_panel_service.h
+++ b/chrome/browser/extensions/api/side_panel/side_panel_service.h
@@ -84,32 +84,35 @@
   void SetOpenSidePanelOnIconClick(const ExtensionId& extension_id,
                                    bool open_side_panel_on_icon_click);
 
-  // Opens the `extension`'s side panel for the specified `tab_id`.
-  // Handles properly determining if the side panel to be opened is a global or
-  // contextual panel. `include_incognito_information` indicates whether the
-  // registry should allow crossing incognito contexts when looking up `tab_id`.
-  // If `window_id` is specified, checks that the given `tab_id` belongs to the
-  // `window_id`. Returns true on success; returns an error string on failure.
+  // Opens the `extension`'s side panel for the specified `tab_id` and profile
+  // specified by `context`. Handles properly determining if the side panel to
+  // be opened is a global or contextual panel. `include_incognito_information`
+  // indicates whether the registry should allow crossing incognito contexts
+  // when looking up `tab_id`. If `window_id` is specified, checks that the
+  // given `tab_id` belongs to the `window_id`. Returns true on success; returns
+  // an error string on failure.
   // TODO(https://crbug.com/1446022): Return an enum here to indicate if the
   // panel was newly-opened vs already-opened in order to support waiting for
   // the panel to open?
   base::expected<bool, std::string> OpenSidePanelForTab(
       const Extension& extension,
+      content::BrowserContext* context,
       int tab_id,
       std::optional<int> window_id,
       bool include_incognito_information);
 
-  // Opens the `extension`'s side panel for the specified `window_id`. This is
-  // only valid if the extension has a registered global side panel. This will
-  // not override any contextual panels in the window.
-  // `include_incognito_information` indicates whether the registry should
-  // allow crossing incognito contexts when looking up `tab_id`.  Returns true
-  // on success; returns an error string on failure.
+  // Opens the `extension`'s side panel for the specified `window_id` and
+  // profile specified by `context`. This is only valid if the extension has a
+  // registered global side panel. This will not override any contextual panels
+  // in the window. `include_incognito_information` indicates whether the
+  // registry should allow crossing incognito contexts when looking up `tab_id`.
+  // Returns true on success; returns an error string on failure.
   // TODO(https://crbug.com/1446022): Return an enum here to indicate if the
   // panel was newly-opened vs already-opened in order to support waiting for
   // the panel to open?
   base::expected<bool, std::string> OpenSidePanelForWindow(
       const Extension& extension,
+      content::BrowserContext* context,
       int window_id,
       bool include_incognito_information);
 
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
index bbbd2524..22229461 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include <cmath>
+#include <string_view>
 
 #include "base/base64.h"
 #include "base/base_switches.h"
@@ -14,7 +15,6 @@
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/path_service.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
@@ -41,7 +41,7 @@
 #include "ui/gl/gl_switches.h"
 
 namespace {
-constexpr base::StringPiece kFullPerformanceRunSwitch = "full-performance-run";
+constexpr std::string_view kFullPerformanceRunSwitch = "full-performance-run";
 }  // namespace
 
 TabCapturePerformanceTestBase::TabCapturePerformanceTestBase() = default;
@@ -153,7 +153,7 @@
 TabCapturePerformanceTestBase::TraceAnalyzerUniquePtr
 TabCapturePerformanceTestBase::TraceAndObserve(
     const std::string& category_patterns,
-    const std::vector<base::StringPiece>& event_names,
+    const std::vector<std::string_view>& event_names,
     int required_event_count) {
   const base::TimeDelta observation_period = is_full_performance_run_
                                                  ? kFullRunObservationPeriod
@@ -240,7 +240,7 @@
 // static
 void TabCapturePerformanceTestBase::QueryTraceEvents(
     trace_analyzer::TraceAnalyzer* analyzer,
-    base::StringPiece event_name,
+    std::string_view event_name,
     trace_analyzer::TraceEventVector* events) {
   const trace_analyzer::Query kQuery =
       trace_analyzer::Query::EventNameIs(std::string(event_name)) &&
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h
index c7a0ad19..e50ffd1 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h
@@ -8,10 +8,10 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
-#include "base/strings/string_piece.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/trace_event_analyzer.h"
@@ -88,7 +88,7 @@
   using TraceAnalyzerUniquePtr = std::unique_ptr<trace_analyzer::TraceAnalyzer>;
   TraceAnalyzerUniquePtr TraceAndObserve(
       const std::string& category_patterns,
-      const std::vector<base::StringPiece>& event_names,
+      const std::vector<std::string_view>& event_names,
       int required_event_count);
 
   // Returns the path ".../test/data/extensions/api_test/".
@@ -104,7 +104,7 @@
   // Queries the |analyzer| for events having the given |event_name| whose phase
   // is classified as BEGIN, INSTANT, or COMPLETE (i.e., omit END events).
   static void QueryTraceEvents(trace_analyzer::TraceAnalyzer* analyzer,
-                               base::StringPiece event_name,
+                               std::string_view event_name,
                                trace_analyzer::TraceEventVector* events);
 
  protected:
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
index e324c999..a03fda3 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <cmath>
+#include <string_view>
 
 #include "base/command_line.h"
 #include "base/containers/flat_map.h"
@@ -312,8 +313,8 @@
   // Observe the running browser for a while, collecting a trace.
   std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer = TraceAndObserve(
       "gpu,gpu.capture",
-      std::vector<base::StringPiece>{kEventCommitAndDrawCompositorFrame,
-                                     kEventCapture},
+      std::vector<std::string_view>{kEventCommitAndDrawCompositorFrame,
+                                    kEventCapture},
       // In a full performance run, events will be trimmed from both ends of
       // trace. Otherwise, just require the bare-minimum to verify the stats
       // calculations will work.
diff --git a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc b/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
index fd03b01..f1459df 100644
--- a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
+++ b/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
@@ -52,6 +52,9 @@
 #if BUILDFLAG(IS_CHROMEOS)
     extension_misc::kContactCenterInsightsExtensionId,
     extension_misc::kDeskApiExtensionId,
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+    extension_misc::kQuickOfficeComponentExtensionId,
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 #endif
   };
 
@@ -103,9 +106,6 @@
     case IDR_CONTACT_CENTER_INSIGHTS_MANIFEST:
     case IDR_DESK_API_MANIFEST:
     case IDR_ECHO_MANIFEST:
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-    case IDR_QUICKOFFICE_MANIFEST:
-#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 #endif  // BUILDFLAG(IS_CHROMEOS)
       return true;
   }
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 14440c4a..ee3b0866 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -109,7 +109,7 @@
   return id;
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 std::optional<base::Value::Dict> LoadManifestOnFileThread(
     const base::FilePath& root_directory,
     const base::FilePath::CharType* manifest_filename,
@@ -138,7 +138,9 @@
 
   return manifest;
 }
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 bool IsNormalSession() {
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
              ash::switches::kGuestSession) &&
@@ -548,9 +550,11 @@
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
     if (!base::FeatureList::IsEnabled(
             chromeos::features::kDisableOfficeEditingComponentApp)) {
-      Add(IDR_QUICKOFFICE_MANIFEST,
-          base::FilePath(
-              FILE_PATH_LITERAL("/usr/share/chromeos-assets/quickoffice")));
+      AddComponentFromDirWithManifestFilename(
+          base::FilePath("/usr/share/chromeos-assets/quickoffice"),
+          extension_misc::kQuickOfficeComponentExtensionId,
+          extensions::kManifestFilename, extensions::kManifestFilename,
+          base::DoNothing());
     }
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 #endif  // BUILDFLAG(IS_CHROMEOS)
@@ -610,24 +614,20 @@
   }
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-void ComponentLoader::AddComponentFromDir(const base::FilePath& root_directory,
-                                          const char* extension_id,
-                                          base::OnceClosure done_cb) {
-  AddComponentFromDirWithManifestFilename(
-      root_directory, extension_id, extensions::kManifestFilename,
-      extension_misc::kGuestManifestFilename, std::move(done_cb));
-}
-
+#if BUILDFLAG(IS_CHROMEOS)
 void ComponentLoader::AddComponentFromDirWithManifestFilename(
     const base::FilePath& root_directory,
-    const char* extension_id,
+    const ExtensionId& extension_id,
     const base::FilePath::CharType* manifest_file_name,
     const base::FilePath::CharType* guest_manifest_file_name,
     base::OnceClosure done_cb) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   const base::FilePath::CharType* manifest_filename =
       IsNormalSession() ? manifest_file_name : guest_manifest_file_name;
+#else
+  const base::FilePath::CharType* manifest_filename = manifest_file_name;
+#endif
   GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult(
       FROM_HERE,
       base::BindOnce(&LoadManifestOnFileThread, root_directory,
@@ -637,9 +637,47 @@
                      std::nullopt, std::nullopt, std::move(done_cb)));
 }
 
+void ComponentLoader::FinishAddComponentFromDir(
+    const base::FilePath& root_directory,
+    const ExtensionId& extension_id,
+    const std::optional<std::string>& name_string,
+    const std::optional<std::string>& description_string,
+    base::OnceClosure done_cb,
+    std::optional<base::Value::Dict> manifest) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!manifest) {
+    return;  // Error already logged.
+  }
+
+  if (name_string) {
+    manifest->Set(manifest_keys::kName, name_string.value());
+  }
+
+  if (description_string) {
+    manifest->Set(manifest_keys::kDescription, description_string.value());
+  }
+
+  std::string actual_extension_id =
+      Add(std::move(*manifest), root_directory, false);
+  CHECK_EQ(extension_id, actual_extension_id);
+  if (done_cb) {
+    std::move(done_cb).Run();
+  }
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+void ComponentLoader::AddComponentFromDir(const base::FilePath& root_directory,
+                                          const ExtensionId& extension_id,
+                                          base::OnceClosure done_cb) {
+  AddComponentFromDirWithManifestFilename(
+      root_directory, extension_id, extensions::kManifestFilename,
+      extension_misc::kGuestManifestFilename, std::move(done_cb));
+}
+
 void ComponentLoader::AddWithNameAndDescriptionFromDir(
     const base::FilePath& root_directory,
-    const char* extension_id,
+    const ExtensionId& extension_id,
     const std::string& name_string,
     const std::string& description_string) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -674,41 +712,13 @@
   }
 }
 
-void ComponentLoader::FinishAddComponentFromDir(
-    const base::FilePath& root_directory,
-    const char* extension_id,
-    const std::optional<std::string>& name_string,
-    const std::optional<std::string>& description_string,
-    base::OnceClosure done_cb,
-    std::optional<base::Value::Dict> manifest) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!manifest) {
-    return;  // Error already logged.
-  }
-
-  if (name_string) {
-    manifest->Set(manifest_keys::kName, name_string.value());
-  }
-
-  if (description_string) {
-    manifest->Set(manifest_keys::kDescription, description_string.value());
-  }
-
-  std::string actual_extension_id =
-      Add(std::move(*manifest), root_directory, false);
-  CHECK_EQ(extension_id, actual_extension_id);
-  if (done_cb) {
-    std::move(done_cb).Run();
-  }
-}
-
 void ComponentLoader::FinishLoadSpeechSynthesisExtension(
-    const char* extension_id) {
+    const ExtensionId& extension_id) {
   // TODO(https://crbug.com/947305): mitigation for extension not awake after
   // load.
   extensions::ProcessManager::Get(profile_)->WakeEventPage(extension_id,
                                                            base::DoNothing());
 }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/component_loader.h b/chrome/browser/extensions/component_loader.h
index 77a9a006..7767305 100644
--- a/chrome/browser/extensions/component_loader.h
+++ b/chrome/browser/extensions/component_loader.h
@@ -107,30 +107,32 @@
   // Return ids of all registered extensions.
   std::vector<ExtensionId> GetRegisteredComponentExtensionsIds() const;
 
+#if BUILDFLAG(IS_CHROMEOS)
+  // Identical to AddComponentFromDir() except allows for the caller to supply
+  // the name of the manifest file.
+  void AddComponentFromDirWithManifestFilename(
+      const base::FilePath& root_directory,
+      const ExtensionId& extension_id,
+      const base::FilePath::CharType* manifest_file_name,
+      const base::FilePath::CharType* guest_manifest_file_name,
+      base::OnceClosure done_cb);
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // Add a component extension from a specific directory. Assumes that the
   // extension uses a different manifest file when this is a guest session
   // and that the manifest file lives in |root_directory|. Calls |done_cb|
   // on success, unless the component loader is shut down during loading.
   void AddComponentFromDir(const base::FilePath& root_directory,
-                           const char* extension_id,
+                           const ExtensionId& extension_id,
                            base::OnceClosure done_cb);
 
-  // Identical to above except allows for the caller to supply the name of the
-  // manifest file.
-  void AddComponentFromDirWithManifestFilename(
-      const base::FilePath& root_directory,
-      const char* extension_id,
-      const base::FilePath::CharType* manifest_file_name,
-      const base::FilePath::CharType* guest_manifest_file_name,
-      base::OnceClosure done_cb);
-
   // Add a component extension from a specific directory. Assumes that the
   // extension's manifest file lives in |root_directory| and its name is
   // 'manifest.json'. |name_string| and |description_string| are used to
   // localize component extension's name and description text exclusively.
   void AddWithNameAndDescriptionFromDir(const base::FilePath& root_directory,
-                                        const char* extension_id,
+                                        const ExtensionId& extension_id,
                                         const std::string& name_string,
                                         const std::string& description_string);
 
@@ -201,6 +203,19 @@
                                  const std::string& description_string);
   void AddWebStoreApp();
 
+#if BUILDFLAG(IS_CHROMEOS)
+  // Used as a reply callback by |AddComponentFromDir|.
+  // Called with a |root_directory| and parsed |manifest| and invokes
+  // |done_cb| after adding the extension.
+  void FinishAddComponentFromDir(
+      const base::FilePath& root_directory,
+      const ExtensionId& extension_id,
+      const std::optional<std::string>& name_string,
+      const std::optional<std::string>& description_string,
+      base::OnceClosure done_cb,
+      std::optional<base::Value::Dict> manifest);
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   void AddChromeApp();
   void AddFileManagerExtension();
@@ -216,21 +231,8 @@
   // Unloads |component| from the memory.
   void UnloadComponent(ComponentExtensionInfo* component);
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // Used as a reply callback by |AddComponentFromDir|.
-  // Called with a |root_directory| and parsed |manifest| and invokes
-  // |done_cb| after adding the extension.
-  void FinishAddComponentFromDir(
-      const base::FilePath& root_directory,
-      const char* extension_id,
-      const std::optional<std::string>& name_string,
-      const std::optional<std::string>& description_string,
-      base::OnceClosure done_cb,
-      std::optional<base::Value::Dict> manifest);
-
   // Finishes loading an extension tts engine.
-  void FinishLoadSpeechSynthesisExtension(const char* extension_id);
-#endif
+  void FinishLoadSpeechSynthesisExtension(const ExtensionId& extension_id);
 
   raw_ptr<Profile> profile_;
 
diff --git a/chrome/browser/facilitated_payments/ui/android/BUILD.gn b/chrome/browser/facilitated_payments/ui/android/BUILD.gn
new file mode 100644
index 0000000..1583f6e1
--- /dev/null
+++ b/chrome/browser/facilitated_payments/ui/android/BUILD.gn
@@ -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.
+
+static_library("android") {
+  sources = [
+    "facilitated_payments_bottom_sheet_bridge.cc",
+    "facilitated_payments_bottom_sheet_bridge.h",
+  ]
+
+  deps = [
+    "//base:base",
+    "//chrome/android:jni_headers",
+    "//content/public/browser:browser",
+    "//ui/android:android"
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "facilitated_payments_bottom_sheet_bridge_unittest.cc" ]
+
+  deps = [
+    ":android",
+    "//chrome/test:test_support",
+    "//content/public/browser:browser",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge.cc b/chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge.cc
new file mode 100644
index 0000000..e461624
--- /dev/null
+++ b/chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge.cc
@@ -0,0 +1,38 @@
+// 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 "chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge.h"
+
+#include "base/android/jni_android.h"
+#include "chrome/android/chrome_jni_headers/FacilitatedPaymentsBottomSheetBridge_jni.h"
+#include "content/public/browser/web_contents.h"
+
+namespace payments::facilitated {
+
+FacilitatedPaymentsBottomSheetBridge::FacilitatedPaymentsBottomSheetBridge()
+    : java_bridge_(Java_FacilitatedPaymentsBottomSheetBridge_Constructor(
+          base::android::AttachCurrentThread())) {}
+
+FacilitatedPaymentsBottomSheetBridge::~FacilitatedPaymentsBottomSheetBridge() =
+    default;
+
+bool FacilitatedPaymentsBottomSheetBridge::RequestShowContent(
+    content::WebContents* web_contents) {
+  if (!web_contents) {
+    return false;
+  }
+
+  base::android::ScopedJavaLocalRef<jobject> java_web_contents =
+      web_contents->GetJavaWebContents();
+  if (!java_web_contents) {
+    return false;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  return Java_FacilitatedPaymentsBottomSheetBridge_requestShowContent(
+      env, java_bridge_, java_web_contents);
+}
+
+}  // namespace payments::facilitated
diff --git a/chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge.h b/chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge.h
new file mode 100644
index 0000000..0bb1143
--- /dev/null
+++ b/chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge.h
@@ -0,0 +1,40 @@
+// 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 CHROME_BROWSER_FACILITATED_PAYMENTS_UI_ANDROID_FACILITATED_PAYMENTS_BOTTOM_SHEET_BRIDGE_H_
+#define CHROME_BROWSER_FACILITATED_PAYMENTS_UI_ANDROID_FACILITATED_PAYMENTS_BOTTOM_SHEET_BRIDGE_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "ui/android/window_android.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace payments::facilitated {
+
+// Bridge class providing an entry point to trigger the facilitated payments
+// bottom sheet on Android.
+class FacilitatedPaymentsBottomSheetBridge {
+ public:
+  FacilitatedPaymentsBottomSheetBridge();
+
+  FacilitatedPaymentsBottomSheetBridge(
+      const FacilitatedPaymentsBottomSheetBridge&) = delete;
+  FacilitatedPaymentsBottomSheetBridge& operator=(
+      const FacilitatedPaymentsBottomSheetBridge&) = delete;
+
+  ~FacilitatedPaymentsBottomSheetBridge();
+
+  bool RequestShowContent(content::WebContents* web_contents);
+
+ private:
+  base::android::ScopedJavaGlobalRef<jobject> java_bridge_;
+};
+
+}  // namespace payments::facilitated
+
+#endif  // CHROME_BROWSER_FACILITATED_PAYMENTS_UI_ANDROID_FACILITATED_PAYMENTS_BOTTOM_SHEET_BRIDGE_H_
diff --git a/chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge_unittest.cc b/chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge_unittest.cc
new file mode 100644
index 0000000..6fa18d8
--- /dev/null
+++ b/chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge_unittest.cc
@@ -0,0 +1,26 @@
+// 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 "chrome/browser/facilitated_payments/ui/android/facilitated_payments_bottom_sheet_bridge.h"
+
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/browser/browser_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace payments::facilitated {
+namespace {
+
+using FacilitatedPaymentsBottomSheetBridgeTest =
+    ChromeRenderViewHostTestHarness;
+
+TEST_F(FacilitatedPaymentsBottomSheetBridgeTest, RequestShowContent) {
+  FacilitatedPaymentsBottomSheetBridge bridge;
+
+  bool did_show = bridge.RequestShowContent(web_contents());
+
+  EXPECT_FALSE(did_show);
+}
+
+}  // namespace
+}  // namespace payments::facilitated
diff --git a/chrome/browser/feed/android/feed_surface_renderer_bridge.cc b/chrome/browser/feed/android/feed_surface_renderer_bridge.cc
index d94920ec..9be2547 100644
--- a/chrome/browser/feed/android/feed_surface_renderer_bridge.cc
+++ b/chrome/browser/feed/android/feed_surface_renderer_bridge.cc
@@ -5,13 +5,13 @@
 #include "chrome/browser/feed/android/feed_surface_renderer_bridge.h"
 
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/android/callback_android.h"
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
-#include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "chrome/browser/feed/android/feed_reliability_logging_bridge.h"
 #include "chrome/browser/feed/android/jni_headers/FeedSurfaceRendererBridge_jni.h"
@@ -128,15 +128,15 @@
   Java_FeedSurfaceRendererBridge_onStreamUpdated(env, java_ref_, j_data);
 }
 
-void FeedSurfaceRendererBridge::ReplaceDataStoreEntry(base::StringPiece key,
-                                                      base::StringPiece data) {
+void FeedSurfaceRendererBridge::ReplaceDataStoreEntry(std::string_view key,
+                                                      std::string_view data) {
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_FeedSurfaceRendererBridge_replaceDataStoreEntry(
       env, java_ref_, base::android::ConvertUTF8ToJavaString(env, key),
       base::android::ToJavaByteArray(env, base::as_byte_span(data)));
 }
 
-void FeedSurfaceRendererBridge::RemoveDataStoreEntry(base::StringPiece key) {
+void FeedSurfaceRendererBridge::RemoveDataStoreEntry(std::string_view key) {
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_FeedSurfaceRendererBridge_removeDataStoreEntry(
       env, java_ref_, base::android::ConvertUTF8ToJavaString(env, key));
diff --git a/chrome/browser/feed/android/feed_surface_renderer_bridge.h b/chrome/browser/feed/android/feed_surface_renderer_bridge.h
index 87e901b..ea3850a 100644
--- a/chrome/browser/feed/android/feed_surface_renderer_bridge.h
+++ b/chrome/browser/feed/android/feed_surface_renderer_bridge.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_FEED_ANDROID_FEED_SURFACE_RENDERER_BRIDGE_H_
 #define CHROME_BROWSER_FEED_ANDROID_FEED_SURFACE_RENDERER_BRIDGE_H_
 
+#include <string_view>
+
 #include "base/android/jni_android.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/memory/raw_ptr.h"
@@ -39,9 +41,9 @@
 
   // SurfaceRenderer implementation.
   void StreamUpdate(const feedui::StreamUpdate& update) override;
-  void ReplaceDataStoreEntry(base::StringPiece key,
-                             base::StringPiece data) override;
-  void RemoveDataStoreEntry(base::StringPiece key) override;
+  void ReplaceDataStoreEntry(std::string_view key,
+                             std::string_view data) override;
+  void RemoveDataStoreEntry(std::string_view key) override;
 
   ReliabilityLoggingBridge& GetReliabilityLoggingBridge() override;
 
diff --git a/chrome/browser/feed/feed_service_factory.cc b/chrome/browser/feed/feed_service_factory.cc
index 9630b62e..ce79083 100644
--- a/chrome/browser/feed/feed_service_factory.cc
+++ b/chrome/browser/feed/feed_service_factory.cc
@@ -6,10 +6,10 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <utility>
 
 #include "base/check.h"
-#include "base/strings/string_piece.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -47,7 +47,7 @@
 #endif
 
 namespace internal {
-const base::StringPiece GetFollowingFeedFollowCountGroupName(
+const std::string_view GetFollowingFeedFollowCountGroupName(
     size_t follow_count) {
   if (follow_count == 0)
     return "None";
@@ -136,7 +136,7 @@
         "FollowingFeedFollowCount",
         internal::GetFollowingFeedFollowCountGroupName(follow_count));
   }
-  void RegisterFeedUserSettingsFieldTrial(base::StringPiece group) override {
+  void RegisterFeedUserSettingsFieldTrial(std::string_view group) override {
     ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
         "FeedUserSettings", group);
   }
diff --git a/chrome/browser/feed/feed_service_factory.h b/chrome/browser/feed/feed_service_factory.h
index a149be1..3c8bf1e 100644
--- a/chrome/browser/feed/feed_service_factory.h
+++ b/chrome/browser/feed/feed_service_factory.h
@@ -5,8 +5,9 @@
 #ifndef CHROME_BROWSER_FEED_FEED_SERVICE_FACTORY_H_
 #define CHROME_BROWSER_FEED_FEED_SERVICE_FACTORY_H_
 
+#include <string_view>
+
 #include "base/no_destructor.h"
-#include "base/strings/string_piece.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 
 namespace content {
@@ -15,7 +16,7 @@
 
 namespace feed {
 namespace internal {
-const base::StringPiece GetFollowingFeedFollowCountGroupName(
+const std::string_view GetFollowingFeedFollowCountGroupName(
     size_t follow_count);
 }
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 7dfd779..e975aa13 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -440,6 +440,16 @@
     "expiry_milestone": 125
   },
   {
+    "name": "ash-forest-feature",
+    "owners": [ "sammiequon@chromium.org", "cros-forest@google.com"],
+    "expiry_milestone": 129
+  },
+  {
+    "name": "ash-forest-feature-key",
+    "owners": [ "sammiequon@chromium.org", "cros-forest@google.com"],
+    "expiry_milestone": 129
+  },
+  {
     "name": "ash-limit-shelf-items-to-active-desk",
     "owners": [ "afakhry@chromium.org", "tclaiborne@chromium.org" ],
     "expiry_milestone": 125
@@ -2379,6 +2389,11 @@
     "expiry_milestone": 116
   },
   {
+    "name": "enable-color-lens-and-voice-icons-in-home-screen-widget",
+    "owners": [ "cmyang@google.com" ],
+    "expiry_milestone": 130
+  },
+  {
     "name": "enable-command-line-on-non-rooted-devices",
     "owners": [ "clank-app-team@google.com" ],
     // This flag is used for debugging on Android; it causes Android Chromium
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c565197a..d3baaf3 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -267,6 +267,14 @@
     "If enabled, the full screen signin promo will be forced to show up at "
     "Chrome start-up.";
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+const char kForestFeatureName[] = "Forest";
+const char kForestFeatureDescription[] = "Enable Forest experiment.";
+
+const char kForestKeyName[] = "Forest key";
+const char kForestKeyDescription[] = "Enable Forest key experiment.";
+#endif
+
 #if BUILDFLAG(USE_FONTATIONS_BACKEND)
 const char kFontationsFontBackendName[] = "Enable Fontations font backend";
 const char kFontationsFontBackendDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8a553fb..49c7d75 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -206,6 +206,14 @@
 extern const char kForceStartupSigninPromoName[];
 extern const char kForceStartupSigninPromoDescription[];
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+extern const char kForestFeatureName[];
+extern const char kForestFeatureDescription[];
+
+extern const char kForestKeyName[];
+extern const char kForestKeyDescription[];
+#endif
+
 extern const char kGainmapHdrImagesName[];
 extern const char kGainmapHdrImagesDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 4807eb5..25ca7ff 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -234,7 +234,6 @@
     &kPreconnectOnTabCreation,
     &kPriceChangeModule,
     &kPwaRestoreUi,
-    &kPwaUniversalInstallUi,
     &kOmahaMinSdkVersionAndroid,
     &kShortCircuitUnfocusAnimation,
     &kOmniboxNoopEditUrlSuggestionClicks,
@@ -363,8 +362,9 @@
     &syncer::kSyncEnableContactInfoDataTypeInTransportMode,
     &syncer::kSyncShowIdentityErrorsForSignedInUsers,
     &syncer::kWebApkBackupAndRestoreBackend,
-    &webapps::features::kWebApkInstallFailureNotification,
     &webapps::features::kAmbientBadgeSuppressFirstVisit,
+    &webapps::features::kPwaUniversalInstallUi,
+    &webapps::features::kWebApkInstallFailureNotification,
     &network::features::kPrivateStateTokens,
 };
 
@@ -722,10 +722,6 @@
 
 BASE_FEATURE(kPwaRestoreUi, "PwaRestoreUi", base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kPwaUniversalInstallUi,
-             "PwaUniversalInstallUi",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kBackGestureActivityTabProvider,
              "BackGestureActivityTabProvider",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index bca64de9..d190152 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -119,7 +119,6 @@
 BASE_DECLARE_FEATURE(kPreconnectOnTabCreation);
 BASE_DECLARE_FEATURE(kPriceChangeModule);
 BASE_DECLARE_FEATURE(kPwaRestoreUi);
-BASE_DECLARE_FEATURE(kPwaUniversalInstallUi);
 BASE_DECLARE_FEATURE(kOpenDownloadDialog);
 BASE_DECLARE_FEATURE(kPartnerCustomizationsUma);
 BASE_DECLARE_FEATURE(kQuickDeleteForAndroid);
diff --git a/chrome/browser/hid/chrome_hid_delegate_unittest.cc b/chrome/browser/hid/chrome_hid_delegate_unittest.cc
index f485b725..4536483 100644
--- a/chrome/browser/hid/chrome_hid_delegate_unittest.cc
+++ b/chrome/browser/hid/chrome_hid_delegate_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/hid/chrome_hid_delegate.h"
 
 #include <memory>
+#include <string_view>
 
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
@@ -61,14 +62,14 @@
 using ::testing::NiceMock;
 using ::testing::UnorderedElementsAre;
 
-constexpr base::StringPiece kDefaultTestUrl{"https://www.google.com"};
-constexpr base::StringPiece kCrossOriginTestUrl{"https://www.chromium.org"};
+constexpr std::string_view kDefaultTestUrl{"https://www.google.com"};
+constexpr std::string_view kCrossOriginTestUrl{"https://www.chromium.org"};
 constexpr char kTestUserEmail[] = "user@example.com";
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-constexpr base::StringPiece kPrivilegedExtensionId{
+constexpr std::string_view kPrivilegedExtensionId{
     "ckcendljdlmgnhghiaomidhiiclmapok"};
-constexpr base::StringPiece kExtensionDocumentFileName{"index.html"};
+constexpr std::string_view kExtensionDocumentFileName{"index.html"};
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 MATCHER_P(HasGuid, matcher, "") {
@@ -229,7 +230,7 @@
   // Creates a fake extension with the specified `extension_id` so that it can
   // exercise behaviors that are only enabled for privileged extensions.
   scoped_refptr<const extensions::Extension> CreateExtensionWithId(
-      base::StringPiece extension_id) {
+      std::string_view extension_id) {
     auto manifest =
         base::Value::Dict()
             .Set("name", "Fake extension")
diff --git a/chrome/browser/hid/hid_chooser_context.cc b/chrome/browser/hid/hid_chooser_context.cc
index 70694ab..e5ff0b4 100644
--- a/chrome/browser/hid/hid_chooser_context.cc
+++ b/chrome/browser/hid/hid_chooser_context.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/hid/hid_chooser_context.h"
 
 #include <set>
+#include <string_view>
 #include <utility>
 
 #include "base/containers/contains.h"
@@ -37,7 +38,7 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/containers/fixed_flat_set.h"
-#include "base/strings/string_piece.h"
+
 #include "extensions/common/constants.h"
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
@@ -523,7 +524,7 @@
 bool HidChooserContext::IsFidoAllowedForOrigin(const url::Origin& origin) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   static constexpr auto kPrivilegedExtensionIds =
-      base::MakeFixedFlatSet<base::StringPiece>({
+      base::MakeFixedFlatSet<std::string_view>({
           "ckcendljdlmgnhghiaomidhiiclmapok",  // gnubbyd-v3 dev
           "lfboplenmmjcmpbkeemecobbadnmpfhi",  // gnubbyd-v3 prod
       });
diff --git a/chrome/browser/hid/hid_chooser_context_unittest.cc b/chrome/browser/hid/hid_chooser_context_unittest.cc
index 55493e45..c12b291 100644
--- a/chrome/browser/hid/hid_chooser_context_unittest.cc
+++ b/chrome/browser/hid/hid_chooser_context_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/hid/hid_chooser_context.h"
 
 #include <optional>
+#include <string_view>
 
 #include "base/barrier_closure.h"
 #include "base/memory/raw_ptr.h"
@@ -132,7 +133,7 @@
   MockHidDeviceObserver& device_observer() { return device_observer_; }
 
   device::mojom::HidDeviceInfoPtr CreateDevice(
-      base::StringPiece serial_number,
+      std::string_view serial_number,
       const std::string& physical_device_id = kTestPhysicalDeviceIds[0]) {
     auto collection = device::mojom::HidCollectionInfo::New();
     collection->usage =
@@ -251,7 +252,7 @@
     loop.Run();
   }
 
-  void SetDynamicBlocklist(base::StringPiece value) {
+  void SetDynamicBlocklist(std::string_view value) {
     feature_list_.Reset();
 
     std::map<std::string, std::string> parameters;
@@ -276,34 +277,34 @@
         std::make_unique<base::Value>(content_setting));
   }
 
-  void SetAskForUrlsPolicy(base::StringPiece policy) {
+  void SetAskForUrlsPolicy(std::string_view policy) {
     profile_->GetTestingPrefService()->SetManagedPref(
         prefs::kManagedWebHidAskForUrls, ParseJson(policy));
   }
 
-  void SetBlockedForUrlsPolicy(base::StringPiece policy) {
+  void SetBlockedForUrlsPolicy(std::string_view policy) {
     profile_->GetTestingPrefService()->SetManagedPref(
         prefs::kManagedWebHidBlockedForUrls, ParseJson(policy));
   }
 
-  void SetAllowDevicesForUrlsPolicy(base::StringPiece policy) {
+  void SetAllowDevicesForUrlsPolicy(std::string_view policy) {
     testing_profile_manager_->local_state()->Get()->SetManagedPref(
         prefs::kManagedWebHidAllowDevicesForUrls, ParseJson(policy));
   }
 
-  void SetAllowDevicesForUrlsOnLoginScreenPolicy(base::StringPiece policy) {
+  void SetAllowDevicesForUrlsOnLoginScreenPolicy(std::string_view policy) {
     testing_profile_manager_->local_state()->Get()->SetManagedPref(
         prefs::kManagedWebHidAllowDevicesForUrlsOnLoginScreen,
         ParseJson(policy));
   }
 
-  void SetAllowDevicesWithHidUsagesForUrlsPolicy(base::StringPiece policy) {
+  void SetAllowDevicesWithHidUsagesForUrlsPolicy(std::string_view policy) {
     testing_profile_manager_->local_state()->Get()->SetManagedPref(
         prefs::kManagedWebHidAllowDevicesWithHidUsagesForUrls,
         ParseJson(policy));
   }
 
-  void SetAllowAllDevicesForUrlsPolicy(base::StringPiece policy) {
+  void SetAllowAllDevicesForUrlsPolicy(std::string_view policy) {
     testing_profile_manager_->local_state()->Get()->SetManagedPref(
         prefs::kManagedWebHidAllowAllDevicesForUrls, ParseJson(policy));
   }
diff --git a/chrome/browser/hid/hid_policy_allowed_devices_unittest.cc b/chrome/browser/hid/hid_policy_allowed_devices_unittest.cc
index 98344cc..7c050d6 100644
--- a/chrome/browser/hid/hid_policy_allowed_devices_unittest.cc
+++ b/chrome/browser/hid/hid_policy_allowed_devices_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/hid/hid_policy_allowed_devices.h"
 
 #include <string>
+#include <string_view>
 #include <utility>
 
 #include "base/strings/string_number_conversions.h"
@@ -39,22 +40,22 @@
                                                         on_login_screen);
   }
 
-  void SetAllowDevicesForUrlsPrefValue(base::StringPiece policy) {
+  void SetAllowDevicesForUrlsPrefValue(std::string_view policy) {
     local_state_.Set(prefs::kManagedWebHidAllowDevicesForUrls,
                      ParseJson(policy));
   }
 
-  void SetAllowDevicesForUrlsOnLoginScreenPrefValue(base::StringPiece policy) {
+  void SetAllowDevicesForUrlsOnLoginScreenPrefValue(std::string_view policy) {
     local_state_.Set(prefs::kManagedWebHidAllowDevicesForUrlsOnLoginScreen,
                      ParseJson(policy));
   }
 
-  void SetAllowDevicesWithHidUsagesForUrlsPrefValue(base::StringPiece policy) {
+  void SetAllowDevicesWithHidUsagesForUrlsPrefValue(std::string_view policy) {
     local_state_.Set(prefs::kManagedWebHidAllowDevicesWithHidUsagesForUrls,
                      ParseJson(policy));
   }
 
-  void SetAllowAllDevicesForUrlsPrefValue(base::StringPiece policy) {
+  void SetAllowAllDevicesForUrlsPrefValue(std::string_view policy) {
     local_state_.Set(prefs::kManagedWebHidAllowAllDevicesForUrls,
                      ParseJson(policy));
   }
diff --git a/chrome/browser/history_embeddings/history_embeddings_service_browsertest.cc b/chrome/browser/history_embeddings/history_embeddings_service_browsertest.cc
index 5e49dab7..ca0f4cc 100644
--- a/chrome/browser/history_embeddings/history_embeddings_service_browsertest.cc
+++ b/chrome/browser/history_embeddings/history_embeddings_service_browsertest.cc
@@ -59,4 +59,30 @@
   ASSERT_EQ(url_passages.passages.passages(0), "A B C D");
 }
 
+IN_PROC_BROWSER_TEST_F(HistoryEmbeddingsBrowserTest,
+                       SearchFindsResultWithSourcePassage) {
+  auto* service =
+      HistoryEmbeddingsServiceFactory::GetForProfile(browser()->profile());
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("/inner_text/test1.html")));
+
+  // Wait for passage retrieval to complete.
+  {
+    base::test::TestFuture<UrlPassages> future;
+    service->RetrievePassages(*web_contents->GetPrimaryMainFrame(),
+                              future.GetCallback());
+    UrlPassages url_passages = future.Take();
+  }
+
+  // Search for the passage.
+  base::test::TestFuture<std::vector<ScoredUrl>> future;
+  service->Search("A B C D e f g", 1, future.GetCallback());
+  std::vector<ScoredUrl> scored_urls = future.Take();
+  EXPECT_EQ(scored_urls.size(), 1u);
+  EXPECT_EQ(scored_urls[0].passage, "A B C D");
+}
+
 }  // namespace history_embeddings
diff --git a/chrome/browser/incognito/android/javatests/src/org/chromium/chrome/browser/incognito/reauth/TabSwitcherIncognitoReauthViewTest.java b/chrome/browser/incognito/android/javatests/src/org/chromium/chrome/browser/incognito/reauth/TabSwitcherIncognitoReauthViewTest.java
index 5b9e897..2e584c5 100644
--- a/chrome/browser/incognito/android/javatests/src/org/chromium/chrome/browser/incognito/reauth/TabSwitcherIncognitoReauthViewTest.java
+++ b/chrome/browser/incognito/android/javatests/src/org/chromium/chrome/browser/incognito/reauth/TabSwitcherIncognitoReauthViewTest.java
@@ -31,6 +31,7 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
@@ -190,6 +191,7 @@
 
     @Test
     @LargeTest
+    @DisabledTest(message = "crbug.com/330226530")
     public void testIncognitoReauthViewIsRestored_WhenActivityIsKilled() {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         openIncognitoReauth(cta);
diff --git a/chrome/browser/installable/ml_promotion_browsertest.cc b/chrome/browser/installable/ml_promotion_browsertest.cc
index 6e97949b..92b714f9 100644
--- a/chrome/browser/installable/ml_promotion_browsertest.cc
+++ b/chrome/browser/installable/ml_promotion_browsertest.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/web_app_command_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/common/chrome_features.h"
 #include "components/segmentation_platform/public/constants.h"
 #include "components/segmentation_platform/public/input_context.h"
 #include "components/segmentation_platform/public/testing/mock_segmentation_platform_service.h"
@@ -85,7 +86,7 @@
 }
 
 enum class InstallDialogState {
-  kPWAConfirmationBubble = 0,
+  kSimpleInstallDialog = 0,
   kDetailedInstallDialog = 1,
   kCreateShortcutDialog = 2,
   kMaxValue = kCreateShortcutDialog
@@ -94,8 +95,8 @@
 std::string GetMLPromotionDialogTestName(
     const ::testing::TestParamInfo<InstallDialogState>& info) {
   switch (info.param) {
-    case InstallDialogState::kPWAConfirmationBubble:
-      return "PWA_Confirmation_Bubble";
+    case InstallDialogState::kSimpleInstallDialog:
+      return "Simple_Install_Dialog";
     case InstallDialogState::kDetailedInstallDialog:
       return "Detailed_Install_Dialog";
     case InstallDialogState::kCreateShortcutDialog:
@@ -645,8 +646,14 @@
       MLInstallabilityPromoter::kShowInstallPromptLabel,
       TrainingRequestId(1ll));
 
+  // Since the site is not installable, the diy install dialog shows up for
+  // universal install.
+  std::string bubble_name_to_use =
+      base::FeatureList::IsEnabled(::features::kWebAppUniversalInstall)
+          ? "WebAppDiyInstallDialog"
+          : "PWAConfirmationBubbleView";
   views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
-                                       "PWAConfirmationBubbleView");
+                                       bubble_name_to_use);
   task_runner_->RunPendingTasks();
   views::Widget* widget = waiter.WaitIfNeededAndGet();
   views::test::WidgetDestroyedWaiter destroyed(widget);
@@ -714,8 +721,7 @@
 }
 
 // TODO(b/285361272): Add tests for cache storage sizes.
-
-// TODO(b/287255120) : Implement ways of measuring ML outputs on Android.
+// TODO(b/329696741): Update DIY App dialog information also here.
 class MLPromotionInstallDialogBrowserTest
     : public MLPromotionBrowserTest,
       public ::testing::WithParamInterface<InstallDialogState> {
@@ -726,8 +732,8 @@
  protected:
   const std::string GetDialogName() {
     switch (GetParam()) {
-      case InstallDialogState::kPWAConfirmationBubble:
-        return "PWAConfirmationBubbleView";
+      case InstallDialogState::kSimpleInstallDialog:
+        return GetSimpleInstallDialogNameBasedOnUniversalInstall();
       case InstallDialogState::kDetailedInstallDialog:
         return "WebAppDetailedInstallDialog";
       case InstallDialogState::kCreateShortcutDialog:
@@ -737,7 +743,7 @@
 
   const GURL GetUrlBasedOnDialogState() {
     switch (GetParam()) {
-      case InstallDialogState::kPWAConfirmationBubble:
+      case InstallDialogState::kSimpleInstallDialog:
         return GetInstallableAppURL();
       case InstallDialogState::kDetailedInstallDialog:
         return https_server()->GetURL(
@@ -750,7 +756,7 @@
   // These names are obtained from the manifests in chrome/test/data/banners/
   const std::string GetAppNameBasedOnDialogState() {
     switch (GetParam()) {
-      case InstallDialogState::kPWAConfirmationBubble:
+      case InstallDialogState::kSimpleInstallDialog:
         return "Manifest test app";
       case InstallDialogState::kDetailedInstallDialog:
         return "PWA Bottom Sheet";
@@ -762,7 +768,7 @@
 
   void InstallAppBasedOnDialogState() {
     switch (GetParam()) {
-      case InstallDialogState::kPWAConfirmationBubble:
+      case InstallDialogState::kSimpleInstallDialog:
       case InstallDialogState::kDetailedInstallDialog:
         InstallAppForCurrentWebContents(/*install_locally=*/true);
         break;
@@ -777,6 +783,14 @@
   bool IsCurrentTestStateShortcutDialog() {
     return GetParam() == InstallDialogState::kCreateShortcutDialog;
   }
+
+ private:
+  const std::string GetSimpleInstallDialogNameBasedOnUniversalInstall() {
+    if (base::FeatureList::IsEnabled(::features::kWebAppUniversalInstall)) {
+      return "WebAppSimpleInstallDialog";
+    }
+    return "PWAConfirmationBubbleView";
+  }
 };
 
 IN_PROC_BROWSER_TEST_P(MLPromotionInstallDialogBrowserTest, MlInstallNotShown) {
@@ -1142,7 +1156,7 @@
 INSTANTIATE_TEST_SUITE_P(
     All,
     MLPromotionInstallDialogBrowserTest,
-    ::testing::Values(InstallDialogState::kPWAConfirmationBubble,
+    ::testing::Values(InstallDialogState::kSimpleInstallDialog,
                       InstallDialogState::kDetailedInstallDialog,
                       InstallDialogState::kCreateShortcutDialog),
     GetMLPromotionDialogTestName);
diff --git a/chrome/browser/lacros/embedded_a11y_manager_lacros.cc b/chrome/browser/lacros/embedded_a11y_manager_lacros.cc
index e12a61e9..c9a6965 100644
--- a/chrome/browser/lacros/embedded_a11y_manager_lacros.cc
+++ b/chrome/browser/lacros/embedded_a11y_manager_lacros.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/singleton.h"
 #include "base/path_service.h"
+#include "chrome/browser/accessibility/embedded_a11y_extension_loader.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -29,49 +30,6 @@
 #include "extensions/common/extension_l10n_util.h"
 #include "extensions/common/file_util.h"
 
-namespace {
-
-std::optional<base::Value::Dict> LoadManifestOnFileThread(
-    const base::FilePath& path,
-    const base::FilePath::CharType* manifest_filename,
-    bool localize) {
-  CHECK(extensions::GetExtensionFileTaskRunner()->RunsTasksInCurrentSequence());
-  std::string error;
-  auto manifest =
-      extensions::file_util::LoadManifest(path, manifest_filename, &error);
-  if (!manifest) {
-    LOG(ERROR) << "Can't load " << path.Append(manifest_filename).AsUTF8Unsafe()
-               << ": " << error;
-    return std::nullopt;
-  }
-  if (localize) {
-    // This is only called for Lacros component extensions which are loaded
-    // from a read-only rootfs partition, so it is safe to set
-    // |gzip_permission| to kAllowForTrustedSource.
-    bool localized = extension_l10n_util::LocalizeExtension(
-        path, &manifest.value(),
-        extension_l10n_util::GzippedMessagesPermission::kAllowForTrustedSource,
-        &error);
-    CHECK(localized) << error;
-  }
-  return manifest;
-}
-
-extensions::ComponentLoader* GetComponentLoader(Profile* profile) {
-  auto* extension_system = extensions::ExtensionSystem::Get(profile);
-  if (!extension_system) {
-    // May be missing on the Lacros login profile.
-    return nullptr;
-  }
-  auto* extension_service = extension_system->extension_service();
-  if (!extension_service) {
-    return nullptr;
-  }
-  return extension_service->component_loader();
-}
-
-}  // namespace
-
 // static
 EmbeddedA11yManagerLacros* EmbeddedA11yManagerLacros::GetInstance() {
   return base::Singleton<
@@ -153,6 +111,8 @@
           &EmbeddedA11yManagerLacros::OnPdfOcrAlwaysActiveChanged,
           weak_ptr_factory_.GetWeakPtr()));
 
+  EmbeddedA11yExtensionLoader::GetInstance()->Init();
+
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   profile_manager_observation_.Observe(profile_manager);
 
@@ -163,7 +123,9 @@
     observed_profiles_.AddObservation(profile);
   }
 
-  UpdateAllProfiles();
+  UpdatePdfOcrEnabledOnAllProfiles();
+  UpdateEmbeddedA11yHelperExtension();
+  UpdateChromeVoxHelperExtension();
 }
 
 void EmbeddedA11yManagerLacros::SpeakSelectedText() {
@@ -181,11 +143,6 @@
   return select_to_speak_enabled_;
 }
 
-void EmbeddedA11yManagerLacros::AddExtensionChangedCallbackForTest(
-    base::RepeatingClosure callback) {
-  extension_installation_changed_callback_for_test_ = std::move(callback);
-}
-
 void EmbeddedA11yManagerLacros::AddSpeakSelectedTextCallbackForTest(
     base::RepeatingClosure callback) {
   speak_selected_text_callback_for_test_ = std::move(callback);
@@ -196,17 +153,6 @@
   focus_changed_callback_for_test_ = std::move(callback);
 }
 
-void EmbeddedA11yManagerLacros::SetReadingModeEnabled(bool enabled) {
-  if (reading_mode_enabled_ != enabled) {
-    reading_mode_enabled_ = enabled;
-    UpdateAllProfiles();
-  }
-}
-
-bool EmbeddedA11yManagerLacros::IsReadingModeEnabled() {
-  return reading_mode_enabled_;
-}
-
 void EmbeddedA11yManagerLacros::OnProfileWillBeDestroyed(Profile* profile) {
   observed_profiles_.RemoveObservation(profile);
 }
@@ -214,56 +160,33 @@
 void EmbeddedA11yManagerLacros::OnOffTheRecordProfileCreated(
     Profile* off_the_record) {
   observed_profiles_.AddObservation(off_the_record);
-  UpdateProfile(off_the_record);
+  UpdatePdfOcrEnabledOnProfile(off_the_record);
 }
 
 void EmbeddedA11yManagerLacros::OnProfileAdded(Profile* profile) {
   observed_profiles_.AddObservation(profile);
-  UpdateProfile(profile);
+  UpdatePdfOcrEnabledOnProfile(profile);
 }
 
 void EmbeddedA11yManagerLacros::OnProfileManagerDestroying() {
   profile_manager_observation_.Reset();
 }
 
-void EmbeddedA11yManagerLacros::UpdateAllProfiles() {
+void EmbeddedA11yManagerLacros::UpdatePdfOcrEnabledOnAllProfiles() {
   std::vector<Profile*> profiles =
       g_browser_process->profile_manager()->GetLoadedProfiles();
   for (auto* profile : profiles) {
-    UpdateProfile(profile);
+    UpdatePdfOcrEnabledOnProfile(profile);
     if (profile->HasAnyOffTheRecordProfile()) {
       const auto& otr_profiles = profile->GetAllOffTheRecordProfiles();
       for (auto* otr_profile : otr_profiles) {
-        UpdateProfile(otr_profile);
+        UpdatePdfOcrEnabledOnProfile(otr_profile);
       }
     }
   }
 }
 
-void EmbeddedA11yManagerLacros::UpdateProfile(Profile* profile) {
-  // Switch Access, Select to Speak, and Reading Mode share a helper extension
-  // which has a manifest content script to tell Google Docs to annotate the
-  // HTML canvas.
-  if (select_to_speak_enabled_ || switch_access_enabled_ ||
-      reading_mode_enabled_) {
-    MaybeInstallExtension(profile,
-                          extension_misc::kEmbeddedA11yHelperExtensionId,
-                          extension_misc::kEmbeddedA11yHelperExtensionPath,
-                          extension_misc::kEmbeddedA11yHelperManifestFilename);
-  } else {
-    MaybeRemoveExtension(profile,
-                         extension_misc::kEmbeddedA11yHelperExtensionId);
-  }
-  // ChromeVox has a helper extension which has a content script to tell Google
-  // Docs that ChromeVox is enabled.
-  if (chromevox_enabled_) {
-    MaybeInstallExtension(profile, extension_misc::kChromeVoxHelperExtensionId,
-                          extension_misc::kChromeVoxHelperExtensionPath,
-                          extension_misc::kChromeVoxHelperManifestFilename);
-  } else {
-    MaybeRemoveExtension(profile, extension_misc::kChromeVoxHelperExtensionId);
-  }
-
+void EmbeddedA11yManagerLacros::UpdatePdfOcrEnabledOnProfile(Profile* profile) {
   if (pdf_ocr_always_active_enabled_.has_value()) {
     PrefService* const pref_service = profile->GetPrefs();
     CHECK(pref_service);
@@ -275,21 +198,21 @@
 void EmbeddedA11yManagerLacros::OnChromeVoxEnabledChanged(base::Value value) {
   CHECK(value.is_bool());
   chromevox_enabled_ = value.GetBool();
-  UpdateAllProfiles();
+  UpdateChromeVoxHelperExtension();
 }
 
 void EmbeddedA11yManagerLacros::OnSelectToSpeakEnabledChanged(
     base::Value value) {
   CHECK(value.is_bool());
   select_to_speak_enabled_ = value.GetBool();
-  UpdateAllProfiles();
+  UpdateEmbeddedA11yHelperExtension();
 }
 
 void EmbeddedA11yManagerLacros::OnSwitchAccessEnabledChanged(
     base::Value value) {
   CHECK(value.is_bool());
   switch_access_enabled_ = value.GetBool();
-  UpdateAllProfiles();
+  UpdateEmbeddedA11yHelperExtension();
 }
 
 void EmbeddedA11yManagerLacros::OnFocusHighlightEnabledChanged(
@@ -311,71 +234,7 @@
   // all profiles.
   CHECK(value.is_bool());
   pdf_ocr_always_active_enabled_ = value.GetBool();
-  UpdateAllProfiles();
-}
-
-void EmbeddedA11yManagerLacros::MaybeRemoveExtension(
-    Profile* profile,
-    const std::string& extension_id) {
-  auto* component_loader = GetComponentLoader(profile);
-  if (!component_loader || !component_loader->Exists(extension_id)) {
-    return;
-  }
-  component_loader->Remove(extension_id);
-  if (extension_installation_changed_callback_for_test_) {
-    extension_installation_changed_callback_for_test_.Run();
-  }
-}
-
-void EmbeddedA11yManagerLacros::MaybeInstallExtension(
-    Profile* profile,
-    const std::string& extension_id,
-    const std::string& extension_path,
-    const base::FilePath::CharType* manifest_name) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  auto* component_loader = GetComponentLoader(profile);
-  if (!component_loader || component_loader->Exists(extension_id)) {
-    return;
-  }
-
-  base::FilePath resources_path;
-  if (!base::PathService::Get(chrome::DIR_RESOURCES, &resources_path)) {
-    NOTREACHED();
-  }
-  auto path = resources_path.Append(extension_path);
-
-  extensions::GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&LoadManifestOnFileThread, path, manifest_name,
-                     /*localize=*/extension_id ==
-                         extension_misc::kEmbeddedA11yHelperExtensionId),
-      base::BindOnce(&EmbeddedA11yManagerLacros::InstallExtension,
-                     weak_ptr_factory_.GetWeakPtr(), component_loader, path,
-                     extension_id));
-}
-
-void EmbeddedA11yManagerLacros::InstallExtension(
-    extensions::ComponentLoader* component_loader,
-    const base::FilePath& path,
-    const std::string& extension_id,
-    std::optional<base::Value::Dict> manifest) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (component_loader->Exists(extension_id)) {
-    // Because this is async and called from another thread, it's possible we
-    // already installed the extension. Don't try and reinstall in that case.
-    // This may happen on init, for example, when ash a11y feature state and
-    // new profiles are loaded all at the same time.
-    return;
-  }
-
-  CHECK(manifest) << "Unable to load extension manifest for extension "
-                  << extension_id;
-  std::string actual_id =
-      component_loader->Add(std::move(manifest.value()), path);
-  CHECK_EQ(actual_id, extension_id);
-  if (extension_installation_changed_callback_for_test_) {
-    extension_installation_changed_callback_for_test_.Run();
-  }
+  UpdatePdfOcrEnabledOnAllProfiles();
 }
 
 void EmbeddedA11yManagerLacros::OnFocusChangedInPage(
@@ -387,3 +246,33 @@
     focus_changed_callback_for_test_.Run(details.node_bounds_in_screen);
   }
 }
+
+void EmbeddedA11yManagerLacros::UpdateEmbeddedA11yHelperExtension() {
+  // Switch Access and Select to Speak share a helper extension which has a
+  // manifest content script to tell Google Docs to annotate the HTML canvas.
+  if (select_to_speak_enabled_ || switch_access_enabled_) {
+    EmbeddedA11yExtensionLoader::GetInstance()->InstallExtensionWithId(
+        extension_misc::kEmbeddedA11yHelperExtensionId,
+        extension_misc::kEmbeddedA11yHelperExtensionPath,
+        extension_misc::kEmbeddedA11yHelperManifestFilename,
+        /*should_localize=*/true);
+  } else {
+    EmbeddedA11yExtensionLoader::GetInstance()->RemoveExtensionWithId(
+        extension_misc::kEmbeddedA11yHelperExtensionId);
+  }
+}
+
+void EmbeddedA11yManagerLacros::UpdateChromeVoxHelperExtension() {
+  // ChromeVox has a helper extension which has a content script to tell Google
+  // Docs that ChromeVox is enabled.
+  if (chromevox_enabled_) {
+    EmbeddedA11yExtensionLoader::GetInstance()->InstallExtensionWithId(
+        extension_misc::kChromeVoxHelperExtensionId,
+        extension_misc::kChromeVoxHelperExtensionPath,
+        extension_misc::kChromeVoxHelperManifestFilename,
+        /*should_localize=*/false);
+  } else {
+    EmbeddedA11yExtensionLoader::GetInstance()->RemoveExtensionWithId(
+        extension_misc::kChromeVoxHelperExtensionId);
+  }
+}
diff --git a/chrome/browser/lacros/embedded_a11y_manager_lacros.h b/chrome/browser/lacros/embedded_a11y_manager_lacros.h
index 5990644..db0a41b 100644
--- a/chrome/browser/lacros/embedded_a11y_manager_lacros.h
+++ b/chrome/browser/lacros/embedded_a11y_manager_lacros.h
@@ -21,16 +21,13 @@
 #include "content/public/browser/focused_node_details.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
-namespace extensions {
-class ComponentLoader;
-}
-
 class Profile;
 
-// Manages extensions and preferences in Lacros that support Accessibility
-// features running in Ash. Installs and uninstalls the extensions on every
-// profile (including guest and incognito) depending on which Ash accessibility
-// features are running and syncs the preferences on all profiles.
+// Manages preferences in Lacros that support Accessibility features running in
+// Ash. This class calls EmbeddedA11yExtensionLoader to install and uninstall
+// the extensions on every profile (including guest and incognito) depending on
+// which Ash accessibility features are running and syncs the preferences on all
+// profiles.
 class EmbeddedA11yManagerLacros
     : public crosapi::mojom::EmbeddedAccessibilityHelper,
       public ProfileObserver,
@@ -66,10 +63,6 @@
   void AddFocusChangedCallbackForTest(
       base::RepeatingCallback<void(gfx::Rect)> callback);
 
-  void SetReadingModeEnabled(bool enabled);
-
-  bool IsReadingModeEnabled();
-
  private:
   EmbeddedA11yManagerLacros();
   ~EmbeddedA11yManagerLacros() override;
@@ -82,8 +75,8 @@
   void OnProfileAdded(Profile* profile) override;
   void OnProfileManagerDestroying() override;
 
-  void UpdateAllProfiles();
-  void UpdateProfile(Profile* profile);
+  void UpdatePdfOcrEnabledOnAllProfiles();
+  void UpdatePdfOcrEnabledOnProfile(Profile* profile);
 
   void OnChromeVoxEnabledChanged(base::Value value);
   void OnSelectToSpeakEnabledChanged(base::Value value);
@@ -91,28 +84,13 @@
   void OnFocusHighlightEnabledChanged(base::Value value);
   void OnPdfOcrAlwaysActiveChanged(base::Value value);
 
-  // Removes the helper extension with `extension_id` from the given `profile`
-  // if it is installed.
-  void MaybeRemoveExtension(Profile* profile, const std::string& extension_id);
-
-  // Installs the helper extension with `extension_id` from the given `profile`
-  // if it isn't yet installed.
-  void MaybeInstallExtension(Profile* profile,
-                             const std::string& extension_id,
-                             const std::string& extension_path,
-                             const base::FilePath::CharType* manifest_name);
-
-  // Installs the helper extension with the given `extension_id`, `manifest` and
-  // `path` using the given `component_loader` for some profile.
-  void InstallExtension(extensions::ComponentLoader* component_loader,
-                        const base::FilePath& path,
-                        const std::string& extension_id,
-                        std::optional<base::Value::Dict> manifest);
-
   // Called when focus highlight feature is active and the focused node
   // changed.
   void OnFocusChangedInPage(const content::FocusedNodeDetails& details);
 
+  void UpdateEmbeddedA11yHelperExtension();
+  void UpdateChromeVoxHelperExtension();
+
   // Observers for Ash feature state.
   std::unique_ptr<CrosapiPrefObserver> chromevox_enabled_observer_;
   std::unique_ptr<CrosapiPrefObserver> select_to_speak_enabled_observer_;
@@ -124,10 +102,8 @@
   bool chromevox_enabled_ = false;
   bool select_to_speak_enabled_ = false;
   bool switch_access_enabled_ = false;
-  bool reading_mode_enabled_ = false;
   std::optional<bool> pdf_ocr_always_active_enabled_;
 
-  base::RepeatingClosure extension_installation_changed_callback_for_test_;
   base::RepeatingClosure speak_selected_text_callback_for_test_;
   base::RepeatingCallback<void(gfx::Rect)> focus_changed_callback_for_test_;
 
diff --git a/chrome/browser/lacros/embedded_a11y_manager_lacros_browsertest.cc b/chrome/browser/lacros/embedded_a11y_manager_lacros_browsertest.cc
index 6e71310..5179aea 100644
--- a/chrome/browser/lacros/embedded_a11y_manager_lacros_browsertest.cc
+++ b/chrome/browser/lacros/embedded_a11y_manager_lacros_browsertest.cc
@@ -10,6 +10,7 @@
 
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "chrome/browser/accessibility/embedded_a11y_extension_loader.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -129,10 +130,12 @@
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
 
-    auto* embedded_a11y_manager = EmbeddedA11yManagerLacros::GetInstance();
-    embedded_a11y_manager->AddExtensionChangedCallbackForTest(
+    auto* embedded_a11y_extension_loader =
+        EmbeddedA11yExtensionLoader::GetInstance();
+    embedded_a11y_extension_loader->AddExtensionChangedCallbackForTest(
         base::BindRepeating(&EmbeddedA11yManagerLacrosTest::OnExtensionChanged,
                             base::Unretained(this)));
+    auto* embedded_a11y_manager = EmbeddedA11yManagerLacros::GetInstance();
     embedded_a11y_manager->AddFocusChangedCallbackForTest(
         base::BindRepeating(&EmbeddedA11yManagerLacrosTest::OnFocusChanged,
                             base::Unretained(this)));
@@ -307,23 +310,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(EmbeddedA11yManagerLacrosTest,
-                       AddsAndRemovesHelperForReadingMode) {
-  ProfileManager* profile_manager = g_browser_process->profile_manager();
-  const auto& profiles = profile_manager->GetLoadedProfiles();
-  ASSERT_GT(profiles.size(), 0u);
-  Profile* profile = profiles[0];
-
-  auto* embedded_a11y_manager = EmbeddedA11yManagerLacros::GetInstance();
-  embedded_a11y_manager->SetReadingModeEnabled(true);
-  WaitForExtensionLoaded(profile,
-                         extension_misc::kEmbeddedA11yHelperExtensionId);
-
-  embedded_a11y_manager->SetReadingModeEnabled(false);
-  WaitForExtensionUnloaded(profile,
-                           extension_misc::kEmbeddedA11yHelperExtensionId);
-}
-
-IN_PROC_BROWSER_TEST_F(EmbeddedA11yManagerLacrosTest,
                        SwitchAccessAndSelectToSpeak) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   const auto& profiles = profile_manager->GetLoadedProfiles();
diff --git a/chrome/browser/media/webrtc/capture_policy_utils.cc b/chrome/browser/media/webrtc/capture_policy_utils.cc
index 55012ab..89608ec 100644
--- a/chrome/browser/media/webrtc/capture_policy_utils.cc
+++ b/chrome/browser/media/webrtc/capture_policy_utils.cc
@@ -141,7 +141,7 @@
 }
 
 bool IsGetAllScreensMediaAllowedForAnySite(content::BrowserContext* context) {
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_CHROMEOS)
   Profile* profile = Profile::FromBrowserContext(context);
   if (!profile) {
     return false;
@@ -176,7 +176,7 @@
 
 bool IsGetAllScreensMediaAllowed(content::BrowserContext* context,
                                  const GURL& url) {
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_CHROMEOS)
   Profile* profile = Profile::FromBrowserContext(context);
   if (!profile) {
     return false;
diff --git a/chrome/browser/media/webrtc/capture_policy_utils_browsertest.cc b/chrome/browser/media/webrtc/capture_policy_utils_browsertest.cc
index 94377ce..fbcc977 100644
--- a/chrome/browser/media/webrtc/capture_policy_utils_browsertest.cc
+++ b/chrome/browser/media/webrtc/capture_policy_utils_browsertest.cc
@@ -27,8 +27,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/origin.h"
 
-// TODO(crbug.com/1447824): Implement no-dynamic refresh (and a test) for
-// lacros.
 namespace {
 struct TestParam {
   std::vector<std::string> allow_listed_origins;
@@ -55,7 +53,6 @@
 
   void SetAllowedOriginsPolicy(
       const std::vector<std::string>& allow_listed_origins) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
     policy::PolicyMap policies;
     base::Value::List allowed_origins;
     for (const auto& allowed_origin : allow_listed_origins) {
@@ -66,7 +63,6 @@
         policy::key::kGetDisplayMediaSetSelectAllScreensAllowedForUrls,
         base::Value(std::move(allowed_origins)));
     provider_.UpdateChromePolicy(policies);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   }
 
   void SetUpInProcessBrowserTestFixture() override {
@@ -118,9 +114,7 @@
             .allow_listed_origins = {},
             .testing_url = "https://www.chromium.org",
             .expected_is_get_all_screens_media_allowed = false,
-        })
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-            ,
+        }),
         TestParam({
             .allow_listed_origins = {},
             .testing_url = "",
@@ -145,11 +139,8 @@
             .allow_listed_origins = {"[*.]chrome.org", "[*.]chromium.org"},
             .testing_url = "https://www.chromium.org",
             .expected_is_get_all_screens_media_allowed = true,
-        })
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-            ));
+        })));
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
 class SelectAllScreensDynamicRefreshTest
     : public SelectAllScreensTestBase,
       public testing::WithParamInterface<NoRefreshTestParam> {
@@ -157,7 +148,8 @@
   SelectAllScreensDynamicRefreshTest() = default;
   ~SelectAllScreensDynamicRefreshTest() override = default;
 
-  SelectAllScreensDynamicRefreshTest(const SelectAllScreensTest&) = delete;
+  explicit SelectAllScreensDynamicRefreshTest(const SelectAllScreensTest&) =
+      delete;
   SelectAllScreensDynamicRefreshTest& operator=(const SelectAllScreensTest&) =
       delete;
 
@@ -213,5 +205,3 @@
             .expected_allowed_origins = {},
             .expected_forbidden_origins = {},
         })));
-
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/media/webrtc/capture_policy_utils_unittest.cc b/chrome/browser/media/webrtc/capture_policy_utils_unittest.cc
index d5769b2..3bc5ea4 100644
--- a/chrome/browser/media/webrtc/capture_policy_utils_unittest.cc
+++ b/chrome/browser/media/webrtc/capture_policy_utils_unittest.cc
@@ -261,7 +261,7 @@
   EXPECT_EQ(expected_media_types, actual_media_types);
 }
 
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_CHROMEOS)
 
 class MultiCaptureTest
     : public testing::Test,
@@ -343,4 +343,4 @@
         ::testing::ValuesIn({std::string("https://www.google.com"),
                              std::string("https://www.notallowed.com")})));
 
-#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
+#endif  // BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
index ac67b02..735f4ec 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
+++ b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
@@ -36,21 +36,22 @@
       .capture_time = base::TimeTicks::Now()};
 }
 
-ProcessMonitor::Metrics GetFakeProcessMetrics() {
+ProcessMonitor::Metrics GetFakeProcessMetrics(bool with_cpu_usage = true) {
   ProcessMonitor::Metrics metrics;
-  metrics.cpu_usage = 5;
+  if (with_cpu_usage) {
+    metrics.cpu_usage = 5;
+  }
   return metrics;
 }
 
 struct HistogramSampleExpectation {
   std::string histogram_name_prefix;
-  base::Histogram::Sample sample;
+  std::optional<base::Histogram::Sample> sample;
 };
 
-#if !BUILDFLAG(IS_WIN) || !defined(ARCH_CPU_ARM64)
 // For each histogram named after the combination of prefixes from
 // `expectations` and suffixes from `suffixes`, verifies that there is a unique
-// sample `expectation.sample`.
+// sample `expectation.sample`, or no sample if `expectation.sample` is nullopt.
 void ExpectHistogramSamples(
     base::HistogramTester* histogram_tester,
     const std::vector<const char*>& suffixes,
@@ -60,12 +61,15 @@
       std::string histogram_name =
           base::StrCat({expectation.histogram_name_prefix, suffix});
       SCOPED_TRACE(histogram_name);
-      histogram_tester->ExpectUniqueSample(histogram_name, expectation.sample,
-                                           1);
+      if (expectation.sample.has_value()) {
+        histogram_tester->ExpectUniqueSample(histogram_name,
+                                             expectation.sample.value(), 1);
+      } else {
+        histogram_tester->ExpectTotalCount(histogram_name, 0);
+      }
     }
   }
 }
-#endif  // !BUILDFLAG(IS_WIN) || !defined(ARCH_CPU_ARM64)
 
 using UkmEntry = ukm::builders::PowerUsageScenariosIntervalData;
 
@@ -227,16 +231,48 @@
 
   task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
 
-// Windows ARM64 does not support Constant Rate TSC so
-// PerformanceMonitor.AverageCPU8.Total is not recorded there.
-#if !BUILDFLAG(IS_WIN) || !defined(ARCH_CPU_ARM64)
   const char* kScenarioSuffix = ".VideoCapture";
   const std::vector<const char*> suffixes({"", kScenarioSuffix});
+#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)
+  // Windows ARM64 does not support Constant Rate TSC so
+  // PerformanceMonitor.AverageCPU8.Total is not recorded there.
+  ExpectHistogramSamples(
+      &histogram_tester_, suffixes,
+      {{"PerformanceMonitor.AverageCPU8.Total", std::nullopt}});
+#else
   ExpectHistogramSamples(&histogram_tester_, suffixes,
                          {{"PerformanceMonitor.AverageCPU8.Total", 500}});
 #endif
 }
 
+TEST_F(PowerMetricsReporterWithoutBatteryLevelProviderUnitTest,
+       CPUTimeMissing) {
+  process_monitor_.SetMetricsToReturn(
+      GetFakeProcessMetrics(/*with_cpu_usage=*/false));
+
+  UsageScenarioDataStore::IntervalData interval_data;
+  interval_data.max_tab_count = 1;
+  interval_data.max_visible_window_count = 1;
+  interval_data.time_capturing_video = base::Seconds(1);
+  long_data_store_.SetIntervalDataToReturn(interval_data);
+
+  task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
+
+  const char* kScenarioSuffix = ".VideoCapture";
+  const std::vector<const char*> suffixes({"", kScenarioSuffix});
+#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)
+  // Windows ARM64 does not support Constant Rate TSC so
+  // PerformanceMonitor.AverageCPU8.Total is not recorded there.
+  ExpectHistogramSamples(
+      &histogram_tester_, suffixes,
+      {{"PerformanceMonitor.AverageCPU8.Total", std::nullopt}});
+#else
+  // Missing `cpu_usage` recorded as 0.
+  ExpectHistogramSamples(&histogram_tester_, suffixes,
+                         {{"PerformanceMonitor.AverageCPU8.Total", 0}});
+#endif
+}
+
 TEST_F(PowerMetricsReporterUnitTest, LongIntervalHistograms) {
   process_monitor_.SetMetricsToReturn(GetFakeProcessMetrics());
   battery_states_.push(MakeBatteryDischargingState(30));
@@ -249,11 +285,15 @@
 
   task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
 
-// Windows ARM64 does not support Constant Rate TSC so
-// PerformanceMonitor.AverageCPU8.Total is not recorded there.
-#if !BUILDFLAG(IS_WIN) || !defined(ARCH_CPU_ARM64)
   const char* kScenarioSuffix = ".VideoCapture";
   const std::vector<const char*> suffixes({"", kScenarioSuffix});
+#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)
+  // Windows ARM64 does not support Constant Rate TSC so
+  // PerformanceMonitor.AverageCPU8.Total is not recorded there.
+  ExpectHistogramSamples(
+      &histogram_tester_, suffixes,
+      {{"PerformanceMonitor.AverageCPU8.Total", std::nullopt}});
+#else
   ExpectHistogramSamples(&histogram_tester_, suffixes,
                          {{"PerformanceMonitor.AverageCPU8.Total", 500}});
 #endif
@@ -656,3 +696,26 @@
   test_ukm_recorder_.ExpectEntryMetric(
       entries[0], UkmEntry::kDeviceSleptDuringIntervalName, true);
 }
+
+TEST_F(PowerMetricsReporterUnitTest, UKMsWithoutCPU) {
+  process_monitor_.SetMetricsToReturn(
+      GetFakeProcessMetrics(/*with_cpu_usage=*/false));
+  battery_states_.push(MakeBatteryDischargingState(30));
+
+  UsageScenarioDataStore::IntervalData fake_interval_data = {};
+  fake_interval_data.source_id_for_longest_visible_origin =
+      ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
+  long_data_store_.SetIntervalDataToReturn(fake_interval_data);
+
+  task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
+
+  auto entries = test_ukm_recorder_.GetEntriesByName(
+      ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
+  ASSERT_EQ(1u, entries.size());
+
+  EXPECT_EQ(entries[0]->source_id,
+            fake_interval_data.source_id_for_longest_visible_origin);
+  // Missing `cpu_usage` should be skipped, not logged as 0% CPU.
+  EXPECT_FALSE(
+      test_ukm_recorder_.EntryHasMetric(entries[0], UkmEntry::kCPUTimeMsName));
+}
diff --git a/chrome/browser/net/log_net_log_browsertest.cc b/chrome/browser/net/log_net_log_browsertest.cc
index bbc3b50..0f1f4f38 100644
--- a/chrome/browser/net/log_net_log_browsertest.cc
+++ b/chrome/browser/net/log_net_log_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <string>
+#include <string_view>
 
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -115,7 +116,7 @@
     // was omitted, and not stripped when it was given a value of
     // IncludeSensitive
     bool include_cookies =
-        GetParam() && base::StringPiece(GetParam()) == "IncludeSensitive";
+        GetParam() && std::string_view(GetParam()) == "IncludeSensitive";
 
     if (include_cookies) {
       EXPECT_TRUE(file_contents.find("Set-Cookie: name=Good;Max-Age=3600") !=
diff --git a/chrome/browser/net/private_network_access_browsertest.cc b/chrome/browser/net/private_network_access_browsertest.cc
index ae11da6..851836d 100644
--- a/chrome/browser/net/private_network_access_browsertest.cc
+++ b/chrome/browser/net/private_network_access_browsertest.cc
@@ -4,10 +4,10 @@
 
 #include <map>
 #include <string>
+#include <string_view>
 
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
-#include "base/strings/string_piece.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/dom_distiller/tab_utils.h"
@@ -116,7 +116,7 @@
       "fetch($1).then(response => true).catch(error => false)", url);
 }
 
-std::string FetchWorkerScript(base::StringPiece relative_url) {
+std::string FetchWorkerScript(std::string_view relative_url) {
   constexpr char kTemplate[] = R"(
     new Promise((resolve) => {
       const worker = new Worker($1);
@@ -137,7 +137,7 @@
 // Instantiates a shared worker script from `path`.
 // If it loads successfully, the worker should post a message to each client
 // that connects to it to signal success.
-std::string FetchSharedWorkerScript(base::StringPiece path) {
+std::string FetchSharedWorkerScript(std::string_view path) {
   constexpr char kTemplate[] = R"(
     new Promise((resolve) => {
       const worker = new SharedWorker($1);
@@ -511,7 +511,7 @@
       feature_histogram_tester.GetNonZeroCounts(AllAddressSpaceFeatures()),
       IsEmpty());
 
-  base::StringPiece script_template = R"(
+  std::string_view script_template = R"(
     new Promise(resolve => {
       const child = document.createElement("iframe");
       child.src = $1;
@@ -547,7 +547,7 @@
       feature_histogram_tester.GetNonZeroCounts(AllAddressSpaceFeatures()),
       IsEmpty());
 
-  base::StringPiece script_template = R"(
+  std::string_view script_template = R"(
     function addChildFrame(doc, src) {
       return new Promise(resolve => {
         const child = doc.createElement("iframe");
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index e5064164..076cc0b 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/net/profile_network_context_service.h"
 
 #include <string>
+#include <string_view>
 
 #include "base/base64.h"
 #include "base/check_op.h"
@@ -680,7 +681,7 @@
     if (!base::Base64Decode(cert_b64.GetString(), &decoded)) {
       continue;
     }
-    base::StringPiece spki_piece;
+    std::string_view spki_piece;
     bool success = net::asn1::ExtractSPKIFromDERCert(decoded, &spki_piece);
     if (success) {
       additional_certificates->distrusted_spkis.emplace_back(spki_piece.begin(),
@@ -1236,6 +1237,9 @@
     network_context_params->enable_ip_protection =
         ipp_config_provider->IsIpProtectionEnabled();
   }
+
+  network_context_params->device_bound_sessions_enabled =
+      base::FeatureList::IsEnabled(net::features::kDeviceBoundSessions);
 }
 
 base::FilePath ProfileNetworkContextService::GetPartitionPath(
diff --git a/chrome/browser/net/profile_network_context_service_browsertest.cc b/chrome/browser/net/profile_network_context_service_browsertest.cc
index fc92a1a..fe496433 100644
--- a/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/check_op.h"
@@ -20,7 +21,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
@@ -772,8 +772,8 @@
   }
 
   void ProvideRequestHandlerKeyCommitmentsToNetworkService(
-      base::StringPiece host) {
-    base::flat_map<url::Origin, base::StringPiece> origins_and_commitments;
+      std::string_view host) {
+    base::flat_map<url::Origin, std::string_view> origins_and_commitments;
     std::string key_commitments = request_handler_.GetKeyCommitmentRecord();
 
     GURL::Replacements replacements;
diff --git a/chrome/browser/net/secure_dns_config.cc b/chrome/browser/net/secure_dns_config.cc
index 5dd10e2..0b23397a 100644
--- a/chrome/browser/net/secure_dns_config.cc
+++ b/chrome/browser/net/secure_dns_config.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/net/secure_dns_config.h"
 
+#include <string_view>
+
 // static
 constexpr char SecureDnsConfig::kModeOff[];
 constexpr char SecureDnsConfig::kModeAutomatic[];
@@ -21,7 +23,7 @@
 
 // static
 std::optional<net::SecureDnsMode> SecureDnsConfig::ParseMode(
-    base::StringPiece name) {
+    std::string_view name) {
   if (name == kModeSecure) {
     return net::SecureDnsMode::kSecure;
   } else if (name == kModeAutomatic) {
diff --git a/chrome/browser/net/secure_dns_config.h b/chrome/browser/net/secure_dns_config.h
index 0fe5355..daf5ea6 100644
--- a/chrome/browser/net/secure_dns_config.h
+++ b/chrome/browser/net/secure_dns_config.h
@@ -6,8 +6,8 @@
 #define CHROME_BROWSER_NET_SECURE_DNS_CONFIG_H_
 
 #include <optional>
+#include <string_view>
 
-#include "base/strings/string_piece.h"
 #include "net/dns/public/dns_over_https_config.h"
 #include "net/dns/public/secure_dns_mode.h"
 
@@ -43,7 +43,7 @@
 
   // Identifies the SecureDnsMode corresponding to one of the above names, or
   // returns nullopt if the name is unrecognized.
-  static std::optional<net::SecureDnsMode> ParseMode(base::StringPiece name);
+  static std::optional<net::SecureDnsMode> ParseMode(std::string_view name);
   // Converts a secure DNS mode to one of the above names.
   static const char* ModeToString(net::SecureDnsMode mode);
 
diff --git a/chrome/browser/net/secure_dns_policy_handler.cc b/chrome/browser/net/secure_dns_policy_handler.cc
index 4d243d0..b9db344 100644
--- a/chrome/browser/net/secure_dns_policy_handler.cc
+++ b/chrome/browser/net/secure_dns_policy_handler.cc
@@ -5,9 +5,9 @@
 #include "chrome/browser/net/secure_dns_policy_handler.h"
 
 #include <string>
+#include <string_view>
 
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
 #include "base/values.h"
 #include "chrome/browser/net/secure_dns_config.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
@@ -82,7 +82,7 @@
 bool CheckDnsOverHttpsTemplatePolicy(const policy::PolicyMap& policies,
                                      policy::PolicyErrorMap* errors,
                                      const std::string& policy_name,
-                                     const base::StringPiece& mode) {
+                                     std::string_view mode) {
   // It is safe to use `GetValueUnsafe()` because type checking is performed
   // before the value is used.
   const base::Value* templates = policies.GetValueUnsafe(policy_name);
@@ -128,7 +128,7 @@
   // It is safe to use `GetValueUnsafe()` because type checking is performed
   // before the value is used.
   const base::Value* mode = policies.GetValueUnsafe(key::kDnsOverHttpsMode);
-  base::StringPiece mode_str;
+  std::string_view mode_str;
   if (!mode) {
     mode_is_applicable = false;
   } else if (!mode->is_string()) {
@@ -192,7 +192,7 @@
                                                  PrefValueMap* prefs) {
   const base::Value* mode =
       policies.GetValue(key::kDnsOverHttpsMode, base::Value::Type::STRING);
-  base::StringPiece mode_str;
+  std::string_view mode_str;
   if (mode) {
     mode_str = mode->GetString();
     prefs->SetString(prefs::kDnsOverHttpsMode,
@@ -236,7 +236,7 @@
 
 bool SecureDnsPolicyHandler::IsTemplatesPolicyNotSpecified(
     bool is_templates_policy_valid,
-    base::StringPiece mode_str) {
+    std::string_view mode_str) {
   if (mode_str == SecureDnsConfig::kModeSecure)
     return !is_templates_policy_valid;
 
diff --git a/chrome/browser/net/secure_dns_policy_handler.h b/chrome/browser/net/secure_dns_policy_handler.h
index 55550194..c3f80e2 100644
--- a/chrome/browser/net/secure_dns_policy_handler.h
+++ b/chrome/browser/net/secure_dns_policy_handler.h
@@ -5,7 +5,8 @@
 #ifndef CHROME_BROWSER_NET_SECURE_DNS_POLICY_HANDLER_H_
 #define CHROME_BROWSER_NET_SECURE_DNS_POLICY_HANDLER_H_
 
-#include "base/strings/string_piece.h"
+#include <string_view>
+
 #include "build/chromeos_buildflags.h"
 #include "components/policy/core/browser/configuration_policy_handler.h"
 
@@ -34,7 +35,7 @@
   // Returns true if templates must be specified (i.e. `mode_str` is secure),
   // but they are not set or invalid (non-string).
   bool IsTemplatesPolicyNotSpecified(bool is_templates_policy_valid,
-                                     const base::StringPiece mode_str);
+                                     const std::string_view mode_str);
   // Indicates whether the DnsOverHttpsTemplates policy is valid and can be
   // applied. If not, the corresponding pref is not set. If the DNS mode is
   // secure, either `is_templates_policy_valid_` or, on Chrome OS only,
diff --git a/chrome/browser/pdf/pdf_extension_accessibility_test.cc b/chrome/browser/pdf/pdf_extension_accessibility_test.cc
index 776b98b..e3e1145 100644
--- a/chrome/browser/pdf/pdf_extension_accessibility_test.cc
+++ b/chrome/browser/pdf/pdf_extension_accessibility_test.cc
@@ -7,6 +7,7 @@
 #include <map>
 #include <set>
 #include <string>
+#include <string_view>
 #include <tuple>
 #include <variant>
 #include <vector>
@@ -17,7 +18,6 @@
 #include "base/logging.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -232,6 +232,79 @@
     "      image 'Unlabeled image'\n";
 #endif  // BUILDFLAG(IS_WIN)
 
+constexpr char kExpectedThreePagePDFAXTreeWithOcrResults[] =
+    "pdfRoot 'PDF document containing 3 pages'\n"
+    "  banner\n"
+    "    status 'This PDF is inaccessible. Text extracted, powered by Google "
+    "AI'\n"
+    "      staticText 'This PDF is inaccessible. Text extracted, powered by "
+    "Google AI'\n"
+    "  region 'Page 1'\n"
+    "    genericContainer\n"
+    "      region\n"
+    "        banner\n"
+    "          staticText 'Start of extracted text'\n"
+    "        paragraph\n"
+    "          staticText 'Hello, world!'\n"
+    "            inlineTextBox 'Hello, world! '\n"
+    "        contentInfo\n"
+    "          staticText 'End of extracted text'\n"
+    "  region 'Page 2'\n"
+    "    genericContainer\n"
+    "      region\n"
+    "        banner\n"
+    "          staticText 'Start of extracted text'\n"
+    "        paragraph\n"
+    "          staticText 'Paragraph 1 on Page 2'\n"
+    "            inlineTextBox 'Paragraph 1 on Page 2 '\n"
+    "        paragraph\n"
+    "          staticText 'Paragraph 2 on Page 2'\n"
+    "            inlineTextBox 'Paragraph 2 on Page 2 '\n"
+    "        contentInfo\n"
+    "          staticText 'End of extracted text'\n"
+    "  region 'Page 3'\n"
+    "    genericContainer\n"
+    "      region\n"
+    "        banner\n"
+    "          staticText 'Start of extracted text'\n"
+    "        paragraph\n"
+    "          staticText 'Paragraph 1 on Page 3'\n"
+    "            inlineTextBox 'Paragraph 1 on Page 3 '\n"
+    "        paragraph\n"
+    "          staticText 'Paragraph 2 on Page 3'\n"
+    "            inlineTextBox 'Paragraph 2 on Page 3 '\n"
+    "        contentInfo\n"
+    "          staticText 'End of extracted text'\n";
+
+constexpr char kExpectedThreePagePDFAXTreeWithoutOcrResults[] =
+    "pdfRoot 'PDF document containing 3 pages'\n"
+    "  banner\n"
+    "    status 'This PDF is inaccessible. Open context menu and turn on "
+    "\"extract text from PDF\"'\n"
+    "      staticText 'This PDF is inaccessible. Open context menu and turn on "
+    "\"extract text from PDF\"'\n"
+    "  region 'Page 1'\n"
+    "    paragraph\n"
+#if BUILDFLAG(IS_WIN)
+    "      image 'Unlabeled graphic'\n"
+#else   // BUILDFLAG(IS_WIN)
+    "      image 'Unlabeled image'\n"
+#endif  // BUILDFLAG(IS_WIN)
+    "  region 'Page 2'\n"
+    "    paragraph\n"
+#if BUILDFLAG(IS_WIN)
+    "      image 'Unlabeled graphic'\n"
+#else   // BUILDFLAG(IS_WIN)
+    "      image 'Unlabeled image'\n"
+#endif  // BUILDFLAG(IS_WIN)
+    "  region 'Page 3'\n"
+    "    paragraph\n"
+#if BUILDFLAG(IS_WIN)
+    "      image 'Unlabeled graphic'\n";
+#else   // BUILDFLAG(IS_WIN)
+    "      image 'Unlabeled image'\n";
+#endif  // BUILDFLAG(IS_WIN)
+
 constexpr char kExpectedBlankPDFAXTreeWithPdfOcr[] =
     "pdfRoot 'PDF document containing 1 page'\n"
     "  banner\n"
@@ -473,8 +546,16 @@
   EXPECT_EQ(ax::mojom::Role::kRegion, region->GetRole());
 }
 
+// TODO(crbug.com/330202391): Fix the flakiness on Windows.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_PdfAccessibilityContextMenuAction \
+  DISABLED_PdfAccessibilityContextMenuAction
+#else
+#define MAYBE_PdfAccessibilityContextMenuAction \
+  PdfAccessibilityContextMenuAction
+#endif  // BUILDFLAG(IS_WIN)
 IN_PROC_BROWSER_TEST_P(PDFExtensionAccessibilityTestWithOopifOverride,
-                       PdfAccessibilityContextMenuAction) {
+                       MAYBE_PdfAccessibilityContextMenuAction) {
   // TODO(crbug.com/324636880): Remove this once the test passes for OOPIF PDF.
   if (UseOopif()) {
     GTEST_SKIP();
@@ -729,7 +810,7 @@
 
       std::string name =
           node->GetStringAttribute(ax::mojom::StringAttribute::kName);
-      base::StringPiece trimmed_name =
+      std::string_view trimmed_name =
           base::TrimString(name, "\r\n", base::TRIM_TRAILING);
       int prev_id =
           node->GetIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId);
@@ -1449,6 +1530,27 @@
   ASSERT_MULTILINE_STREQ(expected_tree_dump, ax_tree_dump);
 }
 
+IN_PROC_BROWSER_TEST_P(PDFOCRIntegrationTest, ThreePagePDF) {
+  // Turn on PDF OCR by setting its pref to be true.
+  browser()->profile()->GetPrefs()->SetBoolean(
+      prefs::kAccessibilityPdfOcrAlwaysActive, true);
+
+  EXPECT_TRUE(LoadPdf(embedded_test_server()->GetURL(
+      "/pdf/accessibility/inaccessible-text-in-three-page.pdf")));
+
+  WaitForTreeStatus(IsOcrAvailable() ? IDS_PDF_OCR_COMPLETED
+                                     : IDS_PDF_OCR_FEATURE_ALERT);
+
+  ui::AXTreeUpdate ax_tree =
+      GetAccessibilityTreeSnapshotForPdf(GetActiveWebContents());
+  std::string ax_tree_dump =
+      DumpPdfAccessibilityTree(ax_tree, /*skip_status_subtree=*/false);
+  const char* expected_tree_dump =
+      IsOcrAvailable() ? kExpectedThreePagePDFAXTreeWithOcrResults
+                       : kExpectedThreePagePDFAXTreeWithoutOcrResults;
+  ASSERT_MULTILINE_STREQ(expected_tree_dump, ax_tree_dump);
+}
+
 IN_PROC_BROWSER_TEST_P(PDFOCRIntegrationTest, FeatureNotificationWhenOff) {
   // Turn off PDF OCR by setting its pref to be false.
   browser()->profile()->GetPrefs()->SetBoolean(
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 0833e6e6..5fd4c91 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -3556,6 +3556,69 @@
                                   kNestedWindowFramesUndefinedCheck));
 }
 
+// Tests that a data URL to a PDF loads successfully.
+IN_PROC_BROWSER_TEST_F(PDFExtensionOopifTest, LoadDataUrlPdfFullPage) {
+  // The data URL to load a simple PDF page.
+  const char kDataUrlPdfFullPage[] =
+      "data:application/pdf;base64,"
+      "JVBERi0xLjcKJaDypPQKMSAwIG9iaiA8PAogIC9UeXBlIC9DYXRhbG9nCiAgL1BhZ2VzIDIg"
+      "MCBSCj4+CmVuZG9iagoyIDAgb2JqIDw8CiAgL1R5cGUgL1BhZ2VzCiAgL0NvdW50IDEKICAv"
+      "S2lkcyBbMyAwIFJdCiAgL1Jlc291cmNlcyA8PCA+Pgo+PgplbmRvYmoKMyAwIG9iaiA8PAog"
+      "IC9UeXBlIC9QYWdlIAogIC9QYXJlbnQgMiAwIFIKICAvTWVkaWFCb3ggWzAgMCAxMDAgNTBd"
+      "Cj4+CmVuZG9iagp4cmVmCjAgNAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTUgMDAw"
+      "MDAgbiAKMDAwMDAwMDA2OCAwMDAwMCBuIAowMDAwMDAwMTUwIDAwMDAwIG4gCnRyYWlsZXIg"
+      "PDwKICAvUm9vdCAxIDAgUgogIC9TaXplIDQKPj4Kc3RhcnR4cmVmCjIyNwolJUVPRgo=";
+  EXPECT_TRUE(LoadPdf(GURL(kDataUrlPdfFullPage)));
+}
+
+// Tests that a data URL to a HTML page embedding a PDF in an embed loads
+// successfully.
+IN_PROC_BROWSER_TEST_F(PDFExtensionOopifTest, LoadDataUrlPdfEmbed) {
+  const std::string url =
+      embedded_test_server()->GetURL("/pdf/test.pdf").spec();
+  const std::string data_url =
+      "data:text/html,"
+      "<html><body>"
+      "<embed type=\"application/pdf\" src=\"" +
+      url +
+      "\">"
+      "</body></html>";
+  EXPECT_TRUE(LoadPdfInFirstChild(GURL(data_url)));
+}
+
+// Tests that a data URL to a HTML page embedding a PDF in an iframe loads
+// successfully.
+IN_PROC_BROWSER_TEST_F(PDFExtensionOopifTest, LoadDataUrlPdfIframe) {
+  const std::string url =
+      embedded_test_server()->GetURL("/pdf/test.pdf").spec();
+  const std::string data_url =
+      "data:text/html,"
+      "<html><body>"
+      "<iframe type=\"application/pdf\" src=\"" +
+      url +
+      "\">"
+      "</body></html>";
+  EXPECT_TRUE(LoadPdfInFirstChild(GURL(data_url)));
+}
+
+// If the document.body of the PDF viewer is replaced, there should no longer
+// be a PDF stream.
+IN_PROC_BROWSER_TEST_F(PDFExtensionOopifTest, ReplaceDocumentBody) {
+  ASSERT_TRUE(LoadPdf(embedded_test_server()->GetURL("/pdf/test.pdf")));
+  EXPECT_TRUE(
+      pdf::PdfViewerStreamManager::FromWebContents(GetActiveWebContents()));
+
+  // Replace the document.body. The embedder RFH will stay, but the extension
+  // and content RFH will be deleted.
+  EXPECT_TRUE(
+      content::ExecJs(GetActiveWebContents(),
+                      "document.body = document.createElement('body');"));
+
+  // The stream should no longer exist.
+  EXPECT_FALSE(
+      pdf::PdfViewerStreamManager::FromWebContents(GetActiveWebContents()));
+}
+
 // TODO(crbug.com/1445746): Stop testing both modes after OOPIF PDF viewer
 // launches.
 INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(PDFExtensionTest);
diff --git a/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc b/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc
index 8a6c97b..f0f767b 100644
--- a/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc
+++ b/chrome/browser/pdf/pdf_find_request_manager_browsertest.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 <string_view>
+
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/memory/raw_ptr.h"
@@ -138,8 +140,8 @@
   {
     auto it = response->http_request()->headers.find("Range");
     ASSERT_NE(response->http_request()->headers.end(), it);
-    base::StringPiece range_header = it->second;
-    base::StringPiece kBytesPrefix = "bytes=";
+    std::string_view range_header = it->second;
+    std::string_view kBytesPrefix = "bytes=";
     ASSERT_TRUE(base::StartsWith(range_header, kBytesPrefix));
     range_header.remove_prefix(kBytesPrefix.size());
     auto dash_pos = range_header.find('-');
diff --git a/chrome/browser/pdf/pdf_viewer_stream_manager.cc b/chrome/browser/pdf/pdf_viewer_stream_manager.cc
index 5c6f510..04a5fa92 100644
--- a/chrome/browser/pdf/pdf_viewer_stream_manager.cc
+++ b/chrome/browser/pdf/pdf_viewer_stream_manager.cc
@@ -14,6 +14,8 @@
 #include "base/functional/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/pdf/pdf_frame_util.h"
+#include "chrome/common/pdf_util.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -173,6 +175,48 @@
   return stream_info->stream()->GetWeakPtr();
 }
 
+bool PdfViewerStreamManager::IsPdfExtensionHost(
+    content::RenderFrameHost* render_frame_host) {
+  // The PDF extension host should always have a parent host.
+  content::RenderFrameHost* parent_host = render_frame_host->GetParent();
+  if (!parent_host) {
+    return false;
+  }
+
+  // The parent host should always be the PDF embedder host.
+  auto* stream_info = GetClaimedStreamInfo(parent_host);
+  if (!stream_info) {
+    return false;
+  }
+
+  return render_frame_host->GetFrameTreeNodeId() ==
+         stream_info->extension_host_frame_tree_node_id();
+}
+
+bool PdfViewerStreamManager::IsPdfContentHost(
+    content::RenderFrameHost* render_frame_host) {
+  // The PDF content host should always have a parent host.
+  content::RenderFrameHost* parent_host = render_frame_host->GetParent();
+  if (!parent_host) {
+    return false;
+  }
+
+  // The parent host should always be the PDF extension host.
+  if (!IsPdfExtensionHost(parent_host)) {
+    return false;
+  }
+
+  // The PDF extension host should always have a parent host (the embedder
+  // host).
+  content::RenderFrameHost* embedder_host = parent_host->GetParent();
+  CHECK(embedder_host);
+  auto* stream_info = GetClaimedStreamInfo(embedder_host);
+  CHECK(stream_info);
+
+  return render_frame_host->GetFrameTreeNodeId() ==
+         stream_info->content_host_frame_tree_node_id();
+}
+
 bool PdfViewerStreamManager::PluginCanSave(
     content::RenderFrameHost* embedder_host) {
   auto* stream_info = GetClaimedStreamInfo(embedder_host);
@@ -205,7 +249,7 @@
 
 void PdfViewerStreamManager::RenderFrameDeleted(
     content::RenderFrameHost* render_frame_host) {
-  // When the PDF embeder frame is deleted, delete its stream.
+  // When the PDF embedder frame is deleted, delete its stream.
   if (GetClaimedStreamInfo(render_frame_host)) {
     DeleteClaimedStreamInfo(render_frame_host);
     // DO NOT add code past this point. `this` may have been deleted.
@@ -241,6 +285,12 @@
     return;
   }
 
+  if (MaybeDeleteStreamOnPdfExtensionHostChanged(old_host) ||
+      MaybeDeleteStreamOnPdfContentHostChanged(old_host)) {
+    // DO NOT add code past this point. `this` may have been deleted.
+    return;
+  }
+
   // If this is an unrelated host, ignore.
   if (!GetClaimedStreamInfo(old_host)) {
     return;
@@ -256,10 +306,22 @@
 }
 
 void PdfViewerStreamManager::FrameDeleted(int frame_tree_node_id) {
-  // If an embedder host is deleted, delete the associated `StreamInfo`.
+  // If a PDF host is deleted, delete the associated `StreamInfo`.
   for (auto iter = stream_infos_.begin(); iter != stream_infos_.end();) {
-    if (iter->first.frame_tree_node_id == frame_tree_node_id) {
-      StreamInfo* stream_info = iter->second.get();
+    StreamInfo* stream_info = iter->second.get();
+    // Check if `frame_tree_node_id` is a PDF host's frame tree node ID.
+    //
+    // Deleting the stream for the extension host and the content host here
+    // should be almost equivalent to how
+    // `MaybeDeleteStreamOnPdfExtensionHostChanged()` and
+    // `MaybeDeleteStreamOnPdfContentHostChanged()` delete the stream. However,
+    // there is only a frame tree node ID here and not a
+    // `content::RenderFrameHost`, so deleting the stream requires iterating
+    // over all `StreamInfo` instances.
+    if (frame_tree_node_id == iter->first.frame_tree_node_id ||
+        frame_tree_node_id ==
+            stream_info->extension_host_frame_tree_node_id() ||
+        frame_tree_node_id == stream_info->content_host_frame_tree_node_id()) {
       if (stream_info->mime_handler_view_container_manager()) {
         stream_info->mime_handler_view_container_manager()
             ->DestroyFrameContainer(stream_info->instance_id());
@@ -278,6 +340,18 @@
   }
 }
 
+void PdfViewerStreamManager::DidStartNavigation(
+    content::NavigationHandle* navigation_handle) {
+  // Set the content host frame tree node ID if the navigation is for a content
+  // host. This needs to occur before the network request for the PDF content
+  // navigation so that
+  // `ChromePdfStreamDelegate::ShouldAllowPdfFrameNavigation()` can properly
+  // check that the navigation is allowed.
+  if (navigation_handle->IsPdf()) {
+    SetStreamContentHostFrameTreeNodeId(navigation_handle);
+  }
+}
+
 void PdfViewerStreamManager::ReadyToCommitNavigation(
     content::NavigationHandle* navigation_handle) {
   // Maybe register a PDF subresource override in the PDF content host.
@@ -343,9 +417,17 @@
     return;
   }
 
+  // `about_blank_host`'s FrameTreeNode will be reused for the extension
+  // `content::RenderFrameHost`, so it is safe to set it in `stream_info` to
+  // identify both hosts.
+  int extension_host_frame_tree_node_id =
+      about_blank_host->GetFrameTreeNodeId();
+  stream_info->set_extension_host_frame_tree_node_id(
+      extension_host_frame_tree_node_id);
+
   content::NavigationController::LoadURLParams params(
       stream_info->stream()->handler_url());
-  params.frame_tree_node_id = about_blank_host->GetFrameTreeNodeId();
+  params.frame_tree_node_id = extension_host_frame_tree_node_id;
   params.source_site_instance = embedder_host->GetSiteInstance();
   web_contents()->GetController().LoadURLWithParams(params);
 
@@ -357,6 +439,24 @@
   ClaimStreamInfo(embedder_host);
 }
 
+void PdfViewerStreamManager::SetExtensionFrameTreeNodeIdForTesting(
+    content::RenderFrameHost* embedder_host,
+    int frame_tree_node_id) {
+  auto* stream_info = GetClaimedStreamInfo(embedder_host);
+  CHECK(stream_info);
+
+  stream_info->set_extension_host_frame_tree_node_id(frame_tree_node_id);
+}
+
+void PdfViewerStreamManager::SetContentFrameTreeNodeIdForTesting(
+    content::RenderFrameHost* embedder_host,
+    int frame_tree_node_id) {
+  auto* stream_info = GetClaimedStreamInfo(embedder_host);
+  CHECK(stream_info);
+
+  stream_info->set_content_host_frame_tree_node_id(frame_tree_node_id);
+}
+
 PdfViewerStreamManager::StreamInfo*
 PdfViewerStreamManager::GetClaimedStreamInfo(
     content::RenderFrameHost* embedder_host) {
@@ -425,6 +525,58 @@
   }
 }
 
+bool PdfViewerStreamManager::MaybeDeleteStreamOnPdfExtensionHostChanged(
+    content::RenderFrameHost* old_host) {
+  if (!IsPdfExtensionHost(old_host)) {
+    return false;
+  }
+
+  // In a PDF load, the initial RFH for the PDF extension frame commits an
+  // initial about:blank URL. Don't delete the stream when this RFH changes.
+  // Another RFH will be chosen to host the PDF extension, with the PDF
+  // extension URL.
+  if (old_host->GetLastCommittedURL().IsAboutBlank()) {
+    return false;
+  }
+
+  content::RenderFrameHost* embedder_host = old_host->GetParent();
+  CHECK(embedder_host);
+
+  DeleteClaimedStreamInfo(embedder_host);
+  // DO NOT add code past this point. `this` may have been deleted.
+
+  return true;
+}
+
+bool PdfViewerStreamManager::MaybeDeleteStreamOnPdfContentHostChanged(
+    content::RenderFrameHost* old_host) {
+  if (!IsPdfContentHost(old_host)) {
+    return false;
+  }
+
+  content::RenderFrameHost* embedder_host =
+      pdf_frame_util::GetEmbedderHost(old_host);
+  CHECK(embedder_host);
+  auto* stream_info = GetClaimedStreamInfo(embedder_host);
+
+  // In a PDF load, the initial RFH for the PDF content frame is created for the
+  // navigation to the PDF stream URL. This navigation is canceled in
+  // `pdf::PdfNavigationThrottle::WillStartRequest()` and never commits. The
+  // initial RFH and the actual PDF content RFH have the same frame tree node
+  // ID, but the actual PDF content RFH commits its navigation to the original
+  // PDF URL. Don't delete the stream when the initial RFH changes.
+  const GURL& url = old_host->GetLastCommittedURL();
+  if (url.is_empty()) {
+    return false;
+  }
+  CHECK(url == stream_info->stream()->original_url());
+
+  DeleteClaimedStreamInfo(embedder_host);
+  // DO NOT add code past this point. `this` may have been deleted.
+
+  return true;
+}
+
 bool PdfViewerStreamManager::MaybeRegisterPdfSubresourceOverride(
     content::NavigationHandle* navigation_handle) {
   // Only register the subresource override if `navigation_handle` is for the
@@ -493,6 +645,15 @@
   return true;
 }
 
+void PdfViewerStreamManager::SetStreamContentHostFrameTreeNodeId(
+    content::NavigationHandle* navigation_handle) {
+  auto* claimed_stream_info =
+      GetClaimedStreamInfoFromPdfContentNavigation(navigation_handle);
+  CHECK(claimed_stream_info);
+  claimed_stream_info->set_content_host_frame_tree_node_id(
+      navigation_handle->GetFrameTreeNodeId());
+}
+
 void PdfViewerStreamManager::SetUpBeforeUnloadControl(
     mojo::PendingRemote<extensions::mime_handler::BeforeUnloadControl>
         before_unload_control_remote) {
diff --git a/chrome/browser/pdf/pdf_viewer_stream_manager.h b/chrome/browser/pdf/pdf_viewer_stream_manager.h
index 0133c65..28e2cc0b 100644
--- a/chrome/browser/pdf/pdf_viewer_stream_manager.h
+++ b/chrome/browser/pdf/pdf_viewer_stream_manager.h
@@ -38,8 +38,8 @@
 // PDF navigation events in a `content::WebContents`. It handles multiple PDF
 // viewer instances in a single `content::WebContents`. It is responsible for:
 // 1. Storing the `extensions::StreamContainer` PDF data.
-// 2. Observing for the PDF embedder RFH either navigating or closing (including
-//    by crashing). This is necessary to ensure that streams that aren't claimed
+// 2. Observing for the PDF frames either navigating or closing (including by
+//    crashing). This is necessary to ensure that streams that aren't claimed
 //    are not leaked, by deleting the stream if any of those events occur.
 // 3. Observing for the RFH created by the PDF embedder RFH to load the PDF
 //    extension URL.
@@ -125,6 +125,22 @@
   base::WeakPtr<extensions::StreamContainer> GetStreamContainer(
       content::RenderFrameHost* embedder_host);
 
+  // Returns true if `render_frame_host` is an extension host for a PDF. During
+  // a PDF load, the initial RFH for the extension frame commits to the
+  // about:blank URL. Another RFH will then be chosen to host the extension.
+  // This returns true for both hosts. Depending on what navigation step the
+  // frame is on, callers can also check the last committed origin to
+  // differentiate between the hosts.
+  bool IsPdfExtensionHost(content::RenderFrameHost* render_frame_host);
+
+  // Returns true if `render_frame_host` is a content host for a PDF. During a
+  // PDF load, the initial RFH for the content frame attempts to navigate to the
+  // stream URL. Another RFH will then be chosen to host the content frame. This
+  // returns true for both hosts. Depending on what navigation step the frame is
+  // on, callers can also check the last committed URL to differentiate between
+  // the hosts.
+  bool IsPdfContentHost(content::RenderFrameHost* render_frame_host);
+
   // Returns whether the PDF plugin should handle save events.
   bool PluginCanSave(content::RenderFrameHost* embedder_host);
 
@@ -141,6 +157,8 @@
   void RenderFrameHostChanged(content::RenderFrameHost* old_host,
                               content::RenderFrameHost* new_host) override;
   void FrameDeleted(int frame_tree_node_id) override;
+  void DidStartNavigation(
+      content::NavigationHandle* navigation_handle) override;
   void ReadyToCommitNavigation(
       content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
@@ -151,6 +169,20 @@
   // ensure such a stream info exists before calling this.
   void ClaimStreamInfoForTesting(content::RenderFrameHost* embedder_host);
 
+  // For testing only. Set `embedder_host`'s extension frame tree node ID as
+  // `frame_tree_node_id`. This is needed to listen for extension host deletion.
+  // Callers must ensure that `embedder_host` has a claimed stream info.
+  void SetExtensionFrameTreeNodeIdForTesting(
+      content::RenderFrameHost* embedder_host,
+      int frame_tree_node_id);
+
+  // For testing only. Set `embedder_host`'s content frame tree node ID as
+  // `frame_tree_node_id`. This is needed to listen for content host deletion.
+  // Callers must ensure that `embedder_host` has a claimed stream info.
+  void SetContentFrameTreeNodeIdForTesting(
+      content::RenderFrameHost* embedder_host,
+      int frame_tree_node_id);
+
  protected:
   // Stream container stored for a single PDF navigation.
   class StreamInfo {
@@ -188,6 +220,22 @@
 
     bool DidPdfContentNavigate() const;
 
+    int extension_host_frame_tree_node_id() const {
+      return extension_host_frame_tree_node_id_;
+    }
+
+    void set_extension_host_frame_tree_node_id(int frame_tree_node_id) {
+      extension_host_frame_tree_node_id_ = frame_tree_node_id;
+    }
+
+    int content_host_frame_tree_node_id() const {
+      return content_host_frame_tree_node_id_;
+    }
+
+    void set_content_host_frame_tree_node_id(int frame_tree_node_id) {
+      content_host_frame_tree_node_id_ = frame_tree_node_id;
+    }
+
     bool plugin_can_save() const { return plugin_can_save_; }
 
     void set_plugin_can_save(bool plugin_can_save) {
@@ -212,6 +260,14 @@
     mojo::AssociatedRemote<extensions::mojom::MimeHandlerViewContainerManager>
         container_manager_;
 
+    // The frame tree node ID of the extension host. Initialized when the
+    // initial about:blank navigation commits in the extension frame.
+    int extension_host_frame_tree_node_id_ = 0;
+
+    // The frame tree node ID of the content host. Initialized when the
+    // navigation to the stream URL starts.
+    int content_host_frame_tree_node_id_ = 0;
+
     // A unique ID for this instance. Used for postMessage support to identify
     // `extensions::MimeHandlerViewFrameContainer` objects.
     int32_t instance_id_;
@@ -252,6 +308,19 @@
   // deletes `this` if there are no remaining stream infos.
   void DeleteClaimedStreamInfo(content::RenderFrameHost* embedder_host);
 
+  // Intended to be called when a RenderFrameHost in the observed
+  // `content::WebContents` is replaced or deleted. If `render_frame_host` is a
+  // deleted PDF extension host, then delete the stream. Deletes `this` if there
+  // are no remaining streams. Returns true if the stream was deleted, false
+  // otherwise.
+  [[nodiscard]] bool MaybeDeleteStreamOnPdfExtensionHostChanged(
+      content::RenderFrameHost* old_host);
+
+  // Same as `MaybeDeleteStreamOnPdfExtensionHostChanged()`, but for the content
+  // host.
+  [[nodiscard]] bool MaybeDeleteStreamOnPdfContentHostChanged(
+      content::RenderFrameHost* old_host);
+
   // Intended to be called during the PDF content frame's
   // `ReadyToCommitNavigation()` event. Registers navigations occurring in a PDF
   // content frame as a subresource.
@@ -263,6 +332,11 @@
   // extension frame after the PDF has finished loading.
   bool MaybeSetUpPostMessage(content::NavigationHandle* navigation_handle);
 
+  // During the PDF content frame navigation, set the related PDF stream's
+  // content host frame tree node ID.
+  void SetStreamContentHostFrameTreeNodeId(
+      content::NavigationHandle* navigation_handle);
+
   // Sets up beforeunload API support for full-page PDF viewers.
   // TODO(crbug.com/1445746): Currently a no-op. Support the beforeunload API.
   void SetUpBeforeUnloadControl(
diff --git a/chrome/browser/pdf/pdf_viewer_stream_manager_unittest.cc b/chrome/browser/pdf/pdf_viewer_stream_manager_unittest.cc
index e58932c..43eeaca 100644
--- a/chrome/browser/pdf/pdf_viewer_stream_manager_unittest.cc
+++ b/chrome/browser/pdf/pdf_viewer_stream_manager_unittest.cc
@@ -7,15 +7,18 @@
 #include <memory>
 
 #include "base/memory/weak_ptr.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/pdf/pdf_test_util.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/mock_navigation_handle.h"
 #include "content/public/test/navigation_simulator.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
+#include "pdf/pdf_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/loader/transferrable_url_loader.mojom.h"
@@ -35,6 +38,12 @@
 
 class PdfViewerStreamManagerTest : public ChromeRenderViewHostTestHarness {
  protected:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    feature_list_.InitAndEnableFeature(chrome_pdf::features::kPdfOopif);
+  }
+
   void TearDown() override {
     ChromeRenderViewHostTestHarness::web_contents()->RemoveUserData(
         PdfViewerStreamManager::UserDataKey());
@@ -69,6 +78,9 @@
     parent_host_tester->InitializeRenderFrameIfNeeded();
     return parent_host_tester->AppendChild(frame_name);
   }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 // Verify adding and getting an `extensions::StreamContainer`.
@@ -189,6 +201,102 @@
   EXPECT_TRUE(pdf_viewer_stream_manager());
 }
 
+// `PdfViewerStreamManager::IsPdfExtensionHost()` should correctly identify the
+// PDF extension hosts.
+TEST_F(PdfViewerStreamManagerTest, IsPdfExtensionHost) {
+  auto* embedder_host = CreateChildRenderFrameHost(main_rfh(), "embedder host");
+  embedder_host = NavigateAndCommit(embedder_host, GURL(kOriginalUrl1));
+
+  // In a PDF load, there's an RFH for the extension frame for the initial
+  // about:blank navigation. This RFH will always be replaced by
+  // `extension_host`.
+  auto* about_blank_host =
+      CreateChildRenderFrameHost(embedder_host, "extension host");
+  auto* extension_host =
+      CreateChildRenderFrameHost(embedder_host, "extension host");
+  auto* other_host = CreateChildRenderFrameHost(embedder_host, "other host");
+
+  PdfViewerStreamManager* manager = pdf_viewer_stream_manager();
+  manager->AddStreamContainer(embedder_host->GetFrameTreeNodeId(),
+                              "internal_id",
+                              pdf_test_util::GenerateSampleStreamContainer(1));
+  manager->ClaimStreamInfoForTesting(embedder_host);
+  ASSERT_TRUE(manager->GetStreamContainer(embedder_host));
+
+  // `about_blank_host` and `extension_host` should have the same frame tree
+  // node ID, but this isn't possible with the current test infrastructure. For
+  // testing purposes, it's okay to set the extension frame tree node ID to the
+  // initial RFH.
+  manager->SetExtensionFrameTreeNodeIdForTesting(
+      embedder_host, about_blank_host->GetFrameTreeNodeId());
+
+  // `about_blank_host` should be considered a PDF content host, even if it
+  // isn't navigating to the original PDF URL.
+  EXPECT_TRUE(manager->IsPdfExtensionHost(about_blank_host));
+
+  // Now, set the extension frame tree node ID to the actual extension frame
+  // tree node ID, which will be the same ID as `about_blank_host` in real
+  // situations.
+  manager->SetExtensionFrameTreeNodeIdForTesting(
+      embedder_host, extension_host->GetFrameTreeNodeId());
+
+  EXPECT_TRUE(manager->IsPdfExtensionHost(extension_host));
+
+  // Unrelated hosts shouldn't be considered PDF content hosts.
+  EXPECT_FALSE(manager->IsPdfExtensionHost(other_host));
+}
+
+// `PdfViewerStreamManager::IsPdfContentHost()` should correctly identify the
+// PDF content hosts.
+TEST_F(PdfViewerStreamManagerTest, IsPdfContentHost) {
+  const GURL pdf_url = GURL(kOriginalUrl1);
+
+  auto* embedder_host = CreateChildRenderFrameHost(main_rfh(), "embedder host");
+  embedder_host = NavigateAndCommit(embedder_host, pdf_url);
+  auto* extension_host =
+      CreateChildRenderFrameHost(embedder_host, "extension host");
+
+  // In a PDF load, there's an RFH for the content frame for the initial
+  // stream URL navigation. This RFH will always be replaced by
+  // `content_host`.
+  auto* stream_url_host =
+      CreateChildRenderFrameHost(extension_host, "content host");
+  auto* content_host =
+      CreateChildRenderFrameHost(extension_host, "content host");
+  content_host = NavigateAndCommit(content_host, pdf_url);
+  auto* other_host = CreateChildRenderFrameHost(extension_host, "other host");
+
+  PdfViewerStreamManager* manager = pdf_viewer_stream_manager();
+  manager->AddStreamContainer(embedder_host->GetFrameTreeNodeId(),
+                              "internal_id",
+                              pdf_test_util::GenerateSampleStreamContainer(1));
+  manager->ClaimStreamInfoForTesting(embedder_host);
+  manager->SetExtensionFrameTreeNodeIdForTesting(
+      embedder_host, extension_host->GetFrameTreeNodeId());
+  ASSERT_TRUE(manager->GetStreamContainer(embedder_host));
+
+  // `stream_url_host` and `content_host` should have the same frame tree node
+  // ID, but this isn't possible with the current test infrastructure. For
+  // testing purposes, it's okay to set the content frame tree node ID to the
+  // initial RFH.
+  manager->SetContentFrameTreeNodeIdForTesting(
+      embedder_host, stream_url_host->GetFrameTreeNodeId());
+
+  // `stream_url_host` should be considered a PDF content host, even if it isn't
+  // navigating to the original PDF URL.
+  EXPECT_TRUE(manager->IsPdfContentHost(stream_url_host));
+
+  // Now, set the content frame tree node ID to the actual content frame tree
+  // node ID, which will be the same as `stream_url_host` in real situations.
+  manager->SetContentFrameTreeNodeIdForTesting(
+      embedder_host, content_host->GetFrameTreeNodeId());
+
+  EXPECT_TRUE(manager->IsPdfContentHost(content_host));
+
+  // Unrelated hosts shouldn't be considered PDF content hosts.
+  EXPECT_FALSE(manager->IsPdfContentHost(other_host));
+}
+
 // If multiple `extensions::StreamContainer`s exist, then deleting one stream
 // shouldn't delete the other stream.
 TEST_F(PdfViewerStreamManagerTest, DeleteWithMultipleStreamContainers) {
@@ -285,7 +393,7 @@
 
 // If the `content::RenderFrameHost` for the stream changes, then the stream
 // should be deleted.
-TEST_F(PdfViewerStreamManagerTest, RenderFrameHostChanged) {
+TEST_F(PdfViewerStreamManagerTest, EmbedderRenderFrameHostChanged) {
   content::RenderFrameHost* old_host =
       NavigateAndCommit(main_rfh(), GURL(kOriginalUrl1));
   auto* new_host = CreateChildRenderFrameHost(old_host, "new host");
@@ -311,9 +419,124 @@
   EXPECT_FALSE(pdf_viewer_stream_manager());
 }
 
+// If the PDF extension host changes to a different host, the stream should be
+// deleted.
+TEST_F(PdfViewerStreamManagerTest, ExtensionRenderFrameHostChanged) {
+  auto* embedder_host = CreateChildRenderFrameHost(main_rfh(), "embedder host");
+  embedder_host = NavigateAndCommit(embedder_host, GURL(kOriginalUrl1));
+
+  // In a PDF load, there's an RFH for the extension frame for the initial
+  // about:blank navigation. This RFH will always be replaced by
+  // `extension_host` and shouldn't trigger stream deletion. Both hosts should
+  // share the same frame name.
+  auto* about_blank_host =
+      CreateChildRenderFrameHost(embedder_host, "extension host");
+  about_blank_host = NavigateAndCommit(about_blank_host, GURL("about:blank"));
+  auto* extension_host =
+      CreateChildRenderFrameHost(embedder_host, "extension host");
+  auto* new_host = CreateChildRenderFrameHost(embedder_host, "new host");
+
+  PdfViewerStreamManager* manager = pdf_viewer_stream_manager();
+  manager->AddStreamContainer(embedder_host->GetFrameTreeNodeId(),
+                              "internal_id",
+                              pdf_test_util::GenerateSampleStreamContainer(1));
+  manager->ClaimStreamInfoForTesting(embedder_host);
+  ASSERT_TRUE(manager->GetStreamContainer(embedder_host));
+
+  // `about_blank_host` and `extension_host` should have the same frame tree
+  // node ID, but this isn't possible with the current test infrastructure. For
+  // testing purposes, it's okay to set the extension frame tree node ID to the
+  // initial RFH.
+  manager->SetExtensionFrameTreeNodeIdForTesting(
+      embedder_host, about_blank_host->GetFrameTreeNodeId());
+
+  // Changing `about_blank_host` to `extension_host` shouldn't delete the
+  // stream.
+  manager->RenderFrameHostChanged(about_blank_host, extension_host);
+
+  ASSERT_TRUE(pdf_viewer_stream_manager());
+  EXPECT_TRUE(manager->GetStreamContainer(embedder_host));
+
+  // Now, set the extension frame tree node ID to the actual extension frame
+  // tree node ID, which will be the same ID as `about_blank_host` in real
+  // situations.
+  manager->SetExtensionFrameTreeNodeIdForTesting(
+      embedder_host, extension_host->GetFrameTreeNodeId());
+
+  // Changing the extension host should delete the stream.
+  manager->RenderFrameHostChanged(extension_host, new_host);
+
+  // There are no remaining streams, so `PdfViewerStreamManager` should delete
+  // itself.
+  EXPECT_FALSE(pdf_viewer_stream_manager());
+}
+
+// If the PDF content host changes to a different host, the stream should be
+// deleted.
+TEST_F(PdfViewerStreamManagerTest, ContentRenderFrameHostChanged) {
+  const GURL pdf_url = GURL(kOriginalUrl1);
+
+  auto* embedder_host = CreateChildRenderFrameHost(main_rfh(), "embedder host");
+  embedder_host = NavigateAndCommit(embedder_host, pdf_url);
+  auto* extension_host =
+      CreateChildRenderFrameHost(embedder_host, "extension host");
+
+  // In a PDF load, there's an RFH for the content frame for the initial
+  // stream URL navigation. This RFH will always be replaced by
+  // `content_host` and shouldn't trigger stream deletion.
+  auto* stream_url_host =
+      CreateChildRenderFrameHost(extension_host, "content host");
+  auto* content_host =
+      CreateChildRenderFrameHost(extension_host, "content host");
+  content_host = NavigateAndCommit(content_host, pdf_url);
+  auto* new_host = CreateChildRenderFrameHost(extension_host, "new host");
+
+  PdfViewerStreamManager* manager = pdf_viewer_stream_manager();
+  manager->AddStreamContainer(embedder_host->GetFrameTreeNodeId(),
+                              "internal_id",
+                              pdf_test_util::GenerateSampleStreamContainer(1));
+  manager->ClaimStreamInfoForTesting(embedder_host);
+  manager->SetExtensionFrameTreeNodeIdForTesting(
+      embedder_host, extension_host->GetFrameTreeNodeId());
+  ASSERT_TRUE(manager->GetStreamContainer(embedder_host));
+
+  // The extension host needs to have the PDF extension origin so that
+  // `pdf_frame_util::GetEmbedderHost()` can identify and get the embedder host
+  // in `PdfViewerStreamManager::MaybeDeleteStreamOnPdfContentHostChanged()`.
+  content::OverrideLastCommittedOrigin(
+      extension_host,
+      url::Origin::Create(GURL(
+          "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html")));
+
+  // `stream_url_host` and `content_host` should have the same frame tree node
+  // ID, but this isn't possible with the current test infrastructure. For
+  // testing purposes, it's okay to set the content frame tree node ID to the
+  // initial RFH.
+  manager->SetContentFrameTreeNodeIdForTesting(
+      embedder_host, stream_url_host->GetFrameTreeNodeId());
+
+  // Changing `stream_url_host` to `content_host` shouldn't delete the stream.
+  manager->RenderFrameHostChanged(stream_url_host, content_host);
+
+  ASSERT_TRUE(pdf_viewer_stream_manager());
+  EXPECT_TRUE(manager->GetStreamContainer(embedder_host));
+
+  // Now, set the content frame tree node ID to the actual content frame tree
+  // node ID, which will be the same as `stream_url_host` in real situations.
+  manager->SetContentFrameTreeNodeIdForTesting(
+      embedder_host, content_host->GetFrameTreeNodeId());
+
+  // Changing the content host should delete the stream.
+  manager->RenderFrameHostChanged(content_host, new_host);
+
+  // There are no remaining streams, so `PdfViewerStreamManager` should delete
+  // itself.
+  EXPECT_FALSE(pdf_viewer_stream_manager());
+}
+
 // If the `content::RenderFrameHost` for the stream is deleted, then the stream
 // should be deleted.
-TEST_F(PdfViewerStreamManagerTest, FrameDeleted) {
+TEST_F(PdfViewerStreamManagerTest, EmbedderFrameDeleted) {
   content::RenderFrameHost* embedder_host =
       NavigateAndCommit(main_rfh(), GURL(kOriginalUrl1));
   int frame_tree_node_id = embedder_host->GetFrameTreeNodeId();
@@ -330,6 +553,113 @@
   EXPECT_FALSE(pdf_viewer_stream_manager());
 }
 
+// If the PDF extension frame is deleted, the stream should be deleted.
+TEST_F(PdfViewerStreamManagerTest, ExtensionFrameDeleted) {
+  auto* embedder_host = CreateChildRenderFrameHost(main_rfh(), "actual host");
+  embedder_host = NavigateAndCommit(embedder_host, GURL(kOriginalUrl1));
+  auto* extension_host =
+      CreateChildRenderFrameHost(embedder_host, "extension host");
+  int frame_tree_node_id = extension_host->GetFrameTreeNodeId();
+
+  PdfViewerStreamManager* manager = pdf_viewer_stream_manager();
+  manager->AddStreamContainer(embedder_host->GetFrameTreeNodeId(),
+                              "internal_id",
+                              pdf_test_util::GenerateSampleStreamContainer(1));
+  manager->ClaimStreamInfoForTesting(embedder_host);
+  ASSERT_TRUE(manager->GetStreamContainer(embedder_host));
+
+  // Set the extension frame tree node ID so the stream can be deleted when the
+  // extension host is deleted.
+  manager->SetExtensionFrameTreeNodeIdForTesting(embedder_host,
+                                                 frame_tree_node_id);
+
+  // Deleting the extension host should cause the stream to be deleted.
+  manager->FrameDeleted(frame_tree_node_id);
+
+  // There are no remaining streams, so `PdfViewerStreamManager` should delete
+  // itself.
+  EXPECT_FALSE(pdf_viewer_stream_manager());
+}
+
+// If the PDF content frame is deleted, the stream should be deleted.
+TEST_F(PdfViewerStreamManagerTest, ContentFrameDeleted) {
+  auto* embedder_host = CreateChildRenderFrameHost(main_rfh(), "embedder host");
+  embedder_host = NavigateAndCommit(embedder_host, GURL(kOriginalUrl1));
+  auto* extension_host =
+      CreateChildRenderFrameHost(embedder_host, "extension host");
+
+  auto* content_host =
+      CreateChildRenderFrameHost(extension_host, "content host");
+  int frame_tree_node_id = content_host->GetFrameTreeNodeId();
+
+  PdfViewerStreamManager* manager = pdf_viewer_stream_manager();
+  manager->AddStreamContainer(embedder_host->GetFrameTreeNodeId(),
+                              "internal_id",
+                              pdf_test_util::GenerateSampleStreamContainer(1));
+  manager->ClaimStreamInfoForTesting(embedder_host);
+  ASSERT_TRUE(manager->GetStreamContainer(embedder_host));
+
+  // Set the content frame tree node ID so the stream can be deleted when the
+  // content host is deleted.
+  manager->SetContentFrameTreeNodeIdForTesting(embedder_host,
+                                               frame_tree_node_id);
+
+  // Deleting the content host should cause the stream to be deleted.
+  manager->FrameDeleted(frame_tree_node_id);
+
+  // There are no remaining streams, so `PdfViewerStreamManager` should delete
+  // itself.
+  EXPECT_FALSE(pdf_viewer_stream_manager());
+}
+
+// Starting the navigation for the content host should set the content host
+// frame tree node ID.
+TEST_F(PdfViewerStreamManagerTest,
+       DidStartNavigationSetContentHostFrameTreeNodeId) {
+  content::RenderFrameHost* embedder_host =
+      NavigateAndCommit(main_rfh(), GURL(kOriginalUrl1));
+  auto* extension_host =
+      CreateChildRenderFrameHost(embedder_host, "extension host");
+  auto* pdf_host = CreateChildRenderFrameHost(extension_host, "pdf host");
+  int content_frame_tree_node_id = pdf_host->GetFrameTreeNodeId();
+
+  PdfViewerStreamManager* manager = pdf_viewer_stream_manager();
+  manager->AddStreamContainer(embedder_host->GetFrameTreeNodeId(),
+                              "internal_id",
+                              pdf_test_util::GenerateSampleStreamContainer(1));
+  manager->ClaimStreamInfoForTesting(embedder_host);
+  ASSERT_TRUE(manager->GetStreamContainer(embedder_host));
+
+  // Deleting the frame using frame tree node ID shouldn't trigger the stream
+  // deletion, since the content host frame tree node ID hasn't been set.
+  manager->FrameDeleted(pdf_host->GetFrameTreeNodeId());
+
+  ASSERT_TRUE(pdf_viewer_stream_manager());
+
+  NiceMock<content::MockNavigationHandle> navigation_handle;
+
+  // Set `navigation_handle`'s frame host to a grandchild frame host. This acts
+  // as the PDF frame host.
+  ON_CALL(navigation_handle, IsPdf).WillByDefault(Return(true));
+  ON_CALL(navigation_handle, GetFrameTreeNodeId)
+      .WillByDefault(Return(content_frame_tree_node_id));
+  navigation_handle.set_render_frame_host(pdf_host);
+
+  // Start the navigation. The content host frame tree node ID should now be
+  // set.
+  manager->DidStartNavigation(&navigation_handle);
+
+  ASSERT_TRUE(pdf_viewer_stream_manager());
+
+  // Deleting the frame should now trigger stream deletion, as the content host
+  // frame tree node ID has been set.
+  manager->FrameDeleted(content_frame_tree_node_id);
+
+  // There are no remaining streams, so `PdfViewerStreamManager` should delete
+  // itself.
+  EXPECT_FALSE(pdf_viewer_stream_manager());
+}
+
 // `PdfViewerStreamManager` should register a subresource
 // override if the navigation handle is for a PDF content frame.
 TEST_F(PdfViewerStreamManagerTest, ReadyToCommitNavigationSubresourceOverride) {
diff --git a/chrome/browser/policy/browser_dm_token_storage_linux.cc b/chrome/browser/policy/browser_dm_token_storage_linux.cc
index 1008513..9fef3079 100644
--- a/chrome/browser/policy/browser_dm_token_storage_linux.cc
+++ b/chrome/browser/policy/browser_dm_token_storage_linux.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/policy/browser_dm_token_storage_linux.h"
 
 #include <string>
+#include <string_view>
 
 #include "base/base64url.h"
 #include "base/files/file_util.h"
@@ -15,7 +16,6 @@
 #include "base/hash/sha1.h"
 #include "base/logging.h"
 #include "base/path_service.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/syslog_logging.h"
@@ -94,7 +94,7 @@
   const int machine_id_size = 32;
   std::string machine_id;
   machine_id = ReadMachineIdFile();
-  base::StringPiece machine_id_trimmed =
+  std::string_view machine_id_trimmed =
       base::TrimWhitespaceASCII(machine_id, base::TRIM_TRAILING);
   if (machine_id_trimmed.size() != machine_id_size) {
     SYSLOG(ERROR) << "Error: /etc/machine-id contains "
diff --git a/chrome/browser/policy/browser_dm_token_storage_win.cc b/chrome/browser/policy/browser_dm_token_storage_win.cc
index fa52580..4756956 100644
--- a/chrome/browser/policy/browser_dm_token_storage_win.cc
+++ b/chrome/browser/policy/browser_dm_token_storage_win.cc
@@ -16,6 +16,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <tuple>
 #include <utility>
 #include <vector>
@@ -25,7 +26,6 @@
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/thread_pool.h"
@@ -250,7 +250,7 @@
 
     DCHECK_LE(size, installer::kMaxDMTokenLength);
     return std::string(base::TrimWhitespaceASCII(
-        base::StringPiece(raw_value.data(), size), base::TRIM_ALL));
+        std::string_view(raw_value.data(), size), base::TRIM_ALL));
   }
 
   DVLOG(1) << "Failed to get DMToken from Registry.";
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 1765f50..04d2311a 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1955,14 +1955,14 @@
     base::Value::Type::BOOLEAN },
 #endif // BUILDFLAG(IS_CHROMEOS)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // TODO(crbug.com/1454054): replace the
   // kGetDisplayMediaSetSelectAllScreensAllowedForUrls policy by a policy that
   // matches the name of the new `getAllScreensMedia` API.
   { key::kGetDisplayMediaSetSelectAllScreensAllowedForUrls,
     capture_policy::kManagedAccessToGetAllScreensMediaAllowedForUrls,
     base::Value::Type::LIST },
-#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
   { key::kAuthNegotiateDelegateByKdcPolicy,
diff --git a/chrome/browser/policy/device_management_service_configuration.cc b/chrome/browser/policy/device_management_service_configuration.cc
index 91ef77e..305240a 100644
--- a/chrome/browser/policy/device_management_service_configuration.cc
+++ b/chrome/browser/policy/device_management_service_configuration.cc
@@ -6,6 +6,8 @@
 
 #include <stdint.h>
 
+#include <string_view>
+
 #include "base/logging.h"
 #include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
@@ -58,7 +60,7 @@
   ash::system::StatisticsProvider* provider =
       ash::system::StatisticsProvider::GetInstance();
 
-  const std::optional<base::StringPiece> hwclass =
+  const std::optional<std::string_view> hwclass =
       provider->GetMachineStatistic(ash::system::kHardwareClassKey);
   if (!hwclass) {
     LOG(ERROR) << "Failed to get machine information";
diff --git a/chrome/browser/policy/policy_network_browsertest.cc b/chrome/browser/policy/policy_network_browsertest.cc
index e351aa5..22ebafc 100644
--- a/chrome/browser/policy/policy_network_browsertest.cc
+++ b/chrome/browser/policy/policy_network_browsertest.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string_view>
+
 #include "base/functional/bind.h"
 #include "base/strings/strcat.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
@@ -70,7 +71,7 @@
     return g_browser_process->local_state()->GetBoolean(pref_name);
   }
 
-  LoadResult LoadPage(base::StringPiece path) {
+  LoadResult LoadPage(std::string_view path) {
     return LoadPage(https_server_.GetURL(path));
   }
 
@@ -149,12 +150,12 @@
 class ECHPolicyTest : public SSLPolicyTest {
  public:
   // a.test is covered by `CERT_TEST_NAMES`.
-  static constexpr base::StringPiece kHostname = "a.test";
-  static constexpr base::StringPiece kPublicName = "public-name.test";
-  static constexpr base::StringPiece kDohServerHostname = "doh.test";
+  static constexpr std::string_view kHostname = "a.test";
+  static constexpr std::string_view kPublicName = "public-name.test";
+  static constexpr std::string_view kDohServerHostname = "doh.test";
 
-  static constexpr base::StringPiece kECHSuccessTitle = "Negotiated ECH";
-  static constexpr base::StringPiece kECHFailureTitle = "Did not negotiate ECH";
+  static constexpr std::string_view kECHSuccessTitle = "Negotiated ECH";
+  static constexpr std::string_view kECHFailureTitle = "Did not negotiate ECH";
 
   ECHPolicyTest() : ech_server_{net::EmbeddedTestServer::TYPE_HTTPS} {
     scoped_feature_list_.InitWithFeaturesAndParameters(
@@ -218,7 +219,7 @@
     return policies;
   }
 
-  GURL GetURL(base::StringPiece path) {
+  GURL GetURL(std::string_view path) {
     return ech_server_.GetURL(kHostname, path);
   }
 
diff --git a/chrome/browser/policy/webhid_device_policy_handler.cc b/chrome/browser/policy/webhid_device_policy_handler.cc
index 88cddde..7ae774b 100644
--- a/chrome/browser/policy/webhid_device_policy_handler.cc
+++ b/chrome/browser/policy/webhid_device_policy_handler.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
@@ -30,7 +31,7 @@
 
 WebHidDevicePolicyHandler::WebHidDevicePolicyHandler(
     const char* policy_key,
-    base::StringPiece pref_name,
+    std::string_view pref_name,
     const Schema& chrome_schema)
     : SchemaValidatingPolicyHandler(
           policy_key,
diff --git a/chrome/browser/policy/webhid_device_policy_handler.h b/chrome/browser/policy/webhid_device_policy_handler.h
index 1daafd4..b8edc06 100644
--- a/chrome/browser/policy/webhid_device_policy_handler.h
+++ b/chrome/browser/policy/webhid_device_policy_handler.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_POLICY_WEBHID_DEVICE_POLICY_HANDLER_H_
 #define CHROME_BROWSER_POLICY_WEBHID_DEVICE_POLICY_HANDLER_H_
 
+#include <string_view>
+
 #include "components/policy/core/browser/configuration_policy_handler.h"
 
 class PrefValueMap;
@@ -19,7 +21,7 @@
 class WebHidDevicePolicyHandler : public SchemaValidatingPolicyHandler {
  public:
   explicit WebHidDevicePolicyHandler(const char* policy_key,
-                                     base::StringPiece pref_name,
+                                     std::string_view pref_name,
                                      const Schema& schema);
   WebHidDevicePolicyHandler(const WebHidDevicePolicyHandler&) = delete;
   WebHidDevicePolicyHandler& operator=(const WebHidDevicePolicyHandler&) =
diff --git a/chrome/browser/predictors/loading_stats_collector.cc b/chrome/browser/predictors/loading_stats_collector.cc
index 2a652e1d..1f5055a4 100644
--- a/chrome/browser/predictors/loading_stats_collector.cc
+++ b/chrome/browser/predictors/loading_stats_collector.cc
@@ -210,30 +210,59 @@
           HintOrigin::OPTIMIZATION_GUIDE);
     }
     if (!optimization_guide_prediction->predicted_subresources.empty()) {
+      url::Origin main_frame_origin = url::Origin::Create(summary.initial_url);
+      size_t cross_origin_predicted_subresources = 0;
+      size_t correctly_predicted_subresources = 0;
+      size_t correctly_predicted_cross_origin_subresources = 0;
+      size_t correctly_predicted_low_priority_subresources = 0;
+      size_t correctly_predicted_low_priority_cross_origin_subresources = 0;
+
+      for (const GURL& subresource_url :
+           optimization_guide_prediction->predicted_subresources) {
+        const bool is_cross_origin =
+            !main_frame_origin.IsSameOriginWith(subresource_url);
+        const bool is_correctly_predicted =
+            base::Contains(summary.subresource_urls, subresource_url);
+        const bool is_correctly_predicted_low_priority =
+            !is_correctly_predicted &&
+            base::Contains(summary.low_priority_subresource_urls,
+                           subresource_url);
+        if (is_cross_origin) {
+          cross_origin_predicted_subresources++;
+        }
+        if (is_correctly_predicted) {
+          correctly_predicted_subresources++;
+        }
+        if (is_cross_origin && is_correctly_predicted) {
+          correctly_predicted_cross_origin_subresources++;
+        }
+        if (is_correctly_predicted_low_priority) {
+          correctly_predicted_low_priority_subresources++;
+        }
+        if (is_cross_origin && is_correctly_predicted_low_priority) {
+          correctly_predicted_low_priority_cross_origin_subresources++;
+        }
+      }
+
       builder.SetOptimizationGuidePredictionSubresources(std::min(
           ukm_cap,
           optimization_guide_prediction->predicted_subresources.size()));
-      size_t correctly_predicted_subresources = base::ranges::count_if(
-          optimization_guide_prediction->predicted_subresources,
-          [&summary](const GURL& subresource_url) {
-            return base::Contains(summary.subresource_urls, subresource_url);
-          });
+      builder.SetOptimizationGuidePredictionSubresources_CrossOrigin(
+          std::min(ukm_cap, cross_origin_predicted_subresources));
       builder.SetOptimizationGuidePredictionCorrectlyPredictedSubresources(
           std::min(ukm_cap, correctly_predicted_subresources));
-      size_t correctly_predicted_low_priority_subresources =
-          base::ranges::count_if(
-              optimization_guide_prediction->predicted_subresources,
-              [&summary](const GURL& subresource_url) {
-                return base::Contains(summary.low_priority_subresource_urls,
-                                      subresource_url) &&
-                       !base::Contains(summary.subresource_urls,
-                                       subresource_url);
-              });
+      builder
+          .SetOptimizationGuidePredictionCorrectlyPredictedSubresources_CrossOrigin(
+              std::min(ukm_cap, correctly_predicted_cross_origin_subresources));
       builder
           .SetOptimizationGuidePredictionCorrectlyPredictedLowPrioritySubresources(
               std::min(ukm_cap, correctly_predicted_low_priority_subresources));
+      builder
+          .SetOptimizationGuidePredictionCorrectlyPredictedLowPrioritySubresources_CrossOrigin(
+              std::min(
+                  ukm_cap,
+                  correctly_predicted_low_priority_cross_origin_subresources));
 
-      url::Origin main_frame_origin = url::Origin::Create(summary.initial_url);
       std::set<url::Origin> predicted_origins;
       for (const auto& subresource :
            optimization_guide_prediction->predicted_subresources) {
diff --git a/chrome/browser/predictors/loading_stats_collector_unittest.cc b/chrome/browser/predictors/loading_stats_collector_unittest.cc
index a2d5b6bd..3897b3e 100644
--- a/chrome/browser/predictors/loading_stats_collector_unittest.cc
+++ b/chrome/browser/predictors/loading_stats_collector_unittest.cc
@@ -218,6 +218,7 @@
   auto gen = [](int index) {
     return base::StringPrintf("http://cdn%d.google.com/script.js", index);
   };
+  const std::string same_origin_subresource_url = "http://google.com/script.js";
 
   PreconnectPrediction local_prediction;
   EXPECT_CALL(*mock_predictor_,
@@ -241,12 +242,16 @@
            {url::Origin::Create(GURL(gen(2))), 1, network_anonymization_key_},
            {url::Origin::Create(GURL(gen(3))), 0, network_anonymization_key_}});
   optimization_guide_prediction->predicted_subresources = {
-      GURL(gen(1)), GURL(gen(2)), GURL(gen(3)), GURL(gen(4))};
+      GURL(same_origin_subresource_url), GURL(gen(1)), GURL(gen(2)),
+      GURL(gen(3)), GURL(gen(4))};
 
   // Simulate a page load with 2 resources, one we know, one we don't, plus we
   // know the main frame origin.
   std::vector<blink::mojom::ResourceLoadInfoPtr> resources;
   resources.push_back(CreateResourceLoadInfo(main_frame_url));
+  resources.push_back(
+      CreateResourceLoadInfo(same_origin_subresource_url,
+                             network::mojom::RequestDestination::kScript));
   resources.push_back(CreateResourceLoadInfo(
       gen(1), network::mojom::RequestDestination::kScript));
   resources.push_back(CreateResourceLoadInfo(
@@ -321,17 +326,32 @@
       entry,
       ukm::builders::LoadingPredictor::
           kOptimizationGuidePredictionSubresourcesName,
+      5);
+  ukm_recorder_->ExpectEntryMetric(
+      entry,
+      ukm::builders::LoadingPredictor::
+          kOptimizationGuidePredictionSubresources_CrossOriginName,
       4);
   ukm_recorder_->ExpectEntryMetric(
       entry,
       ukm::builders::LoadingPredictor::
           kOptimizationGuidePredictionCorrectlyPredictedSubresourcesName,
+      2);
+  ukm_recorder_->ExpectEntryMetric(
+      entry,
+      ukm::builders::LoadingPredictor::
+          kOptimizationGuidePredictionCorrectlyPredictedSubresources_CrossOriginName,
       1);
   ukm_recorder_->ExpectEntryMetric(
       entry,
       ukm::builders::LoadingPredictor::
           kOptimizationGuidePredictionCorrectlyPredictedLowPrioritySubresourcesName,
       0);
+  ukm_recorder_->ExpectEntryMetric(
+      entry,
+      ukm::builders::LoadingPredictor::
+          kOptimizationGuidePredictionCorrectlyPredictedLowPrioritySubresources_CrossOriginName,
+      0);
   // Make sure local metrics are not recorded since there was not a local
   // prediction.
   EXPECT_FALSE(ukm_recorder_->EntryHasMetric(
@@ -352,6 +372,7 @@
 TEST_F(LoadingStatsCollectorTest,
        TestOptimizationGuideCorrectPredictionsPostLoad) {
   const std::string main_frame_url = "http://google.com/?query=cats";
+  const std::string same_origin_subresource_url = "http://google.com/script.js";
   auto gen = [](int index) {
     return base::StringPrintf("http://cdn%d.google.com/script.js", index);
   };
@@ -377,7 +398,8 @@
            {url::Origin::Create(GURL(gen(2))), 1, network_anonymization_key_},
            {url::Origin::Create(GURL(gen(3))), 0, network_anonymization_key_}});
   optimization_guide_prediction->predicted_subresources = {
-      GURL(gen(1)), GURL(gen(2)), GURL(gen(3)), GURL(gen(4))};
+      GURL(same_origin_subresource_url), GURL(gen(1)), GURL(gen(2)),
+      GURL(gen(3)), GURL(gen(4))};
 
   // Simulate a page load with 3 resources (we know all 3). The 3rd resource
   // is fetched after the page finishes loading.
@@ -389,6 +411,9 @@
       gen(2), network::mojom::RequestDestination::kScript));
   resources.push_back(CreateResourceLoadInfo(
       gen(3), network::mojom::RequestDestination::kScript));
+  resources.push_back(
+      CreateResourceLoadInfo(same_origin_subresource_url,
+                             network::mojom::RequestDestination::kScript));
   PageRequestSummary summary =
       CreatePageRequestSummary(main_frame_url, main_frame_url, {}, now,
                                /*main_frame_load_complete=*/false);
@@ -398,6 +423,7 @@
   summary.MainFrameLoadComplete();
   summary.UpdateOrAddResource(*resources[3]);
   summary.UpdateOrAddResource(*resources[2]);
+  summary.UpdateOrAddResource(*resources[4]);
   summary.prefetch_urls = {GURL(gen(1)), GURL(gen(2)), GURL(gen(3)),
                            GURL(gen(4))};
   summary.first_prefetch_initiated = now + base::Milliseconds(1);
@@ -449,6 +475,11 @@
       entry,
       ukm::builders::LoadingPredictor::
           kOptimizationGuidePredictionSubresourcesName,
+      5);
+  ukm_recorder_->ExpectEntryMetric(
+      entry,
+      ukm::builders::LoadingPredictor::
+          kOptimizationGuidePredictionSubresources_CrossOriginName,
       4);
   ukm_recorder_->ExpectEntryMetric(
       entry,
@@ -458,7 +489,17 @@
   ukm_recorder_->ExpectEntryMetric(
       entry,
       ukm::builders::LoadingPredictor::
+          kOptimizationGuidePredictionCorrectlyPredictedSubresources_CrossOriginName,
+      2);
+  ukm_recorder_->ExpectEntryMetric(
+      entry,
+      ukm::builders::LoadingPredictor::
           kOptimizationGuidePredictionCorrectlyPredictedLowPrioritySubresourcesName,
+      2);
+  ukm_recorder_->ExpectEntryMetric(
+      entry,
+      ukm::builders::LoadingPredictor::
+          kOptimizationGuidePredictionCorrectlyPredictedLowPrioritySubresources_CrossOriginName,
       1);
 }
 
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.cc
index 05afa7f1..3b091ea 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
@@ -69,7 +70,7 @@
 
   // URLLoaderThrottle::Delegate:
   void CancelWithError(int error_code,
-                       base::StringPiece custom_reason) override {
+                       std::string_view custom_reason) override {
     cancelled_ = true;
   }
 
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc b/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc
index 9e1deec..65ddfe4 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <utility>
 
 #include "base/functional/bind.h"
@@ -151,7 +152,7 @@
     return;
   }
   while (true) {
-    base::StringPiece response_data =
+    std::string_view response_data =
         loader_->GetMoreDataFromCache(write_position_);
     if (response_data.empty()) {
       if (!response_data.data()) {
@@ -700,14 +701,14 @@
   PostTaskToReleaseOwnership();
 }
 
-base::StringPiece StreamingSearchPrefetchURLLoader::GetMoreDataFromCache(
+std::string_view StreamingSearchPrefetchURLLoader::GetMoreDataFromCache(
     int writing_position) const {
   DCHECK_GE(bytes_of_raw_data_to_transfer_, writing_position);
   if (drain_complete_ && writing_position == bytes_of_raw_data_to_transfer_) {
-    return base::StringPiece();
+    return std::string_view();
   }
-  return base::StringPiece(body_content_.data() + writing_position,
-                           bytes_of_raw_data_to_transfer_ - writing_position);
+  return std::string_view(body_content_.data() + writing_position,
+                          bytes_of_raw_data_to_transfer_ - writing_position);
 }
 
 void StreamingSearchPrefetchURLLoader::PushData() {
@@ -718,7 +719,7 @@
   DCHECK(!streaming_prefetch_request_);
   while (true) {
     DCHECK_GE(bytes_of_raw_data_to_transfer_, write_position_);
-    base::StringPiece response_data = GetMoreDataFromCache(write_position_);
+    std::string_view response_data = GetMoreDataFromCache(write_position_);
 
     if (response_data.empty()) {
       // If no data is provided, the cache has served every byte to loader.
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.h b/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.h
index 8d7df222..11d809fc 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.h
+++ b/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -315,11 +316,11 @@
 
   // Returns the view of `body_content_`, starting from the `writing_position`
   // and ending at the end of the string.
-  // Returns an invalid StringPiece (note, not an empty StringPiece) if there is
-  // no more valid data. Returns an empty StringPiece if writing_position
-  // reaches the end of the current response body but `this` is waiting for the
-  // network to produce more data.
-  base::StringPiece GetMoreDataFromCache(int writing_position) const;
+  // Returns an invalid std::string_view (note, not an empty std::string_view)
+  // if there is no more valid data. Returns an empty std::string_view if
+  // writing_position reaches the end of the current response body but `this` is
+  // waiting for the network to produce more data.
+  std::string_view GetMoreDataFromCache(int writing_position) const;
 
   // Push data into |producer_handle_|.
   void PushData();
diff --git a/chrome/browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc b/chrome/browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc
index 9eb87b08..eb15f8b 100644
--- a/chrome/browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc
+++ b/chrome/browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <string>
+#include <string_view>
 
 #include "base/containers/adapters.h"
 #include "base/files/file_path.h"
@@ -773,7 +774,7 @@
         SearchSuggestionTuple(origin_query, suggestions));
   }
 
-  void InputSearchQuery(base::StringPiece search_query) {
+  void InputSearchQuery(std::string_view search_query) {
     // Trigger an omnibox suggest that has a prerender hint.
     AutocompleteInput input(base::ASCIIToUTF16(search_query),
                             metrics::OmniboxEventProto::BLANK,
@@ -790,7 +791,7 @@
     EXPECT_TRUE(autocomplete_controller->done());
   }
 
-  int InputSearchQueryAndWaitForTrigger(base::StringPiece search_query,
+  int InputSearchQueryAndWaitForTrigger(std::string_view search_query,
                                         const GURL& expected_url) {
     content::test::PrerenderHostRegistryObserver registry_observer(
         *GetActiveWebContents());
diff --git a/chrome/browser/preloading/preview/preview_navigation_throttle.cc b/chrome/browser/preloading/preview/preview_navigation_throttle.cc
index 6bdd7c9..bfa92f8 100644
--- a/chrome/browser/preloading/preview/preview_navigation_throttle.cc
+++ b/chrome/browser/preloading/preview/preview_navigation_throttle.cc
@@ -4,9 +4,10 @@
 
 #include "chrome/browser/preloading/preview/preview_navigation_throttle.h"
 
+#include <string_view>
+
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
-#include "base/strings/string_piece.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
@@ -72,8 +73,8 @@
   std::string extracted_string =
       ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
           IDR_NET_ERROR_HTML);
-  base::StringPiece template_html(extracted_string.data(),
-                                  extracted_string.size());
+  std::string_view template_html(extracted_string.data(),
+                                 extracted_string.size());
   CHECK(!template_html.empty()) << "unable to load template.";
   return webui::GetLocalizedHtml(template_html, page_state.strings);
 }
diff --git a/chrome/browser/preloading/preview/preview_tab.cc b/chrome/browser/preloading/preview/preview_tab.cc
index d1a07da..57c75f5 100644
--- a/chrome/browser/preloading/preview/preview_tab.cc
+++ b/chrome/browser/preloading/preview/preview_tab.cc
@@ -7,10 +7,12 @@
 #include "base/features.h"
 #include "base/functional/callback.h"
 #include "base/time/time.h"
+#include "build/buildflag.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_initialize.h"
 #include "chrome/browser/preloading/preview/preview_zoom_controller.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tab_helpers.h"
 #include "components/zoom/page_zoom.h"
 #include "components/zoom/zoom_controller.h"
@@ -24,6 +26,9 @@
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
+#if defined(USE_AURA)
+#include "ui/aura/window.h"
+#endif  // defined(USE_AURA)
 #include "ui/base/page_transition_types.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/base/window_open_disposition.h"
@@ -44,42 +49,24 @@
 
 }  // namespace
 
+// TODO(b:305007647): Stop creating a Widget's subclass.
 class PreviewTab::PreviewWidget final : public views::Widget {
  public:
   explicit PreviewWidget(PreviewManager* preview_manager)
-      : preview_manager_(preview_manager) {}
+      : preview_manager_(preview_manager) {
+    CHECK(preview_manager_);
+  }
+
+  void PromoteToNewTab() { preview_manager_->PromoteToNewTab(); }
 
  private:
-  // views::Widet implementation:
-  void OnMouseEvent(ui::MouseEvent* event) override {
-    auto rect = GetClientAreaBoundsInScreen();
-    // Check the event occurred on this widget.
-    // Note that `event->location()` is relative to the origin of widget.
-    bool is_event_for_preview_window =
-        0 <= event->location().x() &&
-        event->location().x() <= rect.size().width() &&
-        0 <= event->location().y() &&
-        event->location().y() <= rect.size().height();
-
-    // Tentative trigger for open-in-new-tab: Middle click on preview.
-    if (is_event_for_preview_window && event->type() == ui::ET_MOUSE_RELEASED &&
-        event->IsMiddleMouseButton()) {
-      event->SetHandled();
-      preview_manager_->PromoteToNewTab();
-      return;
-    }
-
-    // This doesn't triggered for long press trigger.
-    //
-    // TODO(b:320386573): Cancel preview when focus lost.
-    if (!is_event_for_preview_window && event->type() == ui::ET_MOUSE_PRESSED) {
-      event->SetHandled();
+  // internal::NativeWidgetDelegate implementation:
+  bool OnNativeWidgetActivationChanged(bool active) override {
+    if (!active) {
       preview_manager_->Cancel(content::PreviewCancelReason::Build(
           content::PreviewFinalStatus::kCancelledByWindowClose));
-      return;
     }
-
-    views::Widget::OnMouseEvent(event);
+    return views::Widget::OnNativeWidgetActivationChanged(active);
   }
 
   // Outlives because `PreviewManager` has `PreviewTab` and `PreviewTab` has
@@ -145,20 +132,21 @@
   views::Widget::InitParams params;
   // TODO(b:292184832): Create with own buttons
   params.type = views::Widget::InitParams::TYPE_WINDOW;
+  params.shadow_type = views::Widget::InitParams::ShadowType::kDrop;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-  params.z_order = ui::ZOrderLevel::kFloatingWindow;
-  const gfx::Rect rect = initiator_web_contents.GetContainerBounds();
+  const gfx::Rect& rect = initiator_web_contents.GetViewBounds();
   params.bounds =
       gfx::Rect(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2,
                 rect.width() / 2, rect.height() / 2);
+#if defined(USE_AURA) && (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+  params.requires_accelerated_widget = true;
+#endif
   widget_->Init(std::move(params));
-  // TODO(b:292184832): Clarify the ownership.
-  widget_->non_client_view()->frame_view()->InsertClientView(
-      new views::ClientView(widget_.get(), view_.get()));
   widget_->non_client_view()->frame_view()->SetLayoutManager(
       std::make_unique<views::FillLayout>());
+  widget_->non_client_view()->frame_view()->InsertClientView(
+      new views::ClientView(widget_.get(), view_.get()));
   widget_->Show();
-  widget_->SetCapture(widget_->client_view());
 }
 
 bool PreviewTab::AuditWebInputEvent(const blink::WebInputEvent& event) {
@@ -172,6 +160,10 @@
       type == blink::WebInputEvent::Type::kGestureScrollUpdate) {
     return true;
   }
+  // Activate by any mouse down as window focus also changes by mouse down.
+  if (type == blink::WebInputEvent::Type::kMouseDown) {
+    widget_->PromoteToNewTab();
+  }
   return false;
 }
 
diff --git a/chrome/browser/printing/prefs_util.cc b/chrome/browser/printing/prefs_util.cc
index 05fdd63c..509ff84 100644
--- a/chrome/browser/printing/prefs_util.cc
+++ b/chrome/browser/printing/prefs_util.cc
@@ -78,6 +78,14 @@
   return IsOopPrintingEnabled() &&
          features::kEnableOopPrintDriversJobPrint.Get();
 }
+
+bool ShouldEarlyStartPrintBackendService() {
+  return IsOopPrintingEnabled() &&
+#if BUILDFLAG(IS_WIN)
+         features::kEnableOopPrintDriversSingleProcess.Get() &&
+#endif
+         features::kEnableOopPrintDriversEarlyStart.Get();
+}
 #endif  // BUILDFLAG(ENABLE_OOP_PRINTING)
 
 }  // namespace printing
diff --git a/chrome/browser/printing/prefs_util.h b/chrome/browser/printing/prefs_util.h
index 00396ee..6deb6a5 100644
--- a/chrome/browser/printing/prefs_util.h
+++ b/chrome/browser/printing/prefs_util.h
@@ -23,6 +23,10 @@
 
 // Determine if printing a job should be done out-of-process.
 bool ShouldPrintJobOop();
+
+// Determine if a Print Backend service should be launched early, after the
+// browser has finished its startup.
+bool ShouldEarlyStartPrintBackendService();
 #endif
 
 }  // namespace printing
diff --git a/chrome/browser/printing/print_backend_service_manager.cc b/chrome/browser/printing/print_backend_service_manager.cc
index 0b8190a8..84abad2 100644
--- a/chrome/browser/printing/print_backend_service_manager.cc
+++ b/chrome/browser/printing/print_backend_service_manager.cc
@@ -104,6 +104,14 @@
 PrintBackendServiceManager::~PrintBackendServiceManager() = default;
 
 // static
+void PrintBackendServiceManager::LaunchPersistentService() {
+  PrintBackendServiceManager& service_mgr = GetInstance();
+  // Registering a query client causes a service to be launched.
+  service_mgr.persistent_service_ = true;
+  std::ignore = service_mgr.RegisterQueryClient();
+}
+
+// static
 std::string PrintBackendServiceManager::ClientTypeToString(
     ClientType client_type) {
   switch (client_type) {
@@ -891,9 +899,13 @@
   return service;
 }
 
-// static
 constexpr base::TimeDelta PrintBackendServiceManager::GetClientTypeIdleTimeout(
-    ClientType client_type) {
+    ClientType client_type) const {
+  if (persistent_service_) {
+    // Intentionally keep service around indefinitely.
+    return base::TimeDelta::Max();
+  }
+
   switch (client_type) {
     case ClientType::kQuery:
       // Want a long timeout so that the service is available across typical
diff --git a/chrome/browser/printing/print_backend_service_manager.h b/chrome/browser/printing/print_backend_service_manager.h
index 12ef9432..208582d 100644
--- a/chrome/browser/printing/print_backend_service_manager.h
+++ b/chrome/browser/printing/print_backend_service_manager.h
@@ -100,6 +100,10 @@
   PrintBackendServiceManager& operator=(const PrintBackendServiceManager&) =
       delete;
 
+  // Launch a service that is intended to persist indefinitely and can be used
+  // by all further clients.
+  static void LaunchPersistentService();
+
   // Client registration routines.  These act as a signal of impending activity
   // enabling possible optimizations within the manager.  They return an ID
   // which the callers are to use with `UnregisterClient()` once they have
@@ -428,8 +432,8 @@
       RemotesBundleMap<T>& bundle_map);
 
   // Get the idle timeout value to user for a particular client type.
-  static constexpr base::TimeDelta GetClientTypeIdleTimeout(
-      ClientType client_type);
+  constexpr base::TimeDelta GetClientTypeIdleTimeout(
+      ClientType client_type) const;
 
   // Whether any clients are queries with UI to `remote_id`.
   bool HasQueryWithUiClientForRemoteId(const RemoteId& remote_id) const;
@@ -712,6 +716,10 @@
   // sequence is sufficient.
   uint32_t remote_id_sequence_ = 0;
 
+  // Set when launched services are intended to persist indefinitely, rather
+  // than being disconnected after a finite idle timeout expires.
+  bool persistent_service_ = false;
+
   // Crash key is kept at class level so that we can obtain printer driver
   // information for a prior call should the process be terminated due to Mojo
   // message response validation.
diff --git a/chrome/browser/printing/printing_init.cc b/chrome/browser/printing/printing_init.cc
index 6802949..ef73df6 100644
--- a/chrome/browser/printing/printing_init.cc
+++ b/chrome/browser/printing/printing_init.cc
@@ -11,6 +11,12 @@
 #include "content/public/browser/web_contents.h"
 #include "printing/buildflags/buildflags.h"
 
+#if BUILDFLAG(ENABLE_OOP_PRINTING)
+#include "base/functional/bind.h"
+#include "chrome/browser/after_startup_task_utils.h"
+#include "content/public/browser/browser_thread.h"
+#endif
+
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 #include "chrome/browser/printing/pdf_nup_converter_client.h"
 #include "chrome/browser/printing/print_view_manager.h"
@@ -20,9 +26,18 @@
 
 namespace printing {
 
+#if BUILDFLAG(ENABLE_OOP_PRINTING)
+void EarlyStartPrintBackendService() {
+  AfterStartupTaskUtils::PostTask(
+      FROM_HERE, content::GetUIThreadTaskRunner({}),
+      base::BindOnce(&PrintBackendServiceManager::LaunchPersistentService));
+}
+#endif
+
 void InitializePrintingForWebContents(content::WebContents* web_contents) {
   // Headless mode uses a minimalistic Print Manager implementation that
-  // shortcuts most of the callbacks providing only print to PDF functionality.
+  // shortcuts most of the callbacks providing only print to PDF
+  // functionality.
   if (headless::IsHeadlessMode()) {
     headless::HeadlessPrintManager::CreateForWebContents(web_contents);
   } else {
diff --git a/chrome/browser/printing/printing_init.h b/chrome/browser/printing/printing_init.h
index f8eda9c..105081b 100644
--- a/chrome/browser/printing/printing_init.h
+++ b/chrome/browser/printing/printing_init.h
@@ -5,12 +5,21 @@
 #ifndef CHROME_BROWSER_PRINTING_PRINTING_INIT_H_
 #define CHROME_BROWSER_PRINTING_PRINTING_INIT_H_
 
+#include "printing/buildflags/buildflags.h"
+
 namespace content {
 class WebContents;
 }
 
 namespace printing {
 
+#if BUILDFLAG(ENABLE_OOP_PRINTING)
+// Perform an early launch of the Print Backend service, if appropriate.  The
+// actual launch does not happen immediately, but is scheduled to start after
+// the browser has completed its startup sequence.
+void EarlyStartPrintBackendService();
+#endif
+
 // Initialize printing related classes for a WebContents.
 void InitializePrintingForWebContents(content::WebContents* web_contents);
 
diff --git a/chrome/browser/printing/system_access_process_print_browsertest.cc b/chrome/browser/printing/system_access_process_print_browsertest.cc
index 8605f09..caf0ef89 100644
--- a/chrome/browser/printing/system_access_process_print_browsertest.cc
+++ b/chrome/browser/printing/system_access_process_print_browsertest.cc
@@ -50,6 +50,9 @@
 #include "chrome/browser/printing/print_job_worker_oop.h"
 #include "chrome/browser/printing/print_preview_dialog_controller.h"
 #include "chrome/browser/printing/printer_query_oop.h"
+#include "chrome/browser/task_manager/task_manager_browsertest_util.h"
+#include "chrome/browser/task_manager/task_manager_interface.h"
+#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
 #endif
 
@@ -71,6 +74,11 @@
 #error "ChromeOS not supported here yet"
 #endif
 
+using task_manager::TaskManagerInterface;
+using task_manager::browsertest_util::MatchAnyTab;
+using task_manager::browsertest_util::MatchUtility;
+using task_manager::browsertest_util::WaitForTaskManagerRows;
+
 namespace printing {
 
 namespace {
@@ -277,6 +285,10 @@
   return variations;
 }
 
+std::string GetServiceLaunchTimingTestSuffix(
+    const testing::TestParamInfo<bool>& info) {
+  return info.param ? "EarlyStart" : "OnDemand";
+}
 #endif  // BUILDFLAG(ENABLE_OOP_PRINTING)
 
 }  // namespace
@@ -598,6 +610,9 @@
   // Only of interest when `UseService()` returns true.
   virtual bool SandboxService() = 0;
 
+  // Only applicable when `UseService()` returns true.
+  virtual bool EarlyStartService() { return false; }
+
 #if BUILDFLAG(IS_WIN)
   // Only applicable when `UseService()` returns true.
   virtual bool UseXps() = 0;
@@ -610,7 +625,9 @@
     if (UseService()) {
       enabled_features.push_back(
           {features::kEnableOopPrintDrivers,
-           {{features::kEnableOopPrintDriversJobPrint.name, "true"},
+           {{features::kEnableOopPrintDriversEarlyStart.name,
+             EarlyStartService() ? "true" : "false"},
+            {features::kEnableOopPrintDriversJobPrint.name, "true"},
             {features::kEnableOopPrintDriversSandbox.name,
              SandboxService() ? "true" : "false"}}});
 #if BUILDFLAG(IS_WIN)
@@ -728,7 +745,7 @@
 
   void SetUpOnMainThread() override {
 #if BUILDFLAG(ENABLE_OOP_PRINTING)
-    if (UseService()) {
+    if (UseService() && !EarlyStartService()) {
       print_backend_service_ = PrintBackendServiceTestImpl::LaunchForTesting(
           test_remote_, test_print_backend(), /*sandboxed=*/true);
     }
@@ -1287,6 +1304,39 @@
 
 #if BUILDFLAG(ENABLE_OOP_PRINTING)
 
+class SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest
+    : public SystemAccessProcessPrintBrowserTestBase,
+      public testing::WithParamInterface<bool> {
+ public:
+  SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest() = default;
+  ~SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest() override =
+      default;
+
+  bool UseService() override { return true; }
+  bool SandboxService() override { return false; }
+  bool EarlyStartService() override { return GetParam(); }
+#if BUILDFLAG(IS_WIN)
+  bool UseXps() override { return false; }
+#endif
+
+  bool DoesPrintBackendServiceTaskExist() {
+    TaskManagerInterface* task_mgr = TaskManagerInterface::GetTaskManager();
+    std::u16string title = MatchUtility(u"Print Backend Service");
+    for (auto task_id : task_mgr->GetTaskIdsList()) {
+      if (title == task_mgr->GetTitle(task_id)) {
+        return true;
+      }
+    }
+    return false;
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest,
+    /*EarlyStartService=*/testing::Bool(),
+    GetServiceLaunchTimingTestSuffix);
+
 class SystemAccessProcessSandboxedServicePrintBrowserTest
     : public SystemAccessProcessPrintBrowserTestBase,
       public testing::WithParamInterface<PlatformPrintApiVariation> {
@@ -1400,6 +1450,20 @@
          PrintBackendFeatureVariation::kOopUnsandboxedService})),
     GetPrintBackendAndPlatformPrintApiTestSuffix);
 
+IN_PROC_BROWSER_TEST_P(
+    SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest,
+    ServiceLaunched) {
+  chrome::ShowTaskManager(browser());
+
+  // Wait for browser to open with a tab.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+
+  // Now that startup is complete, look through the list of processes in the
+  // Task Manager to see if a Print Backend service has bee started (even
+  // though there has not been any request for printing).
+  CHECK_EQ(DoesPrintBackendServiceTaskExist(), EarlyStartService());
+}
+
 IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
                        UpdatePrintSettings) {
   AddPrinter("printer1");
diff --git a/chrome/browser/privacy_budget/surface_set_equivalence.cc b/chrome/browser/privacy_budget/surface_set_equivalence.cc
index 0e140f5..b18a4ff 100644
--- a/chrome/browser/privacy_budget/surface_set_equivalence.cc
+++ b/chrome/browser/privacy_budget/surface_set_equivalence.cc
@@ -6,14 +6,14 @@
 
 #include <iterator>
 #include <set>
+#include <string_view>
 
 #include "base/containers/contains.h"
 #include "chrome/common/privacy_budget/field_trial_param_conversions.h"
 #include "chrome/common/privacy_budget/privacy_budget_features.h"
 
 SurfaceSetEquivalence::EquivalenceClassIdentifierMap
-SurfaceSetEquivalence::DecodeEquivalenceClassSet(
-    base::StringPiece param_value) {
+SurfaceSetEquivalence::DecodeEquivalenceClassSet(std::string_view param_value) {
   EquivalenceClassIdentifierMap representative_map;
   auto surface_set_set =
       DecodeIdentifiabilityFieldTrialParam<SurfaceSetEquivalentClassesList>(
diff --git a/chrome/browser/privacy_budget/surface_set_equivalence.h b/chrome/browser/privacy_budget/surface_set_equivalence.h
index c6c3e55..366b923 100644
--- a/chrome/browser/privacy_budget/surface_set_equivalence.h
+++ b/chrome/browser/privacy_budget/surface_set_equivalence.h
@@ -5,8 +5,9 @@
 #ifndef CHROME_BROWSER_PRIVACY_BUDGET_SURFACE_SET_EQUIVALENCE_H_
 #define CHROME_BROWSER_PRIVACY_BUDGET_SURFACE_SET_EQUIVALENCE_H_
 
+#include <string_view>
+
 #include "base/containers/flat_map.h"
-#include "base/strings/string_piece.h"
 #include "chrome/browser/privacy_budget/representative_surface_set.h"
 #include "chrome/common/privacy_budget/types.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
@@ -48,7 +49,7 @@
       base::flat_map<blink::IdentifiableSurface, RepresentativeSurface>;
 
   static EquivalenceClassIdentifierMap DecodeEquivalenceClassSet(
-      base::StringPiece encoded_class_set);
+      std::string_view encoded_class_set);
 
   // Maps an IdentifiableSurface to its corresponding representative surface.
   //
diff --git a/chrome/browser/privacy_sandbox/browsing_topics_settings_interactive_uitest.cc b/chrome/browser/privacy_sandbox/browsing_topics_settings_interactive_uitest.cc
new file mode 100644
index 0000000..6d14fb3
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/browsing_topics_settings_interactive_uitest.cc
@@ -0,0 +1,182 @@
+// 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/privacy_sandbox/privacy_sandbox_service.h"
+#include "chrome/browser/privacy_sandbox/privacy_sandbox_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/interaction/interactive_browser_test.h"
+#include "components/prefs/pref_service.h"
+#include "components/privacy_sandbox/canonical_topic.h"
+#include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
+#include "content/public/test/browser_test.h"
+
+namespace {
+
+using DeepQuery = WebContentsInteractionTestUtil::DeepQuery;
+
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPrivacySandboxTopicsElementId);
+
+constexpr char BlockedTopicsListLengthFunc[] =
+    "(el) => el.querySelectorAll('privacy-sandbox-interest-item').length";
+constexpr char BlockedTopicsListFirstTopicIdFunc[] =
+    "(el) => "
+    "el.querySelectorAll('privacy-sandbox-interest-item')[0].interest.topic."
+    "topicId";
+
+DeepQuery GetManageTopicsPageQuery() {
+  return DeepQuery({{"settings-ui", "settings-main", "settings-basic-page",
+                     "settings-privacy-page",
+                     "settings-privacy-sandbox-manage-topics-subpage"}});
+}
+
+DeepQuery GetAdTopicsPageQuery() {
+  return DeepQuery(
+      {{"settings-ui", "settings-main", "settings-basic-page",
+        "settings-privacy-page", "settings-privacy-sandbox-topics-subpage"}});
+}
+
+class PrivacySandboxSettingsTopicsInteractiveTest
+    : public InteractiveBrowserTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        privacy_sandbox::kPrivacySandboxProactiveTopicsBlocking);
+    InteractiveBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    browser()->profile()->GetPrefs()->SetBoolean(
+        prefs::kPrivacySandboxM1TopicsEnabled, true);
+    InteractiveBrowserTest::SetUpOnMainThread();
+  }
+
+  void BlockFirstTopic() {
+    auto* const privacy_sandbox_service =
+        PrivacySandboxServiceFactory::GetForProfile(browser()->profile());
+    privacy_sandbox_service->SetTopicAllowed(
+        privacy_sandbox::CanonicalTopic(browsing_topics::Topic(1),
+                                        /*taxonomy_version=*/2),
+        false);
+  }
+
+  size_t GetBlockedTopicsSize() {
+    return PrivacySandboxServiceFactory::GetForProfile(browser()->profile())
+        ->GetBlockedTopics()
+        .size();
+  }
+
+  int GetBlockedTopicsFirstTopicId() {
+    return PrivacySandboxServiceFactory::GetForProfile(browser()->profile())
+        ->GetBlockedTopics()[0]
+        .topic_id()
+        .value();
+  }
+
+  const DeepQuery firstToggle = GetManageTopicsPageQuery() + "#toggle-1";
+  const DeepQuery blockedTopicsList =
+      GetAdTopicsPageQuery() + "#blockedTopicsList";
+  const DeepQuery firstBlockedItemButton =
+      (blockedTopicsList + "privacy-sandbox-interest-item") + "cr-button";
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Block topic(1) through PS service and validate that it's toggled OFF (checked
+// == false) on the Manage Topics Page.
+IN_PROC_BROWSER_TEST_F(PrivacySandboxSettingsTopicsInteractiveTest,
+                       StartWithOneBlockedTopic) {
+  BlockFirstTopic();
+  RunTestSequence(
+      InstrumentTab(kPrivacySandboxTopicsElementId),
+      NavigateWebContents(kPrivacySandboxTopicsElementId,
+                          GURL(chrome::kPrivacySandboxManageTopicsURL)),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId, firstToggle,
+                      "(el) => el.checked", false),
+      NavigateWebContents(kPrivacySandboxTopicsElementId,
+                          GURL(chrome::kPrivacySandboxAdTopicsURL)));
+}
+
+// Block first topic on Manage Topics Page. Validate that it is toggled off
+// (checked == false). Validate that the PS service returns only 1 blocked topic
+// with an ID of 1. Navigate to the Ad Topics Page and validate topic(1) is
+// blocked topics list.
+IN_PROC_BROWSER_TEST_F(PrivacySandboxSettingsTopicsInteractiveTest,
+                       BlockFirstTopicOnManageTopicsPage) {
+  RunTestSequence(
+      InstrumentTab(kPrivacySandboxTopicsElementId),
+      NavigateWebContents(kPrivacySandboxTopicsElementId,
+                          GURL(chrome::kPrivacySandboxManageTopicsURL)),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId, firstToggle,
+                      "(el) => el.checked"),
+      ExecuteJsAt(kPrivacySandboxTopicsElementId, firstToggle,
+                  "(el) => el.click()"),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId, firstToggle,
+                      "(el) => el.checked", false),
+      CheckResult([this]() { return GetBlockedTopicsSize(); }, 1u,
+                  "Checking that there is only one blocked topic"),
+      CheckResult([this]() { return GetBlockedTopicsFirstTopicId(); }, 1,
+                  "Checking that the one blocked topic is topic(1)"),
+      NavigateWebContents(kPrivacySandboxTopicsElementId,
+                          GURL(chrome::kPrivacySandboxAdTopicsURL)),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId, blockedTopicsList,
+                      BlockedTopicsListLengthFunc, 1),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId, blockedTopicsList,
+                      BlockedTopicsListFirstTopicIdFunc, 1));
+}
+
+// Block topic(1) through PS service and validate it is shown in the blocked
+// topics list of the Ad Topics Page. Unblock the topic and validate PS service
+// has 0 blocked topics. Navigate to Manage Topics Page and make sure the first
+// topic toggle is ON (checked == true).
+IN_PROC_BROWSER_TEST_F(PrivacySandboxSettingsTopicsInteractiveTest,
+                       UnblockOneTopicOnAdTopicsPage) {
+  BlockFirstTopic();
+  RunTestSequence(
+      InstrumentTab(kPrivacySandboxTopicsElementId),
+      NavigateWebContents(kPrivacySandboxTopicsElementId,
+                          GURL(chrome::kPrivacySandboxAdTopicsURL)),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId, blockedTopicsList,
+                      BlockedTopicsListLengthFunc, 1),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId, blockedTopicsList,
+                      BlockedTopicsListFirstTopicIdFunc, 1),
+      ExecuteJsAt(kPrivacySandboxTopicsElementId, firstBlockedItemButton,
+                  "(el) => el.click()"),
+      CheckResult([this]() { return GetBlockedTopicsSize(); }, 0u,
+                  "Checking that there is 0 blocked topics"),
+      NavigateWebContents(kPrivacySandboxTopicsElementId,
+                          GURL(chrome::kPrivacySandboxManageTopicsURL)),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId, firstToggle,
+                      "(el) => el.checked"));
+}
+
+// Validate that first icon is shown to confirm we are querying correctly, then
+// check all icons to make sure default one is not used.
+IN_PROC_BROWSER_TEST_F(PrivacySandboxSettingsTopicsInteractiveTest,
+                       ConfirmDefaultIconIsNotUsed) {
+  RunTestSequence(
+      InstrumentTab(kPrivacySandboxTopicsElementId),
+      NavigateWebContents(kPrivacySandboxTopicsElementId,
+                          GURL(chrome::kPrivacySandboxManageTopicsURL)),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId,
+                      GetManageTopicsPageQuery(),
+                      R"(
+        (el) => Array.from(el.shadowRoot.querySelectorAll('iron-icon')).some(
+                    el => el.icon === 'firstLevelTopics20:artist')
+        )"),
+      CheckJsResultAt(kPrivacySandboxTopicsElementId,
+                      GetManageTopicsPageQuery(),
+                      R"(
+        (el) => Array.from(el.shadowRoot.querySelectorAll('iron-icon')).some(
+                el => el.icon === 'firstLevelTopics20:category')
+        )",
+                      false));
+}
+}  // namespace
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.cc
deleted file mode 100644
index a35ab140..0000000
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.cc
+++ /dev/null
@@ -1,84 +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.
-
-#include "chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.h"
-
-#include "base/containers/fixed_flat_set.h"
-#include "base/metrics/histogram_functions.h"
-#include "chrome/browser/browser_process.h"
-#include "components/privacy_sandbox/privacy_sandbox_features.h"
-#include "components/variations/service/variations_service.h"
-#include "components/variations/service/variations_service_utils.h"
-
-namespace privacy_sandbox {
-
-namespace {
-
-constexpr auto kConsentCountries = base::MakeFixedFlatSet<std::string_view>({
-    "gb", "at", "ax", "be", "bg", "bl", "ch", "cy", "cz", "de", "dk",
-    "ee", "es", "fi", "fr", "gf", "gg", "gi", "gp", "gr", "hr", "hu",
-    "ie", "is", "it", "je", "ke", "li", "lt", "lu", "lv", "mf", "mt",
-    "mq", "nc", "nl", "no", "pf", "pl", "pm", "pt", "qa", "re", "ro",
-    "se", "si", "sk", "sj", "tf", "va", "wf", "yt",
-});
-
-enum class ConfirmationType { Notice, Consent };
-
-bool IsFeatureParamEnabled(ConfirmationType confirmation_type) {
-  switch (confirmation_type) {
-    case ConfirmationType::Notice:
-      return privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get();
-    case ConfirmationType::Consent:
-      return privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get();
-  }
-}
-
-void EmitHistogram(ConfirmationType confirmation_type, bool value) {
-  switch (confirmation_type) {
-    case ConfirmationType::Notice:
-      return base::UmaHistogramBoolean(
-          "Settings.PrivacySandbox.NoticeCheckIsMismatched", value);
-    case ConfirmationType::Consent:
-      return base::UmaHistogramBoolean(
-          "Settings.PrivacySandbox.ConsentCheckIsMismatched", value);
-  }
-}
-
-template <typename FilterFunction>
-bool IsConfirmationRequired(ConfirmationType confirmation_type,
-                            FilterFunction filter_function) {
-  CHECK(g_browser_process);
-  bool is_confirmation_required =
-      privacy_sandbox::kPrivacySandboxSettings4.default_state ==
-          base::FEATURE_ENABLED_BY_DEFAULT &&
-      g_browser_process->variations_service() &&
-      filter_function(variations::GetCurrentCountryCode(
-          g_browser_process->variations_service()));
-
-  if (base::FeatureList::GetInstance()->IsFeatureOverridden(
-          privacy_sandbox::kPrivacySandboxSettings4.name)) {
-    bool is_confirmation_required_override =
-        IsFeatureParamEnabled(confirmation_type);
-    EmitHistogram(confirmation_type, is_confirmation_required !=
-                                         is_confirmation_required_override);
-    return is_confirmation_required_override;
-  }
-  return is_confirmation_required;
-}
-
-}  // namespace
-
-bool IsConsentRequired() {
-  return IsConfirmationRequired(
-      ConfirmationType::Consent,
-      [](std::string_view c) { return kConsentCountries.contains(c); });
-}
-
-bool IsNoticeRequired() {
-  return IsConfirmationRequired(
-      ConfirmationType::Notice,
-      [](std::string_view c) { return !kConsentCountries.contains(c); });
-}
-
-}  // namespace privacy_sandbox
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.h b/chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.h
deleted file mode 100644
index 268b060..0000000
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.h
+++ /dev/null
@@ -1,38 +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.
-
-#ifndef CHROME_BROWSER_PRIVACY_SANDBOX_PRIVACY_SANDBOX_NOTICE_CONFIRMATION_H_
-#define CHROME_BROWSER_PRIVACY_SANDBOX_PRIVACY_SANDBOX_NOTICE_CONFIRMATION_H_
-
-namespace privacy_sandbox {
-
-/**
- * Determines whether Privacy Sandbox Ads consent is required.
- *
- * This function checks a collection of conditions to determine if the permanent
- * variation country is in a region where obtaining consent for the Privacy
- * Sandbox is necessary.  It also considers feature flags and overrides that
- * might influence the consent requirement.
- *
- * Returns `true` if user consent is required for Privacy Sandbox features,
- * `false` otherwise.
- *
- */
-bool IsConsentRequired();
-
-/**
- * Determines whether a the Privay Sandbox Ads notice is required.
- *
- * This function evaluates several criteria related to the Privacy Sandbox
- * feature, the permanent variation country, and potential feature overrides to
- * decide if a notice is necessary.
- *
- * Returns `true` if a privacy notice should be displayed, `false` otherwise.
- *
- */
-bool IsNoticeRequired();
-
-}  // namespace privacy_sandbox
-
-#endif  // CHROME_BROWSER_PRIVACY_SANDBOX_PRIVACY_SANDBOX_NOTICE_CONFIRMATION_H_
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation_browsertest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation_browsertest.cc
deleted file mode 100644
index f2d3fa8..0000000
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation_browsertest.cc
+++ /dev/null
@@ -1,226 +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.
-
-#include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "components/privacy_sandbox/privacy_sandbox_features.h"
-#include "components/variations/service/variations_service.h"
-#include "components/variations/variations_switches.h"
-#include "content/public/test/browser_test.h"
-
-namespace privacy_sandbox {
-namespace {
-
-struct PrivacySandboxConfirmationTestData {
-  // Inputs
-  std::vector<base::test::FeatureRefAndParams> enabled_features;
-  std::vector<base::test::FeatureRef> disabled_features;
-  std::string variation_country;
-  // Expectations
-  bool expect_required;
-  bool expect_mismatch_histogram_true;
-  bool expect_mismatch_histogram_false;
-};
-
-class PrivacySandboxConfirmationTestBase
-    : public InProcessBrowserTest,
-      public testing::WithParamInterface<PrivacySandboxConfirmationTestData> {
- public:
-  PrivacySandboxConfirmationTestBase() {
-    // Disabling the field trial testing config explicitly as some tests here
-    // are specifically for when the feature isn't overridden.
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        variations::switches::kDisableFieldTrialTestingConfig);
-    feature_list_.InitWithFeaturesAndParameters(GetParam().enabled_features,
-                                                GetParam().disabled_features);
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-class PrivacySandboxConsentConfirmationTest
-    : public PrivacySandboxConfirmationTestBase {};
-
-base::test::FeatureRefAndParams ConsentFeature() {
-  return {kPrivacySandboxSettings4,
-          {{kPrivacySandboxSettings4ConsentRequiredName, "true"}}};
-}
-
-IN_PROC_BROWSER_TEST_P(PrivacySandboxConsentConfirmationTest, ConsentTest) {
-  // Setup
-  base::HistogramTester histogram_tester;
-  g_browser_process->variations_service()->OverrideStoredPermanentCountry(
-      GetParam().variation_country);
-
-  EXPECT_EQ(IsConsentRequired(), GetParam().expect_required);
-  histogram_tester.ExpectBucketCount(
-      "Settings.PrivacySandbox.ConsentCheckIsMismatched", true,
-      GetParam().expect_mismatch_histogram_true);
-  histogram_tester.ExpectBucketCount(
-      "Settings.PrivacySandbox.ConsentCheckIsMismatched", false,
-      GetParam().expect_mismatch_histogram_false);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    PrivacySandboxConsentConfirmationTest,
-    testing::Values(
-        // 1. GB
-        // 1.1 GB - Feature Overridden, Consent param set to true.
-        PrivacySandboxConfirmationTestData{
-            .enabled_features = {ConsentFeature()},
-            .variation_country = "gb",
-            // Expectations
-            .expect_required = true,
-            .expect_mismatch_histogram_false = true,
-        },
-        // 1.2 GB - Feature Overridden. consent param not set.
-        PrivacySandboxConfirmationTestData{
-            .enabled_features = {{kPrivacySandboxSettings4, {{}}}},
-            .variation_country = "gb",
-            // Expectations
-            .expect_required = false,
-            .expect_mismatch_histogram_true = true,
-        },
-        // 1.3 GB - Feature Explicitly Disabled.
-        PrivacySandboxConfirmationTestData{
-            .disabled_features = {kPrivacySandboxSettings4},
-            .variation_country = "gb",
-            // Expectations
-            .expect_required = false,
-            .expect_mismatch_histogram_true = true,
-        },
-        // 1.4 GB - Feature Not Set.
-        PrivacySandboxConfirmationTestData{
-            .variation_country = "gb",
-            // Expectations
-            .expect_required = true,
-        },
-        // 2. US
-        // 2.1 US - Feature Overridden, Consent param set to true.
-        PrivacySandboxConfirmationTestData{
-            .enabled_features = {ConsentFeature()},
-            .variation_country = "us",
-            // Expectations
-            .expect_required = true,
-            .expect_mismatch_histogram_true = true,
-        },
-        // 2.2 GB - Feature Overridden. consent param not set.
-        PrivacySandboxConfirmationTestData{
-            .enabled_features = {{kPrivacySandboxSettings4, {{}}}},
-            .variation_country = "us",
-            // Expectations
-            .expect_required = false,
-            .expect_mismatch_histogram_false = true,
-        },
-        // 2.3 GB - Feature Explicitly Disabled.
-        PrivacySandboxConfirmationTestData{
-            .disabled_features = {kPrivacySandboxSettings4},
-            .variation_country = "us",
-            // Expectations
-            .expect_required = false,
-            .expect_mismatch_histogram_false = true,
-        },
-        // 2.4 GB - Feature Not Set.
-        PrivacySandboxConfirmationTestData{
-            .variation_country = "us",
-            // Expectations
-            .expect_required = false,
-        }));
-
-class PrivacySandboxNoticeConfirmationTest
-    : public PrivacySandboxConfirmationTestBase {};
-
-base::test::FeatureRefAndParams NoticeFeature() {
-  return {kPrivacySandboxSettings4,
-          {{kPrivacySandboxSettings4NoticeRequiredName, "true"}}};
-}
-
-IN_PROC_BROWSER_TEST_P(PrivacySandboxNoticeConfirmationTest, NoticeTest) {
-  // Setup
-  base::HistogramTester histogram_tester;
-  g_browser_process->variations_service()->OverrideStoredPermanentCountry(
-      GetParam().variation_country);
-
-  EXPECT_EQ(IsNoticeRequired(), GetParam().expect_required);
-  histogram_tester.ExpectBucketCount(
-      "Settings.PrivacySandbox.NoticeCheckIsMismatched", true,
-      GetParam().expect_mismatch_histogram_true);
-  histogram_tester.ExpectBucketCount(
-      "Settings.PrivacySandbox.NoticeCheckIsMismatched", false,
-      GetParam().expect_mismatch_histogram_false);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    PrivacySandboxNoticeConfirmationTest,
-    testing::Values(
-        // 1. GB
-        // 1.1 GB - Feature Overridden, Notice param set to true.
-        PrivacySandboxConfirmationTestData{
-            .enabled_features = {NoticeFeature()},
-            .variation_country = "gb",
-            // Expectations
-            .expect_required = true,
-            .expect_mismatch_histogram_true = true,
-        },
-        // 1.2 GB - Feature Overridden. notice param not set.
-        PrivacySandboxConfirmationTestData{
-            .enabled_features = {{kPrivacySandboxSettings4, {{}}}},
-            .variation_country = "gb",
-            // Expectations
-            .expect_required = false,
-            .expect_mismatch_histogram_false = true,
-        },
-        // 1.3 GB - Feature Explicitly Disabled.
-        PrivacySandboxConfirmationTestData{
-            .disabled_features = {kPrivacySandboxSettings4},
-            .variation_country = "gb",
-            // Expectations
-            .expect_required = false,
-            .expect_mismatch_histogram_false = true,
-        },
-        // 1.4 GB - Feature Not Set.
-        PrivacySandboxConfirmationTestData{
-            .variation_country = "gb",
-            // Expectations
-            .expect_required = false,
-        },
-        // 2. US
-        // 2.1 US - Feature Overridden, notice param set to true.
-        PrivacySandboxConfirmationTestData{
-            .enabled_features = {NoticeFeature()},
-            .variation_country = "us",
-            // Expectations
-            .expect_required = true,
-            .expect_mismatch_histogram_false = true,
-        },
-        // 2.2 US - Feature Overridden. notice param not set.
-        PrivacySandboxConfirmationTestData{
-            .enabled_features = {{kPrivacySandboxSettings4, {{}}}},
-            .variation_country = "us",
-            // Expectations
-            .expect_required = false,
-            .expect_mismatch_histogram_true = true,
-        },
-        // 2.3 US - Feature Explicitly Disabled.
-        PrivacySandboxConfirmationTestData{
-            .disabled_features = {kPrivacySandboxSettings4},
-            .variation_country = "us",
-            // Expectations
-            .expect_required = false,
-            .expect_mismatch_histogram_true = true,
-        },
-        // 2.4 US - Feature Not Set.
-        PrivacySandboxConfirmationTestData{
-            .variation_country = "us",
-            // Expectations
-            .expect_required = true,
-        }));
-
-}  // namespace
-}  // namespace privacy_sandbox
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl.cc
index 5d04445..70de39f 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl.cc
@@ -19,7 +19,6 @@
 #include "base/time/time.h"
 #include "base/types/optional_util.h"
 #include "build/branding_buildflags.h"
-#include "chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chromeos/components/mgs/managed_guest_session_utils.h"
 #include "components/browsing_topics/browsing_topics_service.h"
@@ -335,7 +334,7 @@
   InformSentimentService(action);
   if (PromptAction::kNoticeAcknowledge == action ||
       PromptAction::kNoticeOpenSettings == action) {
-    if (privacy_sandbox::IsConsentRequired()) {
+    if (privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get()) {
       pref_service_->SetBoolean(prefs::kPrivacySandboxM1EEANoticeAcknowledged,
                                 true);
       // It's possible the user is seeing this notice as part of an upgrade to
@@ -348,7 +347,7 @@
                                   true);
       }
     } else {
-      DCHECK(privacy_sandbox::IsNoticeRequired());
+      DCHECK(privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get());
       pref_service_->SetBoolean(prefs::kPrivacySandboxM1RowNoticeAcknowledged,
                                 true);
       pref_service_->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, true);
@@ -360,14 +359,14 @@
     MaybeCloseOpenPrompts();
 #endif  // !BUILDFLAG(IS_ANDROID)
   } else if (PromptAction::kConsentAccepted == action) {
-    DCHECK(privacy_sandbox::IsConsentRequired());
+    DCHECK(privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get());
     pref_service_->SetBoolean(prefs::kPrivacySandboxM1ConsentDecisionMade,
                               true);
     pref_service_->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, true);
     RecordUpdatedTopicsConsent(
         privacy_sandbox::TopicsConsentUpdateSource::kConfirmation, true);
   } else if (PromptAction::kConsentDeclined == action) {
-    DCHECK(privacy_sandbox::IsConsentRequired());
+    DCHECK(privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get());
     pref_service_->SetBoolean(prefs::kPrivacySandboxM1ConsentDecisionMade,
                               true);
     pref_service_->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, false);
@@ -727,7 +726,7 @@
   }
 
   // EEA
-  if (privacy_sandbox::IsConsentRequired()) {
+  if (privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get()) {
     // Consent decision not made
     if (!pref_service_->GetBoolean(
             prefs::kPrivacySandboxM1ConsentDecisionMade)) {
@@ -755,7 +754,7 @@
   }
 
   // ROW
-  if (privacy_sandbox::IsNoticeRequired()) {
+  if (privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get()) {
     base::UmaHistogramEnumeration(
         privacy_sandbox_prompt_startup_histogram,
         row_notice_acknowledged ? PromptStartupState::kROWNoticeFlowCompleted
@@ -942,7 +941,7 @@
 }
 
 bool PrivacySandboxServiceImpl::TopicsConsentRequired() const {
-  return privacy_sandbox::IsConsentRequired();
+  return privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get();
 }
 
 bool PrivacySandboxServiceImpl::TopicsHasActiveConsent() const {
@@ -1016,14 +1015,14 @@
   }
 
   // If neither a notice nor a consent is required, do not show a prompt.
-  if (!privacy_sandbox::IsNoticeRequired() &&
-      !privacy_sandbox::IsConsentRequired()) {
+  if (!privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get() &&
+      !privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get()) {
     return PromptType::kNone;
   }
 
-  // Only one of the consent or notice should be required.
-  DCHECK(!privacy_sandbox::IsNoticeRequired() ||
-         !privacy_sandbox::IsConsentRequired());
+  // Only one of the consent or notice should be required by Finch parameters.
+  DCHECK(!privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get() ||
+         !privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get());
 
   // If a prompt was suppressed once, for any reason, it will forever remain
   // suppressed and a prompt will not be shown.
@@ -1059,8 +1058,8 @@
   }
 
   if (privacy_sandbox::kPrivacySandboxSettings4RestrictedNotice.Get()) {
-    CHECK(privacy_sandbox::IsConsentRequired() ||
-          privacy_sandbox::IsNoticeRequired());
+    CHECK(privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get() ||
+          privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get());
     if (!pref_service->GetBoolean(
             prefs::kPrivacySandboxM1RestrictedNoticeAcknowledged) &&
         !pref_service->GetBoolean(
@@ -1081,7 +1080,7 @@
     }
   }
 
-  if (privacy_sandbox::IsConsentRequired()) {
+  if (privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get()) {
     if (pref_service->GetBoolean(prefs::kPrivacySandboxM1ConsentDecisionMade)) {
       // Since a consent decision has been made, if the eea notice has already
       // been acknowledged, do not show a prompt; else, show the eea notice.
@@ -1110,7 +1109,7 @@
     }
   }
 
-  DCHECK(privacy_sandbox::IsNoticeRequired());
+  DCHECK(privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get());
 
   // If a user that migrated from EEA to ROW has already completed the EEA
   // consent and notice flow, set the suppression reason as such and do not show
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc
index fa6a8481..c7348fc 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc
@@ -16,7 +16,6 @@
 #include "build/buildflag.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
-#include "chrome/browser/privacy_sandbox/privacy_sandbox_notice_confirmation.h"
 #include "chrome/browser/privacy_sandbox/tracking_protection_onboarding_factory.h"
 #include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -146,7 +145,7 @@
 bool PrivacySandboxSettingsDelegate::HasAppropriateTopicsConsent() const {
   // If the profile doesn't require a release 4 consent, then it always has
   // an appropriate (i.e. not required) Topics consent.
-  if (!privacy_sandbox::IsConsentRequired()) {
+  if (!privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get()) {
     return true;
   }
 
diff --git a/chrome/browser/profiles/profile_attributes_entry.cc b/chrome/browser/profiles/profile_attributes_entry.cc
index a2c759b..18ffb1d 100644
--- a/chrome/browser/profiles/profile_attributes_entry.cc
+++ b/chrome/browser/profiles/profile_attributes_entry.cc
@@ -865,6 +865,10 @@
 }
 
 const base::Value::Dict* ProfileAttributesEntry::GetEntryData() const {
+  if (!prefs_) {
+    return nullptr;
+  }
+
   const base::Value::Dict& attributes =
       prefs_->GetDict(prefs::kProfileAttributes);
   return attributes.FindDict(storage_key_);
diff --git a/chrome/browser/readaloud/android/BUILD.gn b/chrome/browser/readaloud/android/BUILD.gn
index c589689..11141cd 100644
--- a/chrome/browser/readaloud/android/BUILD.gn
+++ b/chrome/browser/readaloud/android/BUILD.gn
@@ -220,6 +220,7 @@
     "//chrome/browser/language/android:junit",
     "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
+    "//chrome/browser/readaloud/android:exceptions_java",
     "//chrome/browser/search_engines/android:java",
     "//chrome/browser/signin/services/android:java",
     "//chrome/browser/tab:java",
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 596745eb..da234f24 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
@@ -32,6 +32,7 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.OnUserLeaveHintObserver;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.readaloud.exceptions.ReadAloudUnsupportedException;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -816,9 +817,10 @@
             return promise;
         }
 
+        final String sanitizedUrl = stripUserData(tab.getUrl()).getSpec();
         PlaybackArgs args =
                 new PlaybackArgs(
-                        stripUserData(tab.getUrl()).getSpec(),
+                        sanitizedUrl,
                         isTranslated ? playbackLanguage : null,
                         mPlaybackHooks.getPlaybackVoiceList(
                                 ReadAloudPrefs.getVoices(getPrefService())),
@@ -840,6 +842,13 @@
                 },
                 exception -> {
                     Log.e(TAG, exception.getMessage());
+                    if (exception instanceof ReadAloudUnsupportedException) {
+                        Log.e(TAG, "Attempting to play a non readable website");
+                        mReadabilityMap.put(sanitizedUrl, false);
+                        mReadabilityRequestTimeMap.put(sanitizedUrl, sClock.currentTimeMillis());
+                        notifyReadabilityMayHaveChanged();
+                    }
+
                     onCreatePlaybackFailed(entrypoint);
                 });
         return promise;
@@ -1202,7 +1211,11 @@
 
                     @Override
                     public void onFailure(Throwable throwable) {
-                        promise.reject(new Exception(throwable));
+                        if (throwable instanceof Exception) {
+                            promise.reject((Exception) throwable);
+                        } else {
+                            promise.reject(new Exception(throwable));
+                        }
                     }
                 });
         return promise;
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 78beec99..af95a8043 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
@@ -65,6 +65,7 @@
 import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.readaloud.ReadAloudMetrics.IneligibilityReason;
+import org.chromium.chrome.browser.readaloud.exceptions.ReadAloudUnsupportedException;
 import org.chromium.chrome.browser.search_engines.SearchEngineType;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.signin.services.UnifiedConsentServiceBridge;
@@ -982,16 +983,58 @@
     @Test
     public void testPlayTab_onFailure() {
         mFakeTranslateBridge.setCurrentLanguage("en");
-        mTab.setGurlOverrideForTesting(new GURL("https://en.wikipedia.org/wiki/Google"));
+        GURL gurl = new GURL("https://en.wikipedia.org/wiki/Google");
+        mTab.setGurlOverrideForTesting(gurl);
+        mController.maybeCheckReadability(gurl);
+        // also check that a generic error doesn't invalidate readability result
+        verify(mHooksImpl).isPageReadable(eq(sTestGURL.getSpec()), mCallbackCaptor.capture());
+        mCallbackCaptor
+                .getValue()
+                .onSuccess(
+                        gurl.getSpec(), /* isReadable= */ true, /* timepointsSupported= */ false);
+        mController.playTab(mTab, ReadAloudController.Entrypoint.MAGIC_TOOLBAR);
+        resolvePromises();
+
+        assertTrue(mController.isReadable(mTab));
+        verify(mPlaybackHooks, times(1))
+                .createPlayback(Mockito.any(), mPlaybackCallbackCaptor.capture());
+        mPlaybackCallbackCaptor.getValue().onFailure(new Throwable());
+        resolvePromises();
+        verify(mPlayerCoordinator, times(1)).playbackFailed();
+        assertTrue(mController.isReadable(mTab));
+    }
+
+    @Test
+    public void testPlayTab_onFailure_unsupportedLink() {
+        mFakeTranslateBridge.setCurrentLanguage("en");
+        GURL gurl = new GURL("https://en.wikipedia.org/wiki/Google");
+        mTab.setGurlOverrideForTesting(gurl);
+        mController.maybeCheckReadability(gurl);
+        // also check that a readAloudUnsupported error does invalidate a false positive readability
+        // result
+        verify(mHooksImpl).isPageReadable(eq(sTestGURL.getSpec()), mCallbackCaptor.capture());
+        mCallbackCaptor
+                .getValue()
+                .onSuccess(
+                        gurl.getSpec(), /* isReadable= */ true, /* timepointsSupported= */ false);
+
+        assertTrue(mController.isReadable(mTab));
         mController.playTab(mTab, ReadAloudController.Entrypoint.MAGIC_TOOLBAR);
         resolvePromises();
 
         verify(mPlaybackHooks, times(1))
                 .createPlayback(Mockito.any(), mPlaybackCallbackCaptor.capture());
-
-        mPlaybackCallbackCaptor.getValue().onFailure(new Throwable());
+        mPlaybackCallbackCaptor
+                .getValue()
+                .onFailure(
+                        new ReadAloudUnsupportedException(
+                                "message",
+                                /* throwable= */ null,
+                                ReadAloudUnsupportedException.RejectionReason
+                                        .UNKNOWN_REJECTION_REASON));
         resolvePromises();
         verify(mPlayerCoordinator, times(1)).playbackFailed();
+        assertFalse(mController.isReadable(mTab));
     }
 
     @Test
diff --git a/chrome/browser/renderer_context_menu/read_write_card_observer.cc b/chrome/browser/renderer_context_menu/read_write_card_observer.cc
index 3ac3741..d2b27c38 100644
--- a/chrome/browser/renderer_context_menu/read_write_card_observer.cc
+++ b/chrome/browser/renderer_context_menu/read_write_card_observer.cc
@@ -45,6 +45,8 @@
 void ReadWriteCardObserver::OnContextMenuShown(
     const content::ContextMenuParams& params,
     const gfx::Rect& bounds_in_screen) {
+  bounds_in_screen_ = bounds_in_screen;
+
   chromeos::ReadWriteCardsManager* cards_manager =
       chromeos::ReadWriteCardsManager::Get();
   CHECK(cards_manager);
@@ -52,21 +54,19 @@
   cards_manager->FetchController(
       params, proxy_->GetBrowserContext(),
       base::BindOnce(&ReadWriteCardObserver::OnFetchControllers,
-                     weak_factory_.GetWeakPtr(), params, bounds_in_screen));
+                     weak_factory_.GetWeakPtr(), params));
 }
 
 void ReadWriteCardObserver::OnContextMenuViewBoundsChanged(
     const gfx::Rect& bounds_in_screen) {
+  bounds_in_screen_ = bounds_in_screen;
+
   for (auto controller : read_write_card_controllers_) {
     if (!controller) {
       continue;
     }
-
-    bounds_in_screen_ = bounds_in_screen;
-
-    SetUiControllerContextMenuBounds(bounds_in_screen);
-
-    controller->OnAnchorBoundsChanged(bounds_in_screen);
+    SetUiControllerContextMenuBounds(bounds_in_screen_);
+    controller->OnAnchorBoundsChanged(bounds_in_screen_);
   }
 }
 
@@ -105,16 +105,13 @@
 
 void ReadWriteCardObserver::OnFetchControllers(
     const content::ContextMenuParams& params,
-    const gfx::Rect& bounds_in_screen,
     std::vector<base::WeakPtr<chromeos::ReadWriteCardController>> controllers) {
   if (controllers.empty()) {
     read_write_card_controllers_.clear();
     return;
   }
 
-  bounds_in_screen_ = bounds_in_screen;
-
-  SetUiControllerContextMenuBounds(bounds_in_screen);
+  SetUiControllerContextMenuBounds(bounds_in_screen_);
 
   content::RenderFrameHost* focused_frame =
       proxy_->GetWebContents()->GetFocusedFrame();
diff --git a/chrome/browser/renderer_context_menu/read_write_card_observer.h b/chrome/browser/renderer_context_menu/read_write_card_observer.h
index 9d13b2c..5e93a65 100644
--- a/chrome/browser/renderer_context_menu/read_write_card_observer.h
+++ b/chrome/browser/renderer_context_menu/read_write_card_observer.h
@@ -50,7 +50,6 @@
 
   void OnFetchControllers(
       const content::ContextMenuParams& params,
-      const gfx::Rect& bounds_in_screen,
       std::vector<base::WeakPtr<chromeos::ReadWriteCardController>>
           controllers);
 
diff --git a/chrome/browser/renderer_context_menu/read_write_card_observer_unittest.cc b/chrome/browser/renderer_context_menu/read_write_card_observer_unittest.cc
index a97d0888..f3b96d4 100644
--- a/chrome/browser/renderer_context_menu/read_write_card_observer_unittest.cc
+++ b/chrome/browser/renderer_context_menu/read_write_card_observer_unittest.cc
@@ -93,7 +93,6 @@
     auto controllers = InitControllers();
 
     observer_->OnFetchControllers(content::ContextMenuParams(),
-                                  /*bounds_in_screen=*/gfx::Rect(),
                                   controllers);
   }
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index e237d92..d182bc1 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -92,6 +92,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/exclusive_access/keyboard_lock_controller.h"
@@ -135,7 +136,7 @@
 #include "components/compose/core/browser/compose_features.h"
 #include "components/custom_handlers/protocol_handler.h"
 #include "components/download/public/common/download_url_parameters.h"
-#include "components/feature_engagement/public/feature_constants.h"
+#include "components/feed/feed_feature_list.h"
 #include "components/google/core/common/google_util.h"
 #include "components/guest_view/browser/guest_view_base.h"
 #include "components/language/core/browser/language_model_manager.h"
@@ -848,8 +849,7 @@
       autofill_context_menu_manager_(
           autofill::PersonalDataManagerFactory::GetForProfile(GetProfile()),
           this,
-          &menu_model_),
-      new_badge_tracker_(GetProfile()) {
+          &menu_model_) {
   if (!g_custom_id_ranges_initialized) {
     g_custom_id_ranges_initialized = true;
     SetContentCustomCommandIdRange(IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
@@ -2433,10 +2433,8 @@
       // TODO(b/303646344): Remove new feature tag when no longer new.
       menu_model_.SetIsNewFeatureAt(
           menu_model_.GetItemCount() - 1,
-          new_badge_tracker_.TryShowNewBadge(
-              feature_engagement::kIPHComposeMenuNewBadgeFeature,
-              &compose::features::kEnableCompose));
-
+          GetBrowser()->window()->MaybeShowNewBadgeFor(
+              compose::features::kEnableCompose));
       render_separator = true;
     }
   }
@@ -4050,7 +4048,8 @@
         autofill::FieldGlobalId(
             frame_token, autofill::FieldRendererId(params_.field_renderer_id)),
         compose::ComposeManagerImpl::UiEntryPoint::kContextMenu);
-    new_badge_tracker_.ActionPerformed("compose_menu_item_activated");
+    GetBrowser()->window()->NotifyPromoFeatureUsed(
+        compose::features::kEnableCompose);
   } else {
     compose::LogOpenComposeDialogResult(
         compose::OpenComposeDialogResult::kNoContentAutofillDriver);
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index e83fec15..9634f1d 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -18,7 +18,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ui/autofill/autofill_context_menu_manager.h"
-#include "chrome/browser/ui/user_education/scoped_new_badge_tracker.h"
 #include "components/compose/buildflags.h"
 #include "components/custom_handlers/protocol_handler_registry.h"
 #include "components/lens/buildflags.h"
@@ -547,8 +546,6 @@
   // Responsible for handling autofill related context menu items.
   autofill::AutofillContextMenuManager autofill_context_menu_manager_;
 
-  ScopedNewBadgeTracker new_badge_tracker_;
-
   base::WeakPtrFactory<RenderViewContextMenu> weak_pointer_factory_{this};
 };
 
diff --git a/chrome/browser/resources/about_sys/BUILD.gn b/chrome/browser/resources/about_sys/BUILD.gn
index c322c7f41..c1ff066a 100644
--- a/chrome/browser/resources/about_sys/BUILD.gn
+++ b/chrome/browser/resources/about_sys/BUILD.gn
@@ -26,4 +26,5 @@
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources/js:build_ts",
   ]
+  webui_host = "system"
 }
diff --git a/chrome/browser/resources/access_code_cast/BUILD.gn b/chrome/browser/resources/access_code_cast/BUILD.gn
index b92cd5cd..5220796b 100644
--- a/chrome/browser/resources/access_code_cast/BUILD.gn
+++ b/chrome/browser/resources/access_code_cast/BUILD.gn
@@ -36,4 +36,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "access-code-cast"
 }
diff --git a/chrome/browser/resources/accessibility/BUILD.gn b/chrome/browser/resources/accessibility/BUILD.gn
index e0343f9..c2ec862e 100644
--- a/chrome/browser/resources/accessibility/BUILD.gn
+++ b/chrome/browser/resources/accessibility/BUILD.gn
@@ -21,6 +21,7 @@
 
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   ts_deps = [ "//ui/webui/resources/js:build_ts" ]
+  webui_host = "accessibility"
 }
 
 if (is_chromeos_lacros) {
diff --git a/chrome/browser/resources/app_home/BUILD.gn b/chrome/browser/resources/app_home/BUILD.gn
index 7d6400a..8ccc2233 100644
--- a/chrome/browser/resources/app_home/BUILD.gn
+++ b/chrome/browser/resources/app_home/BUILD.gn
@@ -41,4 +41,5 @@
     "//ui/webui/resources/mojo:build_ts",
   ]
   ts_definitions = [ "//tools/typescript/definitions/metrics_private.d.ts" ]
+  webui_host = "apps"
 }
diff --git a/chrome/browser/resources/app_service_internals/BUILD.gn b/chrome/browser/resources/app_service_internals/BUILD.gn
index 427e5bb..e4d18325 100644
--- a/chrome/browser/resources/app_service_internals/BUILD.gn
+++ b/chrome/browser/resources/app_service_internals/BUILD.gn
@@ -18,4 +18,5 @@
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "app-service-internals"
 }
diff --git a/chrome/browser/resources/ash/settings/common/app_management/reducers.ts b/chrome/browser/resources/ash/settings/common/app_management/reducers.ts
index c192619..239c57a 100644
--- a/chrome/browser/resources/ash/settings/common/app_management/reducers.ts
+++ b/chrome/browser/resources/ash/settings/common/app_management/reducers.ts
@@ -13,7 +13,7 @@
 import {assertNotReached} from 'chrome://resources/js/assert.js';
 
 import {AddAppAction, AppManagementActions, ChangeAppAction, RemoveAppAction} from './actions.js';
-import {AppManagementPageState, AppMap} from './store';
+import {AppManagementPageState, AppMap} from './store.js';
 
 function addApp(apps: AppMap, action: AddAppAction): AppMap {
   if (apps[action.app.id]) {
diff --git a/chrome/browser/resources/ash/settings/device_page/keyboard_remap_modifier_key_row.ts b/chrome/browser/resources/ash/settings/device_page/keyboard_remap_modifier_key_row.ts
index 6a71ee7f..9551bd5 100644
--- a/chrome/browser/resources/ash/settings/device_page/keyboard_remap_modifier_key_row.ts
+++ b/chrome/browser/resources/ash/settings/device_page/keyboard_remap_modifier_key_row.ts
@@ -16,6 +16,7 @@
 
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
 import {assertNotReached} from 'chrome://resources/js/assert.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PolymerElementProperties} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -205,11 +206,20 @@
         value: ModifierKey.kAssistant,
         name: this.i18n('perDeviceKeyboardKeyAssistant'),
       },
-      {
-        value: ModifierKey.kVoid,
-        name: this.i18n('perDeviceKeyboardKeyDisabled'),
-      },
     ];
+
+    if (loadTimeData.getBoolean('enableModifierSplit')) {
+      this.keyMapTargets.push({
+        value: ModifierKey.kRightAlt,
+        name: this.i18n('perDeviceKeyboardKeyRightAlt'),
+      });
+    }
+
+    // Push void last so that right alt is added before it.
+    this.keyMapTargets.push({
+      value: ModifierKey.kVoid,
+      name: this.i18n('perDeviceKeyboardKeyDisabled'),
+    });
   }
 
   private getKeyIcon(): KeyIcon {
diff --git a/chrome/browser/resources/ash/settings/internet_page/settings_traffic_counters.html b/chrome/browser/resources/ash/settings/internet_page/settings_traffic_counters.html
index 2ee18ef8..1bcad39 100644
--- a/chrome/browser/resources/ash/settings/internet_page/settings_traffic_counters.html
+++ b/chrome/browser/resources/ash/settings/internet_page/settings_traffic_counters.html
@@ -3,6 +3,10 @@
     align-items: center;
     display: flex;
   }
+
+  #resetDayHelpTooltip {
+    margin-inline-end: 15px;
+  }
 </style>
 <div class="settings-box single-column stretch first">
   [[i18n('TrafficCountersDataUsageDifferentFromProviderLabel')]]
@@ -32,6 +36,10 @@
         [[i18n('TrafficCountersDataUsageAutoResetDayOfMonthSubLabel')]]
       </div>
     </div>
+    <cr-tooltip-icon id="resetDayHelpTooltip"
+      icon-class="cr:help-outline"
+      tooltip-text="[[i18n('TrafficCountersDataUsageResetDayTooltipText')]]">
+    </cr-tooltip-icon>
     <select class="md-select" id="resetDayList" value="{{resetDay_::change}}"
         on-change="onResetDaySelected_">
       <template is="dom-repeat" items="[[getDaysList_()]]">
diff --git a/chrome/browser/resources/ash/settings/multidevice_page/multidevice_feature_mixin.ts b/chrome/browser/resources/ash/settings/multidevice_page/multidevice_feature_mixin.ts
index b2f96198..6a2bc1a9 100644
--- a/chrome/browser/resources/ash/settings/multidevice_page/multidevice_feature_mixin.ts
+++ b/chrome/browser/resources/ash/settings/multidevice_page/multidevice_feature_mixin.ts
@@ -7,8 +7,8 @@
  * intended to facilitate passing data between elements in the MultiDevice page
  * cleanly and concisely. It includes some constants and utility methods.
  */
-
 import {I18nMixin, I18nMixinInterface} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {isRevampWayfindingEnabled} from '../common/load_time_booleans.js';
@@ -85,6 +85,13 @@
         }
 
         /**
+         * Whether the instant hotspot rebrand feature flag is enabled
+         */
+        isInstantHotspotRebrandEnabled(): boolean {
+          return loadTimeData.getBoolean('isInstantHotspotRebrandEnabled');
+        }
+
+        /**
          * Whether an individual feature is allowed by policy.
          */
         isFeatureAllowedByPolicy(feature: MultiDeviceFeature): boolean {
@@ -214,7 +221,9 @@
             case MultiDeviceFeature.BETTER_TOGETHER_SUITE:
               return this.i18n('multideviceSetupItemHeading');
             case MultiDeviceFeature.INSTANT_TETHERING:
-              return this.i18n('multideviceInstantTetheringItemTitle');
+              return this.isInstantHotspotRebrandEnabled() ?
+                  this.i18n('multideviceInstantHotspotItemTitle') :
+                  this.i18n('multideviceInstantTetheringItemTitle');
             case MultiDeviceFeature.SMART_LOCK:
               return this.i18n('multideviceSmartLockItemTitle');
             case MultiDeviceFeature.PHONE_HUB:
diff --git a/chrome/browser/resources/ash/settings/multidevice_page/multidevice_tether_item.ts b/chrome/browser/resources/ash/settings/multidevice_page/multidevice_tether_item.ts
index e84a88b..3d1da45 100644
--- a/chrome/browser/resources/ash/settings/multidevice_page/multidevice_tether_item.ts
+++ b/chrome/browser/resources/ash/settings/multidevice_page/multidevice_tether_item.ts
@@ -27,7 +27,7 @@
 
 import {castExists} from '../assert_extras.js';
 import {isRevampWayfindingEnabled} from '../common/load_time_booleans.js';
-import {Constructor} from '../common/types';
+import {Constructor} from '../common/types.js';
 import {routes} from '../router.js';
 
 import {MultiDeviceFeatureMixin, MultiDeviceFeatureMixinInterface} from './multidevice_feature_mixin.js';
diff --git a/chrome/browser/resources/ash/settings/os_people_page/fingerprint_list_subpage.html b/chrome/browser/resources/ash/settings/os_people_page/fingerprint_list_subpage.html
index b56a29b..5caa2ab 100644
--- a/chrome/browser/resources/ash/settings/os_people_page/fingerprint_list_subpage.html
+++ b/chrome/browser/resources/ash/settings/os_people_page/fingerprint_list_subpage.html
@@ -29,6 +29,11 @@
     line-height: 18px;
   }
 
+  #fingerprintsList {
+    /* Spacing for fingerprint label input outline. */
+    padding-inline-start: 2px;
+  }
+
   #lockScreenFingerprintWarning {
     margin-inline-start: 4px;
     color: var(--cros-sys-on_surface_variant);
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.html b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.html
index 96fd1a0..6ab9cce 100644
--- a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.html
+++ b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.html
@@ -3,6 +3,12 @@
     border-bottom: var(--cr-separator-line);
   }
 
+  #geolocationModeDescriptionDiv {
+    padding-inline-start: var(--cr-section-padding);
+    padding-inline-end: var(--cr-section-padding);
+    padding-bottom: var(--cr-section-vertical-padding);
+  }
+
   h2 {
     padding-inline-start: var(--cr-section-padding);
   }
@@ -48,6 +54,12 @@
   </settings-dropdown-menu>
 </div>
 
+<div id="geolocationModeDescriptionDiv" class="cr-secondary-text">
+    <localized-link localized-string="[[geolocationModeDescriptionText_]]"
+        link-url="$i18n{systemGeolocationDialogLearnMoreUrl}">
+    </localized-link>
+</div>
+
 <div class="hr"></div>
 <div id="appsSection">
   <h2 id="appsSectionTitle">$i18n{privacyHubAppsSectionTitle}</h2>
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.ts b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.ts
index 473902d6ff..a16b5c0 100644
--- a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.ts
+++ b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.ts
@@ -95,6 +95,14 @@
         },
       },
       /**
+       * Show the right description text for the selected geolocation mode.
+       */
+      geolocationModeDescriptionText_: {
+        type: TrustedHTML,
+        computed: 'computeGeolocationModeDescriptionText_(' +
+            'prefs.ash.user.geolocation_access_level.value)',
+      },
+      /**
        * Apps with location permission defined.
        */
       appList_: {
@@ -165,6 +173,7 @@
   ]);
 
   private geolocationMapTargets_: DropdownMenuOptionList;
+  private geolocationModeDescriptionText_: string;
   private appList_: App[];
   private appPermissionsObserverReceiver_: AppPermissionsObserverReceiver|null;
   private isSecondaryUser_: boolean;
@@ -289,6 +298,24 @@
         PermissionType.kLocation);
   }
 
+  private computeGeolocationModeDescriptionText_(): TrustedHTML {
+    const accessLevel: GeolocationAccessLevel =
+        this.getPref<GeolocationAccessLevel>(
+                'ash.user.geolocation_access_level')
+            .value;
+    switch (accessLevel) {
+      case GeolocationAccessLevel.ALLOWED:
+        return this.i18nAdvanced('geolocationAllowedModeDescription');
+      case GeolocationAccessLevel.ONLY_ALLOWED_FOR_SYSTEM:
+        return this.i18nAdvanced(
+            'geolocationOnlyAllowedForSystemModeDescription');
+      case GeolocationAccessLevel.DISALLOWED:
+        return this.i18nAdvanced('geolocationBlockedModeDescription');
+      default:
+        assertExhaustive(accessLevel);
+    }
+  }
+
   private recordMetric_(): void {
     const accessLevel = this.$.geolocationDropdown.pref!.value;
 
diff --git a/chrome/browser/resources/browsing_topics/BUILD.gn b/chrome/browser/resources/browsing_topics/BUILD.gn
index 517999f2..f9a5bd61 100644
--- a/chrome/browser/resources/browsing_topics/BUILD.gn
+++ b/chrome/browser/resources/browsing_topics/BUILD.gn
@@ -21,4 +21,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "topics-internals"
 }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/macros/reset_cursor_macro.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/macros/reset_cursor_macro.ts
index 67c5fe77..d003930 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/macros/reset_cursor_macro.ts
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/macros/reset_cursor_macro.ts
@@ -5,7 +5,7 @@
 import {Macro, RunMacroResult} from '/common/action_fulfillment/macros/macro.js';
 import {MacroName} from '/common/action_fulfillment/macros/macro_names.js';
 
-import {MouseController} from '../mouse_controller';
+import {MouseController} from '../mouse_controller.js';
 
 /**
  * Class that implements a macro to reset the cursor position.
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/accessibility_private_mv2.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/accessibility_private_mv2.d.ts
index 94c1e41..7cadd34 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/accessibility_private_mv2.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/accessibility_private_mv2.d.ts
@@ -13,7 +13,7 @@
  * regenerate.
  */
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/audio.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/audio.d.ts
index 51345e34..d0e190b3 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/audio.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/audio.d.ts
@@ -9,7 +9,7 @@
  * -g ts_definitions` to regenerate.
  */
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts
index c3c4ec8..89bd467 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts
@@ -30,7 +30,6 @@
       CHECKED_STATE_CHANGED = 'checkedStateChanged',
       CHECKED_STATE_DESCRIPTION_CHANGED = 'checkedStateDescriptionChanged',
       CHILDREN_CHANGED = 'childrenChanged',
-      CLASS_NAME_CHANGED = 'classNameChanged',
       CLICKED = 'clicked',
       COLLAPSED = 'collapsed',
       CONTROLS_CHANGED = 'controlsChanged',
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/clipboard_mv2.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/clipboard_mv2.d.ts
index 5aaee3f..da8038a2 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/clipboard_mv2.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/clipboard_mv2.d.ts
@@ -11,7 +11,7 @@
  * extensions/common/api/clipboard.idl -g ts_definitions` to regenerate.
  */
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/input_ime.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/input_ime.d.ts
index 3f86e8cf..33a1d6af 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/input_ime.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/input_ime.d.ts
@@ -9,7 +9,7 @@
  * chrome/common/extensions/api/input_ime.json -g ts_definitions` to regenerate.
  */
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/input_method_private.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/input_method_private.d.ts
index ca415c29..fae894c 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/input_method_private.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/input_method_private.d.ts
@@ -10,7 +10,7 @@
  * regenerate.
  */
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/settings_private_mv2.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/settings_private_mv2.d.ts
index c46462b..5a9709e1 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/settings_private_mv2.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/settings_private_mv2.d.ts
@@ -7,7 +7,7 @@
 // TODO(b/260590502): Delete this after MV3 migration.
 // TODO(crbug.com/1203307): Auto-generate this file.
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/speech_recognition_private.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/speech_recognition_private.d.ts
index 36f43b2..42f0192 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/speech_recognition_private.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/speech_recognition_private.d.ts
@@ -10,7 +10,7 @@
  * ts_definitions` to regenerate.
  */
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/storage_mv2.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/storage_mv2.d.ts
index f28338c..398a0c5 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/storage_mv2.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/storage_mv2.d.ts
@@ -12,7 +12,7 @@
 // This file exists because MV3 supports promises and MV2 does not.
 // TODO(b/260590502): Delete this after MV3 migration.
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/tts.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/tts.d.ts
index 1375359..0eb7a305 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/tts.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/tts.d.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 {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 // TODO(crbug.com/1203307): Auto-generate this file.
 
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/tts_engine.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/tts_engine.d.ts
index 2032295..143f536 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/tts_engine.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/tts_engine.d.ts
@@ -10,7 +10,7 @@
  * regenerate.
  */
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/windows.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/windows.d.ts
index d1a09ba..c84dde9 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/windows.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/windows.d.ts
@@ -9,7 +9,7 @@
  * chrome/common/extensions/api/windows.json -g ts_definitions` to regenerate.
  */
 
-import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event';
+import {ChromeEvent} from '../../../../../../tools/typescript/definitions/chrome_event.js';
 
 declare global {
   export namespace chrome {
diff --git a/chrome/browser/resources/chromeos/desk_api/background.ts b/chrome/browser/resources/chromeos/desk_api/background.ts
index 6ffb3ce..82046849 100644
--- a/chrome/browser/resources/chromeos/desk_api/background.ts
+++ b/chrome/browser/resources/chromeos/desk_api/background.ts
@@ -7,10 +7,10 @@
  * This module routes Desk API calls to the service worker.
  */
 
-import {ChromeApi, DeskApiBridgeRequest, MessageSender, SendMessageResponse} from './desk_api_types';
-import {ResponseType} from './message_type';
-import {ServiceWorkerFactory} from './service_worker';
-import {DeskApi, NotificationApi} from './types';
+import {ChromeApi, DeskApiBridgeRequest, MessageSender, SendMessageResponse} from './desk_api_types.js';
+import {ResponseType} from './message_type.js';
+import {ServiceWorkerFactory} from './service_worker.js';
+import {DeskApi, NotificationApi} from './types.js';
 
 /**
  * Extension entry point.
diff --git a/chrome/browser/resources/chromeos/desk_api/background_main.ts b/chrome/browser/resources/chromeos/desk_api/background_main.ts
index 92c63ac..9557158 100644
--- a/chrome/browser/resources/chromeos/desk_api/background_main.ts
+++ b/chrome/browser/resources/chromeos/desk_api/background_main.ts
@@ -6,9 +6,9 @@
  * @fileoverview Entry file for extension
  */
 
-import {Background} from './background';
-import {DeskApiImpl} from './desk_api_impl';
-import {NotificationApiImpl} from './notification_api_impl';
+import {Background} from './background.js';
+import {DeskApiImpl} from './desk_api_impl.js';
+import {NotificationApiImpl} from './notification_api_impl.js';
 
 function main() {
   return new Background(chrome, new DeskApiImpl(), new NotificationApiImpl());
diff --git a/chrome/browser/resources/chromeos/desk_api/desk_api_impl.ts b/chrome/browser/resources/chromeos/desk_api/desk_api_impl.ts
index 0f9da30d..8ad0501 100644
--- a/chrome/browser/resources/chromeos/desk_api/desk_api_impl.ts
+++ b/chrome/browser/resources/chromeos/desk_api/desk_api_impl.ts
@@ -6,7 +6,7 @@
  * @fileoverview The implementation of Desk API
  */
 
-import {DeskApi, LaunchOptions, RemoveDeskOptions, WindowProperties} from './types';
+import {DeskApi, LaunchOptions, RemoveDeskOptions, WindowProperties} from './types.js';
 
 
 /**
diff --git a/chrome/browser/resources/chromeos/desk_api/desk_api_types.d.ts b/chrome/browser/resources/chromeos/desk_api/desk_api_types.d.ts
index 0f01fc6..ca62237 100644
--- a/chrome/browser/resources/chromeos/desk_api/desk_api_types.d.ts
+++ b/chrome/browser/resources/chromeos/desk_api/desk_api_types.d.ts
@@ -7,8 +7,8 @@
  * with extension.
  */
 
-import {RequestType, ResponseType} from './message_type';
-import {Desk, DeskApi, LaunchOptions, RemoveDeskOperands, SwitchDeskOperands, WindowProperties} from './types';
+import {RequestType, ResponseType} from './message_type.js';
+import {Desk, DeskApi, LaunchOptions, RemoveDeskOperands, SwitchDeskOperands, WindowProperties} from './types.js';
 
 
 /**
diff --git a/chrome/browser/resources/chromeos/desk_api/notification_api_impl.ts b/chrome/browser/resources/chromeos/desk_api/notification_api_impl.ts
index bbd83fb..6b5f681 100644
--- a/chrome/browser/resources/chromeos/desk_api/notification_api_impl.ts
+++ b/chrome/browser/resources/chromeos/desk_api/notification_api_impl.ts
@@ -6,7 +6,7 @@
  * @fileoverview The implementation of Notification API
  */
 
-import {ClickEventListener, NotificationApi, NotificationOptions, VoidCallback} from './types';
+import {ClickEventListener, NotificationApi, NotificationOptions, VoidCallback} from './types.js';
 
 /**
  * Provides the implementation for Notification API.
diff --git a/chrome/browser/resources/chromeos/desk_api/service_worker.ts b/chrome/browser/resources/chromeos/desk_api/service_worker.ts
index 6b49630..9708e1b 100644
--- a/chrome/browser/resources/chromeos/desk_api/service_worker.ts
+++ b/chrome/browser/resources/chromeos/desk_api/service_worker.ts
@@ -7,9 +7,9 @@
  * class as declared in types.d.ts
  */
 
-import {DeskApiBridgeRequest, DeskApiBridgeResponse, MessageSender, ServiceWorker} from './desk_api_types';
-import {EventType, RequestType, ResponseType} from './message_type';
-import {Desk, DeskApi, GetDeskByIdOperands, LaunchOptions, NotificationApi, NotificationOptions, RemoveDeskOperands, SwitchDeskOperands, WindowProperties} from './types';
+import {DeskApiBridgeRequest, DeskApiBridgeResponse, MessageSender, ServiceWorker} from './desk_api_types.js';
+import {EventType, RequestType, ResponseType} from './message_type.js';
+import {Desk, DeskApi, GetDeskByIdOperands, LaunchOptions, NotificationApi, NotificationOptions, RemoveDeskOperands, SwitchDeskOperands, WindowProperties} from './types.js';
 
 
 /**
diff --git a/chrome/browser/resources/chromeos/emoji_picker/events.ts b/chrome/browser/resources/chromeos/emoji_picker/events.ts
index 9226fa4..029edca 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/events.ts
+++ b/chrome/browser/resources/chromeos/emoji_picker/events.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 {CategoryEnum, Emoji, Gender, Tone, VisualContent} from './types';
+import {CategoryEnum, Emoji, Gender, Tone, VisualContent} from './types.js';
 
 export type CategoryButtonClickEvent =
     CustomEvent<{categoryName: CategoryEnum}>;
diff --git a/chrome/browser/resources/chromeos/quickoffice b/chrome/browser/resources/chromeos/quickoffice
deleted file mode 160000
index cf37f7d..0000000
--- a/chrome/browser/resources/chromeos/quickoffice
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit cf37f7d861973f9f7f3e937358a452406b16093a
diff --git a/chrome/browser/resources/commerce/BUILD.gn b/chrome/browser/resources/commerce/BUILD.gn
index 1e099e5..88b78be 100644
--- a/chrome/browser/resources/commerce/BUILD.gn
+++ b/chrome/browser/resources/commerce/BUILD.gn
@@ -18,4 +18,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "compare"
 }
diff --git a/chrome/browser/resources/commerce/app.html b/chrome/browser/resources/commerce/app.html
index e700290..8ffb8e2 100644
--- a/chrome/browser/resources/commerce/app.html
+++ b/chrome/browser/resources/commerce/app.html
@@ -20,6 +20,6 @@
   <div class="summary">
     <h3>$i18n{summaryTitle}</h3>
     <div id="message">[[message_]]</div>
-    <div>Last tracked ID: [[lastSubscribedId]]</div>
+    <div>[[status_]]</div>
   </div>
 </div>
diff --git a/chrome/browser/resources/commerce/app.ts b/chrome/browser/resources/commerce/app.ts
index d296a15..cfca4ef 100644
--- a/chrome/browser/resources/commerce/app.ts
+++ b/chrome/browser/resources/commerce/app.ts
@@ -6,7 +6,6 @@
 
 import type {BrowserProxy} from '//resources/cr_components/commerce/browser_proxy.js';
 import {BrowserProxyImpl} from '//resources/cr_components/commerce/browser_proxy.js';
-import type {BookmarkProductInfo} from '//resources/cr_components/commerce/shopping_service.mojom-webui.js';
 import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -32,21 +31,35 @@
   }
 
   private shoppingApi_: BrowserProxy = BrowserProxyImpl.getInstance();
-  private lastSubscribedId: string = 'N/A';
+  private status_: string;
 
   constructor() {
     super();
     ColorChangeUpdater.forDocument().start();
   }
 
-  override connectedCallback() {
+  override async connectedCallback() {
     super.connectedCallback();
+    const params = new URLSearchParams(window.location.search);
+    const urlsParam = params.get('urls');
+    if (!urlsParam) {
+      return;
+    }
 
-    const callbackRouter = this.shoppingApi_.getCallbackRouter();
-    callbackRouter.priceTrackedForBookmark.addListener(
-        (product: BookmarkProductInfo) => {
-          this.lastSubscribedId = product.info.clusterId.toString();
-        });
+    let urls: string[] = [];
+    try {
+      urls = JSON.parse(urlsParam);
+    } catch (_) {
+      return;
+    }
+
+    const {productSpecs} =
+        await this.shoppingApi_.getProductSpecificationsForUrls(
+            urls.map(url => {
+              return {url};
+            }));
+    this.status_ =
+        `Found: ${urls.length}. Resolved: ${productSpecs.products.length}`;
   }
 }
 
diff --git a/chrome/browser/resources/components/BUILD.gn b/chrome/browser/resources/components/BUILD.gn
index 48601b7..772e209 100644
--- a/chrome/browser/resources/components/BUILD.gn
+++ b/chrome/browser/resources/components/BUILD.gn
@@ -14,4 +14,5 @@
 
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   ts_deps = [ "//ui/webui/resources/js:build_ts" ]
+  webui_host = "components"
 }
diff --git a/chrome/browser/resources/compose/BUILD.gn b/chrome/browser/resources/compose/BUILD.gn
index d9ace91..c8ae797 100644
--- a/chrome/browser/resources/compose/BUILD.gn
+++ b/chrome/browser/resources/compose/BUILD.gn
@@ -38,4 +38,6 @@
     "$root_gen_dir/chrome/common/compose/compose.mojom-webui.ts",
     "$root_gen_dir/components/compose/core/browser/compose_enums.mojom-webui.ts",
   ]
+
+  webui_host = "chrome-untrusted://compose"
 }
diff --git a/chrome/browser/resources/connectors_internals/BUILD.gn b/chrome/browser/resources/connectors_internals/BUILD.gn
index 0798e7c..fb90185 100644
--- a/chrome/browser/resources/connectors_internals/BUILD.gn
+++ b/chrome/browser/resources/connectors_internals/BUILD.gn
@@ -30,4 +30,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "connectors-internals"
 }
diff --git a/chrome/browser/resources/device_log/BUILD.gn b/chrome/browser/resources/device_log/BUILD.gn
index 81f59d9..2395a0a2 100644
--- a/chrome/browser/resources/device_log/BUILD.gn
+++ b/chrome/browser/resources/device_log/BUILD.gn
@@ -17,4 +17,5 @@
 
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   ts_deps = [ "//ui/webui/resources/js:build_ts" ]
+  webui_host = "device-log"
 }
diff --git a/chrome/browser/resources/discards/BUILD.gn b/chrome/browser/resources/discards/BUILD.gn
index 70ccf0f..8f124fb 100644
--- a/chrome/browser/resources/discards/BUILD.gn
+++ b/chrome/browser/resources/discards/BUILD.gn
@@ -43,4 +43,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "discards"
 }
diff --git a/chrome/browser/resources/dlp_internals/BUILD.gn b/chrome/browser/resources/dlp_internals/BUILD.gn
index 1bc7a255c..be3e39b1 100644
--- a/chrome/browser/resources/dlp_internals/BUILD.gn
+++ b/chrome/browser/resources/dlp_internals/BUILD.gn
@@ -22,4 +22,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "dlp-internals"
 }
diff --git a/chrome/browser/resources/engagement/BUILD.gn b/chrome/browser/resources/engagement/BUILD.gn
index f0f980c5..9fef636 100644
--- a/chrome/browser/resources/engagement/BUILD.gn
+++ b/chrome/browser/resources/engagement/BUILD.gn
@@ -19,4 +19,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "site-engagement"
 }
diff --git a/chrome/browser/resources/feed_internals/BUILD.gn b/chrome/browser/resources/feed_internals/BUILD.gn
index 275eb87..2aceaf1 100644
--- a/chrome/browser/resources/feed_internals/BUILD.gn
+++ b/chrome/browser/resources/feed_internals/BUILD.gn
@@ -24,4 +24,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "snippets-internals"
 }
diff --git a/chrome/browser/resources/feedback/BUILD.gn b/chrome/browser/resources/feedback/BUILD.gn
index e2febba..22ad5872b 100644
--- a/chrome/browser/resources/feedback/BUILD.gn
+++ b/chrome/browser/resources/feedback/BUILD.gn
@@ -63,4 +63,5 @@
     ts_deps +=
         [ "//ui/webui/resources/cr_components/color_change_listener:build_ts" ]
   }
+  webui_host = "feedback"
 }
diff --git a/chrome/browser/resources/feedback/app.ts b/chrome/browser/resources/feedback/app.ts
index 5ccc690..c8be93e8 100644
--- a/chrome/browser/resources/feedback/app.ts
+++ b/chrome/browser/resources/feedback/app.ts
@@ -158,6 +158,14 @@
       this.getRequiredElement('#log-id-container').hidden = false;
     }
 
+    const isSeaPenFlow: boolean|undefined =
+        isAiFlow && feedbackInfo.aiMetadata?.includes('is_feature_sea_pen');
+
+    if (isSeaPenFlow) {
+      this.getRequiredElement<HTMLInputElement>('#sys-info-checkbox').checked =
+          false;
+    }
+
     const whenScreenshotUpdated = takeScreenshot().then((screenshotCanvas) => {
       // We've taken our screenshot, show the feedback page without any
       // further delay.
diff --git a/chrome/browser/resources/history/app.html b/chrome/browser/resources/history/app.html
index 0a267f10..a99f0c29 100644
--- a/chrome/browser/resources/history/app.html
+++ b/chrome/browser/resources/history/app.html
@@ -100,7 +100,6 @@
           items="{{items}}">
         <div id="tabs-container" path="history">
           <template is="dom-if" if="[[enableHistoryEmbeddings_]]">
-            <cr-history-embeddings></cr-history-embeddings>
             <cr-history-embeddings-filter-chips
                 show-results-by-group="[[
                     getShowResultsByGroup_(selectedPage_)]]"
diff --git a/chrome/browser/resources/history/history_list.html b/chrome/browser/resources/history/history_list.html
index 67df89fc..066e3f6 100644
--- a/chrome/browser/resources/history/history_list.html
+++ b/chrome/browser/resources/history/history_list.html
@@ -12,12 +12,27 @@
       dialog [slot=body] {
         white-space: pre-wrap;
       }
+
+      cr-history-embeddings {
+        margin: 0 auto;
+        margin-block-start: var(--first-card-padding-top);
+        max-width: var(--cluster-max-width);
+      }
     </style>
     <div id="no-results" class="centered-message"
         hidden$="[[hasResults_(historyData_.length)]]">
       [[noResultsMessage_(searchedTerm)]]
     </div>
 
+    <template is="dom-if"
+        if="[[showHistoryEmbeddings_(searchedTerm, historyData_.length)]]">
+      <cr-history-embeddings
+          search-query="[[searchedTerm]]"
+          results="[[getHistoryEmbeddingsMatches_(historyData_)]]"
+          on-result-click="onHistoryEmbeddingsResultClick_">
+      </cr-history-embeddings>
+    </template>
+
     <iron-list class="history-cards"
         items="[[historyData_]]" as="item" id="infinite-list"
         role="grid" aria-rowcount$="[[historyData_.length]]"
diff --git a/chrome/browser/resources/history/history_list.ts b/chrome/browser/resources/history/history_list.ts
index 2977fcbe..c59a0dc 100644
--- a/chrome/browser/resources/history/history_list.ts
+++ b/chrome/browser/resources/history/history_list.ts
@@ -606,6 +606,15 @@
   private onHistoryDataChanged_() {
     this.$['infinite-list'].fire('iron-resize');
   }
+
+  private getHistoryEmbeddingsMatches_(): HistoryEntry[] {
+    return this.historyData_.slice(0, 3);
+  }
+
+  private showHistoryEmbeddings_(): boolean {
+    return loadTimeData.getBoolean('enableHistoryEmbeddings') &&
+        !!this.searchedTerm && this.historyData_?.length > 0;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/identity_internals/BUILD.gn b/chrome/browser/resources/identity_internals/BUILD.gn
index 729d461..1f5ddcd 100644
--- a/chrome/browser/resources/identity_internals/BUILD.gn
+++ b/chrome/browser/resources/identity_internals/BUILD.gn
@@ -17,4 +17,5 @@
   html_to_wrapper_template = "native"
   ts_deps = [ "//ui/webui/resources/js:build_ts" ]
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+  webui_host = "identity-internals"
 }
diff --git a/chrome/browser/resources/internals/user_education/user_education_internals.html b/chrome/browser/resources/internals/user_education/user_education_internals.html
index 5832b5f..0973422 100644
--- a/chrome/browser/resources/internals/user_education/user_education_internals.html
+++ b/chrome/browser/resources/internals/user_education/user_education_internals.html
@@ -6,6 +6,7 @@
 
   --iph-section-border-color: var(--google-green-300);
   --main-column-max-width: 680px;
+  --new-badge-section-border-color: var(--google-yellow-300);
   --side-bar-width: 200px;
   --tutorials-section-border-color: var(--google-blue-300);
 }
@@ -88,6 +89,12 @@
   scroll-margin-top: 16px;
 }
 
+#newBadges {
+  border-inline-start: 1px solid var(--new-badge-section-border-color);
+  padding-inline: var(--cr-section-padding);
+  scroll-margin-top: 16px;
+}
+
 user-education-internals-card {
   padding-block: var(--cr-section-vertical-padding);
   padding-inline: var(--cr-section-padding);
@@ -148,6 +155,9 @@
         <a role="menuitem" href="#iph" class="cr-nav-menu-item">
           Feature Promos
         </a>
+        <a role="menuitem" href="#newBadges" class="cr-nav-menu-item">
+          "New" Badges
+        </a>
     </div>
   </div>
   <div id="main">
@@ -166,6 +176,7 @@
               id="[[item.internalName]]"
               hidden$="[[!promoFilter_(item, filter)]]"
               promo="[[item]]"
+              show-action
               on-promo-launch="startTutorial_">
           </user-education-internals-card>
         </template>
@@ -191,11 +202,24 @@
               id="[[item.internalName]]"
               hidden$="[[!promoFilter_(item, filter)]]"
               promo="[[item]]"
+              show-action
               on-promo-launch="showFeaturePromo_"
               on-clear-promo-data="clearPromoData_">
           </user-education-internals-card>
         </template>
       </div>
+      <div id="newBadges">
+        <a name="newBadges"></a>
+        <h2>"New" Badges</h2>
+        <template id="newBadges" is="dom-repeat" items="[[newBadges_]]">
+          <user-education-internals-card
+              id="[[item.internalName]]"
+              hidden$="[[!promoFilter_(item, filter)]]"
+              promo="[[item]]"
+              on-clear-promo-data="clearNewBadgeData_">
+          </user-education-internals-card>
+        </template>
+      </div>
     </div>
   </div>
   <div id="right" hidden$="[[narrow_]]"></div>
diff --git a/chrome/browser/resources/internals/user_education/user_education_internals.ts b/chrome/browser/resources/internals/user_education/user_education_internals.ts
index 06b7fbd..ee9c644 100644
--- a/chrome/browser/resources/internals/user_education/user_education_internals.ts
+++ b/chrome/browser/resources/internals/user_education/user_education_internals.ts
@@ -87,6 +87,7 @@
   filter: string = '';
   private tutorials_: FeaturePromoDemoPageInfo[];
   private featurePromos_: FeaturePromoDemoPageInfo[];
+  private newBadges_: FeaturePromoDemoPageInfo[];
   private featurePromoErrorMessage_: string;
   private narrow_: boolean = false;
   private sessionData_: FeaturePromoDemoPageData[];
@@ -129,6 +130,10 @@
     this.handler_.getFeaturePromos().then(({featurePromos}) => {
       this.featurePromos_ = featurePromos;
     });
+
+    this.handler_.getNewBadges().then(({newBadges}) => {
+      this.newBadges_ = newBadges;
+    });
   }
 
   private onSearchChanged_(e: CustomEvent) {
@@ -188,6 +193,22 @@
     });
   }
 
+  private clearNewBadgeData_(e: CustomEvent) {
+    const id = e.detail;
+    this.featurePromoErrorMessage_ = '';
+
+    this.handler_.clearNewBadgeData(id).then(({errorMessage}) => {
+      this.featurePromoErrorMessage_ = errorMessage;
+      if (errorMessage !== '') {
+        this.$.errorMessageToast.show();
+      } else {
+        this.handler_.getNewBadges().then(({newBadges}) => {
+          this.newBadges_ = newBadges;
+        });
+      }
+    });
+  }
+
   private promoFilter_(promo: FeaturePromoDemoPageInfo, filter: string) {
     return filter === '' || promo.displayTitle.toLowerCase().includes(filter) ||
         promo.displayDescription.toLowerCase().includes(filter) ||
diff --git a/chrome/browser/resources/internals/user_education/user_education_internals_card.html b/chrome/browser/resources/internals/user_education/user_education_internals_card.html
index 7fac8d3..13157aa 100644
--- a/chrome/browser/resources/internals/user_education/user_education_internals_card.html
+++ b/chrome/browser/resources/internals/user_education/user_education_internals_card.html
@@ -81,6 +81,7 @@
     </cr-button>
   </div>
 </div>
-<cr-button class="action-button" id="launch" on-click="launchPromo_">
+<cr-button class="action-button" hidden="[[!showAction]]" id="launch"
+    on-click="launchPromo_">
   Launch
 </cr-button>
diff --git a/chrome/browser/resources/internals/user_education/user_education_internals_card.ts b/chrome/browser/resources/internals/user_education/user_education_internals_card.ts
index 1471faac..7d09c40 100644
--- a/chrome/browser/resources/internals/user_education/user_education_internals_card.ts
+++ b/chrome/browser/resources/internals/user_education/user_education_internals_card.ts
@@ -29,6 +29,11 @@
     return {
       promo: Object,
 
+      showAction: {
+        type: Boolean,
+        value: false,
+      },
+
       /**
        * Indicates if the list of instructions is expanded or collapsed.
        */
@@ -48,6 +53,7 @@
   }
 
   promo: FeaturePromoDemoPageInfo;
+  showAction: boolean;
   private instructionsExpanded_: boolean;
   private dataExpanded_: boolean;
 
@@ -59,9 +65,9 @@
 
   private clearData_() {
     if (confirm(
-            'Clear Feature Promo data and Feature Enagement events?\n' +
+            'Clear all data associated with this User Education journey?\n' +
             'Note: because of session tracking and event constraints, ' +
-            'Feature Engagement may still disallow this promo.')) {
+            'Feature Engagement may still disallow some IPH.')) {
       this.dispatchEvent(new CustomEvent(
           CLEAR_PROMO_DATA_EVENT,
           {bubbles: true, composed: true, detail: this.promo.internalName}));
diff --git a/chrome/browser/resources/location_internals/BUILD.gn b/chrome/browser/resources/location_internals/BUILD.gn
index 259b135..a3507c2 100644
--- a/chrome/browser/resources/location_internals/BUILD.gn
+++ b/chrome/browser/resources/location_internals/BUILD.gn
@@ -37,4 +37,5 @@
     "$root_gen_dir/services/device/public/mojom/geolocation_internals.mojom-webui.ts",
     "$root_gen_dir/services/device/public/mojom/geoposition.mojom-webui.ts",
   ]
+  webui_host = "location-internals"
 }
diff --git a/chrome/browser/resources/media_router/cast_feedback/BUILD.gn b/chrome/browser/resources/media_router/cast_feedback/BUILD.gn
index ad6b5e8..6334eec9 100644
--- a/chrome/browser/resources/media_router/cast_feedback/BUILD.gn
+++ b/chrome/browser/resources/media_router/cast_feedback/BUILD.gn
@@ -22,4 +22,5 @@
     "//tools/typescript/definitions/feedback_private.d.ts",
     "//tools/typescript/definitions/chrome_send.d.ts",
   ]
+  webui_host = "cast-feedback"
 }
diff --git a/chrome/browser/resources/media_router/internals/BUILD.gn b/chrome/browser/resources/media_router/internals/BUILD.gn
index 80bdb1b1..826724fd 100644
--- a/chrome/browser/resources/media_router/internals/BUILD.gn
+++ b/chrome/browser/resources/media_router/internals/BUILD.gn
@@ -19,4 +19,6 @@
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
   ]
+
+  webui_host = "media-router-internals"
 }
diff --git a/chrome/browser/resources/memory_internals/BUILD.gn b/chrome/browser/resources/memory_internals/BUILD.gn
index dbf6108..4add4df 100644
--- a/chrome/browser/resources/memory_internals/BUILD.gn
+++ b/chrome/browser/resources/memory_internals/BUILD.gn
@@ -11,4 +11,5 @@
 
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   ts_deps = [ "//ui/webui/resources/js:build_ts" ]
+  webui_host = "memory-internals"
 }
diff --git a/chrome/browser/resources/nearby_internals/cross_device_logs_browser_proxy.ts b/chrome/browser/resources/nearby_internals/cross_device_logs_browser_proxy.ts
index 9dc4ec31..299e2c5 100644
--- a/chrome/browser/resources/nearby_internals/cross_device_logs_browser_proxy.ts
+++ b/chrome/browser/resources/nearby_internals/cross_device_logs_browser_proxy.ts
@@ -4,7 +4,7 @@
 
 import {sendWithPromise} from 'chrome://resources/js/cr.js';
 
-import type {LogMessage} from './types';
+import type {LogMessage} from './types.js';
 
 /**
  * JavaScript hooks into the native WebUI handler to pass LogMessages to the
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index c7949e8e..35c7bad 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -102,8 +102,8 @@
     "//ui/webui/resources/cr_components/customize_themes:build_ts",
     "//ui/webui/resources/cr_components/help_bubble:build_ts",
     "//ui/webui/resources/cr_components/most_visited:build_ts",
-    "//ui/webui/resources/cr_components/omnibox:build_ts",
     "//ui/webui/resources/cr_components/page_image_service:build_ts",
+    "//ui/webui/resources/cr_components/searchbox:build_ts",
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
@@ -121,7 +121,7 @@
                                 "chrome://resources/cr_components/customize_themes/customize_themes.mojom-webui.js",
                                 "chrome://resources/cr_components/page_image_service/page_image_service.mojom-webui.js",
                                 "chrome://resources/cr_components/most_visited/most_visited.mojom-webui.js",
-                                "chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js",
+                                "chrome://resources/cr_components/searchbox/omnibox.mojom-webui.js",
                                 "chrome://resources/js/browser_command/browser_command.mojom-webui.js",
                                 "chrome://resources/js/metrics_reporter/metrics_reporter.mojom-webui.js",
                                 "chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js",
diff --git a/chrome/browser/resources/new_tab_page/app.ts b/chrome/browser/resources/new_tab_page/app.ts
index 97a1b73..0b7dcbac 100644
--- a/chrome/browser/resources/new_tab_page/app.ts
+++ b/chrome/browser/resources/new_tab_page/app.ts
@@ -5,7 +5,7 @@
 import './iframe.js';
 import './logo.js';
 import './strings.m.js';
-import 'chrome://resources/cr_components/omnibox/realbox.js';
+import 'chrome://resources/cr_components/searchbox/realbox.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/cr_elements/cr_shared_style.css.js';
 
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
index 51ba1c51..c1e86807 100644
--- a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
@@ -16,7 +16,7 @@
 import {LayoutType} from '../../history_clusters_layout_type.mojom-webui.js';
 import {I18nMixin, loadTimeData} from '../../i18n_setup.js';
 import {NewTabPageProxy} from '../../new_tab_page_proxy.js';
-import type {InfoDialogElement} from '../info_dialog';
+import type {InfoDialogElement} from '../info_dialog.js';
 import {ModuleDescriptor} from '../module_descriptor.js';
 
 import {HistoryClustersProxyImpl} from './history_clusters_proxy.js';
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.ts b/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.ts
index 0046d13..8cf1738 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.ts
@@ -19,7 +19,7 @@
 import {LayoutType} from '../../../history_clusters_layout_type.mojom-webui.js';
 import {I18nMixin, loadTimeData} from '../../../i18n_setup.js';
 import {NewTabPageProxy} from '../../../new_tab_page_proxy.js';
-import type {InfoDialogElement} from '../../info_dialog';
+import type {InfoDialogElement} from '../../info_dialog.js';
 import {ModuleDescriptor} from '../../module_descriptor.js';
 
 import {HistoryClustersProxyImpl} from './history_clusters_proxy.js';
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/tab_resumption/module.ts b/chrome/browser/resources/new_tab_page/modules/v2/tab_resumption/module.ts
index f3bc565b..9a2cdd02 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/tab_resumption/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/v2/tab_resumption/module.ts
@@ -12,9 +12,9 @@
 
 import type {Tab} from '../../../history_types.mojom-webui.js';
 import {I18nMixin, loadTimeData} from '../../../i18n_setup.js';
-import type {InfoDialogElement} from '../../info_dialog';
+import type {InfoDialogElement} from '../../info_dialog.js';
 import {ModuleDescriptor} from '../../module_descriptor.js';
-import type {MenuItem, ModuleHeaderElementV2} from '../module_header';
+import type {MenuItem, ModuleHeaderElementV2} from '../module_header.js';
 
 import {getTemplate} from './module.html.js';
 import {TabResumptionProxyImpl} from './tab_resumption_proxy.js';
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.ts b/chrome/browser/resources/new_tab_page/new_tab_page.ts
index 26c7f8d5..5d60689 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page.ts
+++ b/chrome/browser/resources/new_tab_page/new_tab_page.ts
@@ -9,10 +9,10 @@
  * things tests need.
  */
 
-export {RealboxElement} from 'chrome://resources/cr_components/omnibox/realbox.js';
-export {RealboxBrowserProxy} from 'chrome://resources/cr_components/omnibox/realbox_browser_proxy.js';
-export {RealboxIconElement} from 'chrome://resources/cr_components/omnibox/realbox_icon.js';
-export {RealboxMatchElement} from 'chrome://resources/cr_components/omnibox/realbox_match.js';
+export {RealboxElement} from 'chrome://resources/cr_components/searchbox/realbox.js';
+export {RealboxBrowserProxy} from 'chrome://resources/cr_components/searchbox/realbox_browser_proxy.js';
+export {RealboxIconElement} from 'chrome://resources/cr_components/searchbox/realbox_icon.js';
+export {RealboxMatchElement} from 'chrome://resources/cr_components/searchbox/realbox_match.js';
 export {CrAutoImgElement} from 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js';
 export {BrowserCommandProxy} from 'chrome://resources/js/browser_command/browser_command_proxy.js';
 export {BrowserProxyImpl} from 'chrome://resources/js/metrics_reporter/browser_proxy.js';
diff --git a/chrome/browser/resources/new_tab_page_third_party/BUILD.gn b/chrome/browser/resources/new_tab_page_third_party/BUILD.gn
index 8000eaf..7a73b36 100644
--- a/chrome/browser/resources/new_tab_page_third_party/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page_third_party/BUILD.gn
@@ -21,4 +21,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "new-tab-page-third-party"
 }
diff --git a/chrome/browser/resources/omnibox/ml/ml_calculator.ts b/chrome/browser/resources/omnibox/ml/ml_calculator.ts
index 68fd378..073ab505 100644
--- a/chrome/browser/resources/omnibox/ml/ml_calculator.ts
+++ b/chrome/browser/resources/omnibox/ml/ml_calculator.ts
@@ -8,7 +8,7 @@
 import type {Signals} from '../omnibox.mojom-webui.js';
 import {clamp, createEl, setFormattedClipboardForMl, signalNames} from '../omnibox_util.js';
 
-import type {MlBrowserProxy} from './ml_browser_proxy';
+import type {MlBrowserProxy} from './ml_browser_proxy.js';
 // @ts-ignore:next-line
 import sheet from './ml_calculator.css' assert {type : 'css'};
 import {getTemplate} from './ml_calculator.html.js';
diff --git a/chrome/browser/resources/omnibox/ml/ml_chart.ts b/chrome/browser/resources/omnibox/ml/ml_chart.ts
index 276f8b1..69ba878 100644
--- a/chrome/browser/resources/omnibox/ml/ml_chart.ts
+++ b/chrome/browser/resources/omnibox/ml/ml_chart.ts
@@ -7,7 +7,7 @@
 import type {Signals} from '../omnibox.mojom-webui.js';
 import {clamp, signalNames} from '../omnibox_util.js';
 
-import type {MlBrowserProxy} from './ml_browser_proxy';
+import type {MlBrowserProxy} from './ml_browser_proxy.js';
 // @ts-ignore:next-line
 import sheet from './ml_chart.css' assert {type : 'css'};
 import {getTemplate} from './ml_chart.html.js';
diff --git a/chrome/browser/resources/omnibox_popup/BUILD.gn b/chrome/browser/resources/omnibox_popup/BUILD.gn
index 1784b6c..a1adeeb 100644
--- a/chrome/browser/resources/omnibox_popup/BUILD.gn
+++ b/chrome/browser/resources/omnibox_popup/BUILD.gn
@@ -15,7 +15,7 @@
   ts_deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources/cr_components/color_change_listener:build_ts",
-    "//ui/webui/resources/cr_components/omnibox:build_ts",
+    "//ui/webui/resources/cr_components/searchbox:build_ts",
     "//ui/webui/resources/js:build_ts",
   ]
   webui_host = "omnibox-popup.top-chrome"
@@ -24,11 +24,12 @@
     optimize_webui_in_files = [ "omnibox_popup.js" ]
     optimize_webui_excludes = [
       "chrome://resources/cr_components/color_change_listener/color_change_listener.mojom-webui.js",
-      "chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js",
+      "chrome://resources/cr_components/searchbox/omnibox.mojom-webui.js",
       "chrome://resources/js/metrics_reporter/metrics_reporter.mojom-webui.js",
       "chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js",
       "chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js",
       "chrome://resources/mojo/url/mojom/url.mojom-webui.js",
     ]
   }
+  webui_host = "omnibox-popup.top-chrome"
 }
diff --git a/chrome/browser/resources/omnibox_popup/app.ts b/chrome/browser/resources/omnibox_popup/app.ts
index 8ffd0287..c01df47 100644
--- a/chrome/browser/resources/omnibox_popup/app.ts
+++ b/chrome/browser/resources/omnibox_popup/app.ts
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '//resources/cr_components/omnibox/realbox_dropdown.js';
+import '//resources/cr_components/searchbox/realbox_dropdown.js';
 import './strings.m.js';
 
 import {ColorChangeUpdater} from '//resources/cr_components/color_change_listener/colors_css_updater.js';
-import type {AutocompleteResult, OmniboxPopupSelection, PageCallbackRouter} from '//resources/cr_components/omnibox/omnibox.mojom-webui.js';
-import {RealboxBrowserProxy} from '//resources/cr_components/omnibox/realbox_browser_proxy.js';
-import type {RealboxDropdownElement} from '//resources/cr_components/omnibox/realbox_dropdown.js';
+import type {AutocompleteResult, OmniboxPopupSelection, PageCallbackRouter} from '//resources/cr_components/searchbox/omnibox.mojom-webui.js';
+import {RealboxBrowserProxy} from '//resources/cr_components/searchbox/realbox_browser_proxy.js';
+import type {RealboxDropdownElement} from '//resources/cr_components/searchbox/realbox_dropdown.js';
 import {assert} from '//resources/js/assert.js';
 import {MetricsReporterImpl} from '//resources/js/metrics_reporter/metrics_reporter.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/on_device_internals/BUILD.gn b/chrome/browser/resources/on_device_internals/BUILD.gn
index 316b160..9fc2852 100644
--- a/chrome/browser/resources/on_device_internals/BUILD.gn
+++ b/chrome/browser/resources/on_device_internals/BUILD.gn
@@ -29,4 +29,5 @@
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "on-device-internals"
 }
diff --git a/chrome/browser/resources/predictors/BUILD.gn b/chrome/browser/resources/predictors/BUILD.gn
index 992e0a58..ad97f46 100644
--- a/chrome/browser/resources/predictors/BUILD.gn
+++ b/chrome/browser/resources/predictors/BUILD.gn
@@ -21,4 +21,5 @@
     "//ui/webui/resources/js:build_ts",
   ]
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+  webui_host = "predictors"
 }
diff --git a/chrome/browser/resources/privacy_sandbox/BUILD.gn b/chrome/browser/resources/privacy_sandbox/BUILD.gn
index d03c15d..bf363fc6 100644
--- a/chrome/browser/resources/privacy_sandbox/BUILD.gn
+++ b/chrome/browser/resources/privacy_sandbox/BUILD.gn
@@ -53,4 +53,5 @@
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
   ]
+  webui_host = "privacy-sandbox-dialog"
 }
diff --git a/chrome/browser/resources/privacy_sandbox/internals/BUILD.gn b/chrome/browser/resources/privacy_sandbox/internals/BUILD.gn
index 6140a61..31e4162 100644
--- a/chrome/browser/resources/privacy_sandbox/internals/BUILD.gn
+++ b/chrome/browser/resources/privacy_sandbox/internals/BUILD.gn
@@ -37,4 +37,6 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+
+  webui_host = "privacy-sandbox-internals"
 }
diff --git a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_combined_dialog_app.ts b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_combined_dialog_app.ts
index e8a6a33..c97f10e 100644
--- a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_combined_dialog_app.ts
+++ b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_combined_dialog_app.ts
@@ -17,7 +17,7 @@
 
 import {getTemplate} from './privacy_sandbox_combined_dialog_app.html.js';
 import {PrivacySandboxDialogBrowserProxy, PrivacySandboxPromptAction} from './privacy_sandbox_dialog_browser_proxy.js';
-import type {PrivacySandboxDialogMixinInterface} from './privacy_sandbox_dialog_mixin';
+import type {PrivacySandboxDialogMixinInterface} from './privacy_sandbox_dialog_mixin.js';
 import {PrivacySandboxDialogResizeMixin} from './privacy_sandbox_dialog_resize_mixin.js';
 
 export enum PrivacySandboxCombinedDialogStep {
diff --git a/chrome/browser/resources/profile_internals/BUILD.gn b/chrome/browser/resources/profile_internals/BUILD.gn
index cc9de11..6950824 100644
--- a/chrome/browser/resources/profile_internals/BUILD.gn
+++ b/chrome/browser/resources/profile_internals/BUILD.gn
@@ -19,4 +19,5 @@
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
   ]
+  webui_host = "profile-internals"
 }
diff --git a/chrome/browser/resources/search_engine_choice/BUILD.gn b/chrome/browser/resources/search_engine_choice/BUILD.gn
index d8c066a..7243b9f 100644
--- a/chrome/browser/resources/search_engine_choice/BUILD.gn
+++ b/chrome/browser/resources/search_engine_choice/BUILD.gn
@@ -28,4 +28,5 @@
 
   ts_definitions = [ "//tools/typescript/definitions/metrics_private.d.ts" ]
   ts_composite = true
+  webui_host = "search-engine-choice"
 }
diff --git a/chrome/browser/resources/segmentation_internals/BUILD.gn b/chrome/browser/resources/segmentation_internals/BUILD.gn
index 4400b3eb..1b10232 100644
--- a/chrome/browser/resources/segmentation_internals/BUILD.gn
+++ b/chrome/browser/resources/segmentation_internals/BUILD.gn
@@ -17,4 +17,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "segmentation-internals"
 }
diff --git a/chrome/browser/resources/settings/people_page/manage_profile.html b/chrome/browser/resources/settings/people_page/manage_profile.html
index 2a93e11c..ef7080a 100644
--- a/chrome/browser/resources/settings/people_page/manage_profile.html
+++ b/chrome/browser/resources/settings/people_page/manage_profile.html
@@ -57,11 +57,7 @@
   #profileAvatarSelector {
     --avatar-size: var(--icon-size);
     --avatar-spacing: var(--icon-grid-gap);
-    --avatar-grid-columns: 6;
     padding: 4px;
-    width: calc(var(--avatar-size) * var(--avatar-grid-columns) +
-        var(--avatar-spacing) * (var(--avatar-grid-columns) - 1));
-
   }
 
   #outerRow {
diff --git a/chrome/browser/resources/settings_shared/BUILD.gn b/chrome/browser/resources/settings_shared/BUILD.gn
index e7f6731..460ad18 100644
--- a/chrome/browser/resources/settings_shared/BUILD.gn
+++ b/chrome/browser/resources/settings_shared/BUILD.gn
@@ -48,4 +48,8 @@
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
   ]
+
+  # Using chrome://settings as the host. This folder is only used by trusted
+  # UIs.
+  webui_host = "settings"
 }
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html b/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
index dd4d137..d551237 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
@@ -21,9 +21,6 @@
 
   cr-input {
     --cr-input-background-color: transparent;
-    --cr-input-border-bottom: 1px solid var(--cr-secondary-text-color);
-    --cr-input-border-radius: 0;
-    --cr-input-padding-start: 0;
     --cr-input-error-display: none;
     width: 100%;
   }
diff --git a/chrome/browser/resources/signin/profile_customization/profile_customization_app.html b/chrome/browser/resources/signin/profile_customization/profile_customization_app.html
index 03e1a152..63cb489f 100644
--- a/chrome/browser/resources/signin/profile_customization/profile_customization_app.html
+++ b/chrome/browser/resources/signin/profile_customization/profile_customization_app.html
@@ -180,12 +180,10 @@
   cr-profile-avatar-selector {
     --avatar-size: 72px;
     --avatar-spacing: 18px;
-    --avatar-grid-columns: 5;
     height: fit-content;
     padding-bottom: 12px;
     padding-inline-start: 16px;
     padding-top: 12px;
-    width: fit-content;
   }
 
   .select-avatar-header {
@@ -316,7 +314,7 @@
       </div>
       <div id="selectAvatarWrapper" class="custom-scrollbar">
         <cr-profile-avatar-selector avatars="[[availableIcons_]]"
-            selected-avatar="{{selectedAvatar_}}">
+            selected-avatar="{{selectedAvatar_}}" columns="5">
         </cr-profile-avatar-selector>
       </div>
       <div class="action-container">
diff --git a/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.ts b/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.ts
index 319991a..e43cbd8 100644
--- a/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.ts
+++ b/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.ts
@@ -137,7 +137,7 @@
    * browser window.
    * TODO(https://crbug.com/1282157): Add createShortcut parameter.
    */
-  continueWithoutAccount(profileColor: number): void;
+  createProfileAndOpenCustomizationDialog(profileColor: number): void;
 
   /**
    * Sets the local profile name.
@@ -248,8 +248,8 @@
     return sendWithPromise('getAvailableIcons');
   }
 
-  continueWithoutAccount(profileColor: number) {
-    chrome.send('continueWithoutAccount', [profileColor]);
+  createProfileAndOpenCustomizationDialog(profileColor: number) {
+    chrome.send('createProfileAndOpenCustomizationDialog', [profileColor]);
   }
 
   setProfileName(profilePath: string, profileName: string) {
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_app.ts b/chrome/browser/resources/signin/profile_picker/profile_picker_app.ts
index 870da71..9475e68 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_app.ts
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_app.ts
@@ -126,8 +126,9 @@
       this.profileCreationInProgress = true;
       // TODO(https://crbug.com/1282157): Add createShortcut parameter.
       this.initializeNewProfileThemeInfo_().then(
-          () => this.manageProfilesBrowserProxy_.continueWithoutAccount(
-              this.newProfileThemeInfo?.color));
+          () => this.manageProfilesBrowserProxy_
+                    .createProfileAndOpenCustomizationDialog(
+                        this.newProfileThemeInfo?.color));
       return;
     }
 
diff --git a/chrome/browser/resources/support_tool/BUILD.gn b/chrome/browser/resources/support_tool/BUILD.gn
index b0bc812..ee709b69 100644
--- a/chrome/browser/resources/support_tool/BUILD.gn
+++ b/chrome/browser/resources/support_tool/BUILD.gn
@@ -42,4 +42,5 @@
 
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   ts_composite = true
+  webui_host = "support-tool"
 }
diff --git a/chrome/browser/resources/sync_file_system_internals/BUILD.gn b/chrome/browser/resources/sync_file_system_internals/BUILD.gn
index 9552f508..e262ec7 100644
--- a/chrome/browser/resources/sync_file_system_internals/BUILD.gn
+++ b/chrome/browser/resources/sync_file_system_internals/BUILD.gn
@@ -28,4 +28,5 @@
     "//ui/webui/resources/js:build_ts",
   ]
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+  webui_host = "syncfs-internals"
 }
diff --git a/chrome/browser/resources/tab_strip/BUILD.gn b/chrome/browser/resources/tab_strip/BUILD.gn
index 62ec734..8b2f3f6 100644
--- a/chrome/browser/resources/tab_strip/BUILD.gn
+++ b/chrome/browser/resources/tab_strip/BUILD.gn
@@ -57,4 +57,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "tab-strip.top-chrome"
 }
diff --git a/chrome/browser/resources/usb_internals/BUILD.gn b/chrome/browser/resources/usb_internals/BUILD.gn
index a4b965d4..fcc30fc8 100644
--- a/chrome/browser/resources/usb_internals/BUILD.gn
+++ b/chrome/browser/resources/usb_internals/BUILD.gn
@@ -38,4 +38,5 @@
     "//ui/webui/resources/mojo:build_ts",
   ]
   html_to_wrapper_template = "native"
+  webui_host = "usb-internals"
 }
diff --git a/chrome/browser/resources/web_app_internals/BUILD.gn b/chrome/browser/resources/web_app_internals/BUILD.gn
index 206009f2..d43cd2c4 100644
--- a/chrome/browser/resources/web_app_internals/BUILD.gn
+++ b/chrome/browser/resources/web_app_internals/BUILD.gn
@@ -30,4 +30,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "web-app-internals"
 }
diff --git a/chrome/browser/resources/webapks/BUILD.gn b/chrome/browser/resources/webapks/BUILD.gn
index fed05e1ea..df038f1d 100644
--- a/chrome/browser/resources/webapks/BUILD.gn
+++ b/chrome/browser/resources/webapks/BUILD.gn
@@ -16,4 +16,5 @@
 
   ts_deps = [ "//ui/webui/resources/js:build_ts" ]
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+  webui_host = "webapks"
 }
diff --git a/chrome/browser/resources/welcome/BUILD.gn b/chrome/browser/resources/welcome/BUILD.gn
index 4e93ca6..5a892f42 100644
--- a/chrome/browser/resources/welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/BUILD.gn
@@ -108,4 +108,6 @@
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
   ]
+
+  webui_host = "welcome"
 }
diff --git a/chrome/browser/resources/whats_new/BUILD.gn b/chrome/browser/resources/whats_new/BUILD.gn
index 208f99e3..0138d54 100644
--- a/chrome/browser/resources/whats_new/BUILD.gn
+++ b/chrome/browser/resources/whats_new/BUILD.gn
@@ -17,4 +17,5 @@
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
+  webui_host = "whats-new"
 }
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager.cc b/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
index 0c5d546..aa6f941 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
@@ -220,6 +220,8 @@
     return;
 
   // Refresh OAuth access token.
+  // TODO(crbug.com/330195352): Use a less powerful scope, like
+  // GaiaConstants::kGoogleUserInfoEmail.
   signin::ScopeSet scopes;
   scopes.insert(GaiaConstants::kOAuth1LoginScope);
 
@@ -230,7 +232,7 @@
               &AdvancedProtectionStatusManager::OnAccessTokenFetchComplete,
               base::Unretained(this), unconsented_primary_account_id),
           signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate,
-          signin::ConsentLevel::kSync);
+          signin::ConsentLevel::kSignin);
 }
 
 void AdvancedProtectionStatusManager::ScheduleNextRefresh() {
diff --git a/chrome/browser/search/background/ntp_background_service.cc b/chrome/browser/search/background/ntp_background_service.cc
index 2ff989f..63a0eab 100644
--- a/chrome/browser/search/background/ntp_background_service.cc
+++ b/chrome/browser/search/background/ntp_background_service.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/search/background/ntp_background_service.h"
 
+#include <string_view>
+
 #include "base/barrier_closure.h"
 #include "base/command_line.h"
 #include "base/functional/bind.h"
@@ -62,7 +64,7 @@
 constexpr char kFilteringLabel[] = "chrome_desktop_ntp";
 
 // Returns the configured collections base URL with |path| appended.
-GURL GetUrl(base::StringPiece path) {
+GURL GetUrl(std::string_view path) {
   return GURL(base::CommandLine::ForCurrentProcess()->HasSwitch(
                   kCollectionsBaseUrlCmdlineSwitch)
                   ? base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
diff --git a/chrome/browser/search/most_visited_iframe_source.cc b/chrome/browser/search/most_visited_iframe_source.cc
index a29ebf7cb..e65bff3 100644
--- a/chrome/browser/search/most_visited_iframe_source.cc
+++ b/chrome/browser/search/most_visited_iframe_source.cc
@@ -4,8 +4,9 @@
 
 #include "chrome/browser/search/most_visited_iframe_source.h"
 
+#include <string_view>
+
 #include "base/memory/ref_counted_memory.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "chrome/browser/search/instant_service.h"
@@ -56,7 +57,7 @@
 }
 
 std::string MostVisitedIframeSource::GetMimeType(const GURL& url) {
-  base::StringPiece path = url.path_piece();
+  std::string_view path = url.path_piece();
   if (base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII))
     return "application/javascript";
   if (base::EndsWith(path, ".css", base::CompareCase::INSENSITIVE_ASCII))
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 485bdf68b..88512a1 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -53,7 +53,6 @@
 #include "chrome/browser/sessions/session_service_log.h"
 #include "chrome/browser/sessions/session_service_lookup.h"
 #include "chrome/browser/sessions/session_service_utils.h"
-#include "chrome/browser/sessions/tab_loader.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -860,10 +859,9 @@
   }
 
   // |tab_index| is ignored for pinned tabs which will always be pushed behind
-  // the last existing pinned tab.
-  // |tab_loader_| will schedule this tab for loading if |is_selected_tab| is
-  // false. |last_active_time| is the value to use to set the last time the
-  // WebContents was made active.
+  // the last existing pinned tab. If |is_selected_tab| is true the tab will be
+  // shown after loading, otherwise it's loaded hidden. |last_active_time| is
+  // the value to use to set the last time the WebContents was made active.
   void RestoreTab(const sessions::SessionTab& tab,
                   Browser* browser,
                   std::vector<RestoredTab>* created_contents,
@@ -1137,9 +1135,6 @@
   // Set of URLs to open in addition to those restored from the session.
   StartupTabs startup_tabs_;
 
-  // Responsible for loading the tabs.
-  scoped_refptr<TabLoader> tab_loader_;
-
   // When synchronous we run a nested run loop. To avoid creating windows
   // from the nested run loop (which can make exiting the nested message
   // loop take a while) we cache the SessionWindows here and create the actual
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
index 55f96c21..3751d3d 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <string_view>
 
 #include "base/base64url.h"
 #include "base/containers/span.h"
@@ -68,7 +69,7 @@
 }
 
 std::string GetChallengeFromJwt(std::string_view jwt) {
-  std::vector<base::StringPiece> parts = base::SplitStringPiece(
+  std::vector<std::string_view> parts = base::SplitStringPiece(
       jwt, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
 
   if (parts.size() != 3) {
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.cc b/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.cc
index 1646329..432b594 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.cc
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.h"
 
+#include <string_view>
+
 #include "base/base64.h"
 #include "base/containers/span.h"
 #include "base/json/json_reader.h"
@@ -267,7 +269,7 @@
     std::unique_ptr<std::string> response_body) {
   // JSON responses normally should start with XSSI-protection prefix which
   // should be removed prior to parsing.
-  base::StringPiece response_json = *response_body;
+  std::string_view response_json = *response_body;
   if (base::StartsWith(*response_body, kXSSIPrefix,
                        base::CompareCase::SENSITIVE)) {
     response_json = response_json.substr(strlen(kXSSIPrefix));
diff --git a/chrome/browser/signin/bound_session_credentials/registration_token_helper.cc b/chrome/browser/signin/bound_session_credentials/registration_token_helper.cc
index 0df51c0..9eb03d7 100644
--- a/chrome/browser/signin/bound_session_credentials/registration_token_helper.cc
+++ b/chrome/browser/signin/bound_session_credentials/registration_token_helper.cc
@@ -5,12 +5,12 @@
 #include "chrome/browser/signin/bound_session_credentials/registration_token_helper.h"
 
 #include <optional>
+#include <string_view>
 
 #include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/memory/ptr_util.h"
-#include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "components/signin/public/base/session_binding_utils.h"
 #include "components/unexportable_keys/background_task_priority.h"
@@ -47,7 +47,7 @@
 std::unique_ptr<RegistrationTokenHelper>
 RegistrationTokenHelper::CreateForSessionBinding(
     unexportable_keys::UnexportableKeyService& unexportable_key_service,
-    base::StringPiece challenge,
+    std::string_view challenge,
     const GURL& registration_url,
     base::OnceCallback<void(std::optional<Result>)> callback) {
   HeaderAndPayloadGenerator header_and_payload_generator = base::BindRepeating(
@@ -62,8 +62,8 @@
 std::unique_ptr<RegistrationTokenHelper>
 RegistrationTokenHelper::CreateForTokenBinding(
     unexportable_keys::UnexportableKeyService& unexportable_key_service,
-    base::StringPiece client_id,
-    base::StringPiece auth_code,
+    std::string_view client_id,
+    std::string_view auth_code,
     const GURL& registration_url,
     base::OnceCallback<void(std::optional<Result>)> callback) {
   HeaderAndPayloadGenerator header_and_payload_generator = base::BindRepeating(
diff --git a/chrome/browser/signin/bound_session_credentials/registration_token_helper.h b/chrome/browser/signin/bound_session_credentials/registration_token_helper.h
index c22e863..f91cfc0 100644
--- a/chrome/browser/signin/bound_session_credentials/registration_token_helper.h
+++ b/chrome/browser/signin/bound_session_credentials/registration_token_helper.h
@@ -7,12 +7,12 @@
 
 #include <optional>
 #include <string>
+#include <string_view>
 
 #include "base/containers/span.h"
 #include "base/functional/callback.h"
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
-#include "base/strings/string_piece.h"
 #include "components/unexportable_keys/service_error.h"
 #include "components/unexportable_keys/unexportable_key_id.h"
 #include "crypto/signature_verifier.h"
@@ -59,13 +59,13 @@
   // TODO(alexilin): support timeout.
   static std::unique_ptr<RegistrationTokenHelper> CreateForSessionBinding(
       unexportable_keys::UnexportableKeyService& unexportable_key_service,
-      base::StringPiece challenge,
+      std::string_view challenge,
       const GURL& registration_url,
       base::OnceCallback<void(std::optional<Result>)> callback);
   static std::unique_ptr<RegistrationTokenHelper> CreateForTokenBinding(
       unexportable_keys::UnexportableKeyService& unexportable_key_service,
-      base::StringPiece client_id,
-      base::StringPiece auth_code,
+      std::string_view client_id,
+      std::string_view auth_code,
       const GURL& registration_url,
       base::OnceCallback<void(std::optional<Result>)> callback);
 
diff --git a/chrome/browser/signin/bound_session_credentials/session_binding_helper.cc b/chrome/browser/signin/bound_session_credentials/session_binding_helper.cc
index ad6accf..f607aea 100644
--- a/chrome/browser/signin/bound_session_credentials/session_binding_helper.cc
+++ b/chrome/browser/signin/bound_session_credentials/session_binding_helper.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/signin/bound_session_credentials/session_binding_helper.h"
 
 #include <string>
+#include <string_view>
 
 #include "base/containers/span.h"
 #include "base/functional/bind.h"
@@ -58,7 +59,7 @@
 }
 
 void SessionBindingHelper::GenerateBindingKeyAssertion(
-    base::StringPiece challenge,
+    std::string_view challenge,
     const GURL& destination_url,
     base::OnceCallback<void(std::string)> callback) {
   MaybeLoadBindingKey();
@@ -70,7 +71,7 @@
 }
 
 void SessionBindingHelper::SignAssertionToken(
-    base::StringPiece challenge,
+    std::string_view challenge,
     const GURL& destination_url,
     base::OnceCallback<void(std::string)> callback,
     unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>
diff --git a/chrome/browser/signin/bound_session_credentials/session_binding_helper.h b/chrome/browser/signin/bound_session_credentials/session_binding_helper.h
index e39a4f8..31be2e1 100644
--- a/chrome/browser/signin/bound_session_credentials/session_binding_helper.h
+++ b/chrome/browser/signin/bound_session_credentials/session_binding_helper.h
@@ -6,11 +6,12 @@
 #define CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_SESSION_BINDING_HELPER_H_
 
 #include <cstdint>
+#include <string_view>
+
 #include "base/containers/span.h"
 #include "base/functional/callback_forward.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ref.h"
-#include "base/strings/string_piece.h"
 #include "components/unexportable_keys/service_error.h"
 #include "components/unexportable_keys/unexportable_key_id.h"
 
@@ -43,7 +44,7 @@
   // `wrapped_binding_key` passed in the constructor. The result is returned
   // through `callback`. Returns an empty string if the generation fails.
   void GenerateBindingKeyAssertion(
-      base::StringPiece challenge,
+      std::string_view challenge,
       const GURL& destination_url,
       base::OnceCallback<void(std::string)> callback);
 
@@ -52,7 +53,7 @@
   FRIEND_TEST_ALL_PREFIXES(SessionBindingHelperTest, MaybeLoadBindingKey);
 
   void SignAssertionToken(
-      base::StringPiece challenge,
+      std::string_view challenge,
       const GURL& destination_url,
       base::OnceCallback<void(std::string)> callback,
       unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>
diff --git a/chrome/browser/signin/dice_response_handler.cc b/chrome/browser/signin/dice_response_handler.cc
index e0abaff..43c3739 100644
--- a/chrome/browser/signin/dice_response_handler.cc
+++ b/chrome/browser/signin/dice_response_handler.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/signin/dice_response_handler.h"
 
+#include <string_view>
+
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
@@ -88,8 +90,8 @@
 #if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
 std::unique_ptr<RegistrationTokenHelper> BuildRegistrationTokenHelper(
     unexportable_keys::UnexportableKeyService& unexportable_key_service,
-    base::StringPiece client_id,
-    base::StringPiece auth_code,
+    std::string_view client_id,
+    std::string_view auth_code,
     const GURL& registration_url,
     base::OnceCallback<void(std::optional<RegistrationTokenHelper::Result>)>
         callback) {
diff --git a/chrome/browser/signin/dice_response_handler.h b/chrome/browser/signin/dice_response_handler.h
index e95fa93..95dff78 100644
--- a/chrome/browser/signin/dice_response_handler.h
+++ b/chrome/browser/signin/dice_response_handler.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/cancelable_callback.h"
@@ -84,8 +85,8 @@
 #if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
   using RegistrationTokenHelperFactory =
       base::RepeatingCallback<std::unique_ptr<RegistrationTokenHelper>(
-          base::StringPiece client_id,
-          base::StringPiece auth_code,
+          std::string_view client_id,
+          std::string_view auth_code,
           const GURL& registration_url,
           base::OnceCallback<void(
               std::optional<RegistrationTokenHelper::Result>)> callback)>;
diff --git a/chrome/browser/signin/dice_response_handler_unittest.cc b/chrome/browser/signin/dice_response_handler_unittest.cc
index 04edd48..107e2a8 100644
--- a/chrome/browser/signin/dice_response_handler_unittest.cc
+++ b/chrome/browser/signin/dice_response_handler_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/signin/dice_response_handler.h"
 
 #include <memory>
+#include <string_view>
 #include <utility>
 
 #include "base/check.h"
@@ -233,7 +234,7 @@
       bool invalid_primary_account);
 
 #if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
-  void EnableRegistrationTokenHelper(base::StringPiece authorization_code) {
+  void EnableRegistrationTokenHelper(std::string_view authorization_code) {
     EXPECT_CALL(mock_registration_token_helper_factory_,
                 Run(_, authorization_code, _, _))
         .WillOnce(Invoke([this](Unused, Unused, Unused, auto callback) {
diff --git a/chrome/browser/signin/e2e_tests/test_accounts_util_unittest.cc b/chrome/browser/signin/e2e_tests/test_accounts_util_unittest.cc
index 5aca2c5..2648a38 100644
--- a/chrome/browser/signin/e2e_tests/test_accounts_util_unittest.cc
+++ b/chrome/browser/signin/e2e_tests/test_accounts_util_unittest.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/signin/e2e_tests/test_accounts_util.h"
+
+#include <string_view>
+
 #include "base/files/file_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -17,7 +20,7 @@
                                      unsigned int length) {
   FilePath tmp_file;
   CHECK(base::CreateTemporaryFile(&tmp_file));
-  bool success = base::WriteFile(tmp_file, base::StringPiece(contents, length));
+  bool success = base::WriteFile(tmp_file, std::string_view(contents, length));
   CHECK(success);
   return tmp_file;
 }
diff --git a/chrome/browser/signin/signin_ui_delegate_impl_lacros_unittest.cc b/chrome/browser/signin/signin_ui_delegate_impl_lacros_unittest.cc
index 7fd86a2..6a6f60b6 100644
--- a/chrome/browser/signin/signin_ui_delegate_impl_lacros_unittest.cc
+++ b/chrome/browser/signin/signin_ui_delegate_impl_lacros_unittest.cc
@@ -4,13 +4,14 @@
 
 #include "chrome/browser/signin/signin_ui_delegate_impl_lacros.h"
 
+#include <string_view>
+
 #include "base/containers/fixed_flat_map.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
 #include "base/scoped_observation.h"
 #include "base/strings/strcat.h"
-#include "base/strings/string_piece.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
@@ -39,14 +40,14 @@
 
 constexpr auto kPromoSuffixes = base::MakeFixedFlatMap<
     signin_metrics::PromoAction,
-    base::StringPiece>(
+    std::string_view>(
     {{signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT, ".WithDefault"},
      {signin_metrics::PromoAction::PROMO_ACTION_NOT_DEFAULT, ".NotDefault"},
      {signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
       ".NewAccountNoExistingAccount"},
      {signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_EXISTING_ACCOUNT,
       ".NewAccountExistingAccount"}});
-constexpr base::StringPiece kSigninStartedHistogramBaseName =
+constexpr std::string_view kSigninStartedHistogramBaseName =
     "Signin.SigninStartedAccessPoint";
 
 constexpr signin_metrics::AccessPoint kAccessPoint =
diff --git a/chrome/browser/speech/speech_recognition_recognizer_client_impl.cc b/chrome/browser/speech/speech_recognition_recognizer_client_impl.cc
index 6a988d5..b4071bb 100644
--- a/chrome/browser/speech/speech_recognition_recognizer_client_impl.cc
+++ b/chrome/browser/speech/speech_recognition_recognizer_client_impl.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/speech/speech_recognition_recognizer_client_impl.h"
 
 #include <algorithm>
+#include <string_view>
 #include <utility>
 
 #include "ash/constants/ash_features.h"
@@ -131,7 +132,7 @@
   }
 
   static constexpr auto kSupportedLanguagesAndLocales =
-      base::MakeFixedFlatSet<base::StringPiece>({
+      base::MakeFixedFlatSet<std::string_view>({
           "de",              // German
           "de-AT",           // German (Austria)
           "de-CH",           // German (Switzerland)
diff --git a/chrome/browser/speech/speech_recognition_service_browsertest.cc b/chrome/browser/speech/speech_recognition_service_browsertest.cc
index 3dbdd0fb..72567cdb 100644
--- a/chrome/browser/speech/speech_recognition_service_browsertest.cc
+++ b/chrome/browser/speech/speech_recognition_service_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <algorithm>
+#include <string_view>
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -532,7 +533,7 @@
   ASSERT_TRUE(base::PathExists(config_dir));
   base::FilePath config_file_path =
       config_dir.Append(FILE_PATH_LITERAL("config_file"));
-  ASSERT_TRUE(base::WriteFile(config_file_path, base::StringPiece()));
+  ASSERT_TRUE(base::WriteFile(config_file_path, std::string_view()));
   ASSERT_TRUE(base::PathExists(config_file_path));
   g_browser_process->local_state()->SetFilePath(prefs::kSodaEnUsConfigPath,
                                                 config_file_path);
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc
index fe3cc08..f2c30624 100644
--- a/chrome/browser/storage_access_api/api_browsertest.cc
+++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <string_view>
 
 #include "base/containers/adapters.h"
 #include "base/feature_list.h"
@@ -10,7 +11,6 @@
 #include "base/path_service.h"
 #include "base/strings/escape.h"
 #include "base/strings/strcat.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -165,7 +165,7 @@
              net::features::kThirdPartyStoragePartitioning);
 }
 
-std::string CookieAttributes(base::StringPiece domain) {
+std::string CookieAttributes(std::string_view domain) {
   return base::StrCat({";SameSite=None;Secure;Domain=", domain, ";Path=/"});
 }
 
@@ -434,7 +434,7 @@
     return ChildFrameAt(GetPrimaryMainFrame(), 1);
   }
 
-  void EnsureUserInteractionOn(base::StringPiece host,
+  void EnsureUserInteractionOn(std::string_view host,
                                Browser* browser_ptr = nullptr) {
     if (browser_ptr == nullptr) {
       browser_ptr = browser();
diff --git a/chrome/browser/storage_access_api/site_pair_cache_unittest.cc b/chrome/browser/storage_access_api/site_pair_cache_unittest.cc
index fbd5a25..f4a5835 100644
--- a/chrome/browser/storage_access_api/site_pair_cache_unittest.cc
+++ b/chrome/browser/storage_access_api/site_pair_cache_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/storage_access_api/site_pair_cache.h"
 
+#include <string_view>
+
 #include "base/strings/strcat.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,7 +23,7 @@
 constexpr char kIPv6Addr[] = "::1";
 constexpr char kLocalhost[] = "localhost";
 
-url::Origin OriginFromHost(base::StringPiece host) {
+url::Origin OriginFromHost(std::string_view host) {
   return url::Origin::Create(GURL(base::StrCat({"https://", host})));
 }
 
diff --git a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
index a997ba2..0191d36 100644
--- a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
+++ b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
@@ -4,8 +4,9 @@
 
 #include "chrome/browser/sync/prefs/chrome_syncable_prefs_database.h"
 
+#include <string_view>
+
 #include "base/containers/fixed_flat_map.h"
-#include "base/strings/string_piece.h"
 #include "chrome/browser/promos/promos_pref_names.h"
 #include "chrome/browser/ui/toolbar/toolbar_pref_names.h"
 #include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.h"
@@ -340,7 +341,7 @@
 
 // Non-iOS specific list of syncable preferences.
 constexpr auto kChromeSyncablePrefsAllowlist = base::MakeFixedFlatMap<
-    base::StringPiece,
+    std::string_view,
     sync_preferences::SyncablePrefMetadata>({
 #if BUILDFLAG(IS_ANDROID)
     {language::prefs::kAppLanguagePromptShown,
@@ -1438,9 +1439,9 @@
   return common_syncable_prefs_database_.GetSyncablePrefMetadata(pref_name);
 }
 
-std::map<base::StringPiece, sync_preferences::SyncablePrefMetadata>
+std::map<std::string_view, sync_preferences::SyncablePrefMetadata>
 ChromeSyncablePrefsDatabase::GetAllSyncablePrefsForTest() const {
-  std::map<base::StringPiece, sync_preferences::SyncablePrefMetadata>
+  std::map<std::string_view, sync_preferences::SyncablePrefMetadata>
       syncable_prefs;
   base::ranges::copy(kChromeSyncablePrefsAllowlist,
                      std::inserter(syncable_prefs, syncable_prefs.end()));
diff --git a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.h b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.h
index 9070ffc..3fb787d 100644
--- a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.h
+++ b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.h
@@ -6,8 +6,8 @@
 #define CHROME_BROWSER_SYNC_PREFS_CHROME_SYNCABLE_PREFS_DATABASE_H_
 
 #include <map>
+#include <string_view>
 
-#include "base/strings/string_piece.h"
 #include "components/sync_preferences/common_syncable_prefs_database.h"
 #include "components/sync_preferences/syncable_prefs_database.h"
 
@@ -21,7 +21,7 @@
   std::optional<sync_preferences::SyncablePrefMetadata> GetSyncablePrefMetadata(
       const std::string& pref_name) const override;
 
-  std::map<base::StringPiece, sync_preferences::SyncablePrefMetadata>
+  std::map<std::string_view, sync_preferences::SyncablePrefMetadata>
   GetAllSyncablePrefsForTest() const;
 
  private:
diff --git a/chrome/browser/sync/prefs/chrome_syncable_prefs_database_unittest.cc b/chrome/browser/sync/prefs/chrome_syncable_prefs_database_unittest.cc
index 335acb6..66e13b1 100644
--- a/chrome/browser/sync/prefs/chrome_syncable_prefs_database_unittest.cc
+++ b/chrome/browser/sync/prefs/chrome_syncable_prefs_database_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/sync/prefs/chrome_syncable_prefs_database.h"
 
+#include <string_view>
+
 #include "base/test/metrics/histogram_enum_reader.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -18,7 +20,7 @@
          "tools/metrics/histograms/metadata/sync/enums.xml.";
 
   browser_sync::ChromeSyncablePrefsDatabase db;
-  std::map<base::StringPiece, sync_preferences::SyncablePrefMetadata>
+  std::map<std::string_view, sync_preferences::SyncablePrefMetadata>
       syncable_prefs = db.GetAllSyncablePrefsForTest();
   for (const auto& [pref_name, metadata] : syncable_prefs) {
     EXPECT_TRUE(syncable_pref_enums->contains(metadata.syncable_pref_id()))
diff --git a/chrome/browser/sync/test/integration/two_client_printers_sync_test.cc b/chrome/browser/sync/test/integration/two_client_printers_sync_test.cc
index 3fb28800..a212672 100644
--- a/chrome/browser/sync/test/integration/two_client_printers_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_printers_sync_test.cc
@@ -4,6 +4,8 @@
 
 #include <stdio.h>
 
+#include <string_view>
+
 #include "base/run_loop.h"
 #include "build/build_config.h"
 #include "chrome/browser/ash/printing/printers_sync_bridge.h"
@@ -258,7 +260,7 @@
   spec_printer = bridge->GetPrinter(spec_printer_id);
   ASSERT_TRUE(spec_printer);
 
-  base::StringPiece make_and_model = spec_printer->make_and_model();
+  std::string_view make_and_model = spec_printer->make_and_model();
   EXPECT_THAT(make_and_model, Not(IsEmpty()));
   EXPECT_THAT(make_and_model, StartsWith(kMake));
   EXPECT_THAT(make_and_model, EndsWith(kModel));
diff --git a/chrome/browser/tab_group/BUILD.gn b/chrome/browser/tab_group/BUILD.gn
index 22788aeb..31ea98a 100644
--- a/chrome/browser/tab_group/BUILD.gn
+++ b/chrome/browser/tab_group/BUILD.gn
@@ -43,6 +43,7 @@
     "//chrome/browser/tab:java",
     "//chrome/browser/tabmodel:java",
     "//chrome/test/android:chrome_java_unit_test_support",
+    "//components/tab_groups:tab_groups_java",
     "//third_party/android_deps:espresso_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_collection_collection_java",
diff --git a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtils.java b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtils.java
index e64bb7c..4cfd7862 100644
--- a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtils.java
+++ b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupColorUtils.java
@@ -25,18 +25,21 @@
     private static final int MIGRATION_DONE = 1;
 
     /**
-     * This method stores tab group colors with reference to {@code tabRootId}.
+     * This method stores tab group colors with reference to {@code tabRootId}. Package protected as
+     * all access should route through the {@link TabGroupModelFilter}.
      *
      * @param tabRootId The tab root ID which is used as a reference to store group colors.
      * @param color The tab group color {@link TabGroupColorId} to store.
      */
-    public static void storeTabGroupColor(int tabRootId, int color) {
+    static void storeTabGroupColor(int tabRootId, int color) {
         assert tabRootId != Tab.INVALID_TAB_ID;
         getSharedPreferences().edit().putInt(String.valueOf(tabRootId), color).apply();
     }
 
     /**
      * This method deletes a specific stored tab group color with reference to {@code tabRootId}.
+     * While currently public, the intent is to make this package protected and force all access to
+     * go through the {@Link TabGroupModelFilter}.
      *
      * @param tabRootId The tab root ID whose related tab group color will be deleted.
      */
@@ -46,7 +49,9 @@
     }
 
     /**
-     * This method fetches tab group colors for the related tab group root ID.
+     * This method fetches tab group colors for the related tab group root ID. While currently
+     * public, the intent is to make this package protected and force all access to go through the
+     * {@Link TabGroupModelFilter}.
      *
      * @param tabRootId The tab root ID whose related tab group color will be fetched.
      * @return The stored color of the target tab group, default value is -1 (INVALID_COLOR_ID).
diff --git a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java
index d656f06..cf669642 100644
--- a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java
+++ b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelFilter;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.components.tab_groups.TabGroupColorId;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -1226,6 +1227,33 @@
         return group != null;
     }
 
+    /** Returns the current title of the tab group. */
+    public String getTabGroupTitle(int rootId) {
+        return TabGroupTitleUtils.getTabGroupTitle(rootId);
+    }
+
+    /** Stores the given title for the tab group. */
+    public void setTabGroupTitle(int rootId, String title) {
+        TabGroupTitleUtils.storeTabGroupTitle(rootId, title);
+        for (TabGroupModelFilterObserver observer : mGroupFilterObserver) {
+            observer.didChangeTabGroupTitle(rootId, title);
+        }
+    }
+
+    /** Returns the current color of the tab group. */
+    public @TabGroupColorId int getTabGroupColor(int rootId) {
+        // TODO(crbug.com/329127327): Refactor and emit an event when this changes the color.
+        return TabGroupColorUtils.getOrCreateTabGroupColor(rootId, this);
+    }
+
+    /** Stores the given color for the tab group. */
+    public void setTabGroupColor(int rootId, @TabGroupColorId int color) {
+        TabGroupColorUtils.storeTabGroupColor(rootId, color);
+        for (TabGroupModelFilterObserver observer : mGroupFilterObserver) {
+            observer.didChangeTabGroupColor(rootId, color);
+        }
+    }
+
     private static Token getOrCreateTabGroupId(@NonNull Tab tab) {
         return getOrCreateTabGroupIdWithDefault(tab, null);
     }
diff --git a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterObserver.java b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterObserver.java
index 17fd062..616e1b17 100644
--- a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterObserver.java
+++ b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterObserver.java
@@ -6,6 +6,7 @@
 
 import org.chromium.base.Token;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.components.tab_groups.TabGroupColorId;
 
 import java.util.List;
 
@@ -99,4 +100,20 @@
      * @param filter The {@link TabGroupModelFilter} that the new group event triggers on.
      */
     default void didCreateNewGroup(Tab destinationTab, TabGroupModelFilter filter) {}
+
+    /**
+     * This method is called after a new title is set on a tab group.
+     *
+     * @param rootId The current rootId of the tab group.
+     * @param newTitle The new title.
+     */
+    default void didChangeTabGroupTitle(int rootId, String newTitle) {}
+
+    /**
+     * This method is called after a new color is set on a tab group.
+     *
+     * @param rootId The current rootId of the tab group.
+     * @param newColor The new color.
+     */
+    default void didChangeTabGroupColor(int rootId, @TabGroupColorId int newColor) {}
 }
diff --git a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupTitleUtils.java b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupTitleUtils.java
index e01863e..6c56fc8 100644
--- a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupTitleUtils.java
+++ b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupTitleUtils.java
@@ -17,18 +17,23 @@
     private static final String TAB_GROUP_TITLES_FILE_NAME = "tab_group_titles";
 
     /**
-     * This method stores tab group title with reference to {@code tabRootId}.
-     * @param tabRootId   The tab root ID which is used as reference to store group title.
-     * @param title       The tab group title to store.
+     * This method stores tab group title with reference to {@code tabRootId}. Package protected as
+     * all access should route through the {@link TabGroupModelFilter}.
+     *
+     * @param tabRootId The tab root ID which is used as reference to store group title.
+     * @param title The tab group title to store.
      */
-    public static void storeTabGroupTitle(int tabRootId, String title) {
+    static void storeTabGroupTitle(int tabRootId, String title) {
         assert tabRootId != Tab.INVALID_TAB_ID;
         getSharedPreferences().edit().putString(String.valueOf(tabRootId), title).apply();
     }
 
     /**
      * This method deletes specific stored tab group title with reference to {@code tabRootId}.
-     * @param tabRootId  The tab root ID whose related tab group title will be deleted.
+     * While currently public, the intent is to make this package protected and force all access to
+     * go through the {@Link TabGroupModelFilter}.
+     *
+     * @param tabRootId The tab root ID whose related tab group title will be deleted.
      */
     // Package Private.
     public static void deleteTabGroupTitle(int tabRootId) {
@@ -37,8 +42,11 @@
     }
 
     /**
-     * This method fetches tab group title with related tab group root ID.
-     * @param tabRootId  The tab root ID whose related tab group title will be fetched.
+     * This method fetches tab group title with related tab group root ID. While currently public,
+     * the intent is to make this package protected and force all access to go through the {@Link
+     * TabGroupModelFilter}.
+     *
+     * @param tabRootId The tab root ID whose related tab group title will be fetched.
      * @return The stored title of the target tab group, default value is null.
      */
     public static @Nullable String getTabGroupTitle(int tabRootId) {
diff --git a/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java b/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java
index 795d9801..6dc7fca 100644
--- a/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java
+++ b/chrome/browser/tab_group/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java
@@ -58,6 +58,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.components.tab_groups.TabGroupColorId;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -117,20 +118,15 @@
     private static final int COLOR_ID = 0;
 
     @Rule public TestRule mProcessor = new Features.JUnitProcessor();
-
     @Rule public JniMocker mJniMocker = new JniMocker();
 
     @Mock Token.Natives mTokenJniMock;
-
     @Mock TabModel mTabModel;
-
     @Mock TabGroupModelFilterObserver mTabGroupModelFilterObserver;
-
     @Mock Context mContext;
-
     @Mock SharedPreferences mSharedPreferencesTitle;
-
     @Mock SharedPreferences mSharedPreferencesColor;
+    @Mock SharedPreferences.Editor mEditor;
 
     @Captor ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
 
@@ -340,6 +336,10 @@
         ContextUtils.initApplicationContextForTests(mContext);
         when(mSharedPreferencesTitle.getString(anyString(), any())).thenReturn(TAB_TITLE);
         when(mSharedPreferencesColor.getInt(anyString(), anyInt())).thenReturn(INVALID_COLOR_ID);
+        when(mSharedPreferencesTitle.edit()).thenReturn(mEditor);
+        when(mSharedPreferencesColor.edit()).thenReturn(mEditor);
+        when(mEditor.putString(anyString(), anyString())).thenReturn(mEditor);
+        when(mEditor.putInt(anyString(), anyInt())).thenReturn(mEditor);
 
         mModelAndObserverInOrder = inOrder(mTabModel, mTabGroupModelFilterObserver);
     }
@@ -1847,4 +1847,17 @@
 
         assertEquals(rootIds, mTabGroupModelFilter.getAllTabGroupRootIds());
     }
+
+    @Test
+    public void testSetTabGroupTitle() {
+        mTabGroupModelFilter.setTabGroupTitle(TAB2_ROOT_ID, "Foo");
+        verify(mTabGroupModelFilterObserver).didChangeTabGroupTitle(TAB2_ROOT_ID, "Foo");
+    }
+
+    @Test
+    public void testSetTabGroupColor() {
+        mTabGroupModelFilter.setTabGroupColor(TAB2_ROOT_ID, TabGroupColorId.GREY);
+        verify(mTabGroupModelFilterObserver)
+                .didChangeTabGroupColor(TAB2_ROOT_ID, TabGroupColorId.GREY);
+    }
 }
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/ForeignSessionTabResumptionDataProvider.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/ForeignSessionTabResumptionDataProvider.java
index 73719a3..fd19fad 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/ForeignSessionTabResumptionDataProvider.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/ForeignSessionTabResumptionDataProvider.java
@@ -11,18 +11,18 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
 
 /**
  * TabResumptionDataProvider that uses ForeignSessionTabResumptionDataSource data, while supporting
  * the following update requirements:
  *
  * <pre>
- * 1. Get initial suggestions quickly so Magic stack can decide whether to show or hide the module.
- * 2. Get up-to-date suggestions that needs a more time to fetch, and may be unavailable if the data
- *    (1) is already up to date.
- * 3. Stabilize suggestion data beyond a certain time threshold.
- * 4. Handle suggestion (and module) removal if the permission changes.
+ * 1. Fast path: Read cached suggestions so Magic stack can show module quickly.
+ * 2. Slow path: Read up-to-date suggestions that needs time to fetch. This may not fire if (1) fast
+ *    path data is already the most recent.
+ * 3. Stability: Slow path data may (a) arrive late (from post-start updates) or (b) arrive in
+ *    quick successions (from frequent updates). Reject this so results are stable.
+ * 4. Permission change: Handle suggestion (and module) removal if the permission changes.
  * </pre>
  *
  * The callback passed by fetchSuggestions() is single-use. To refresh data, the caller will need to
@@ -30,16 +30,9 @@
  */
 public class ForeignSessionTabResumptionDataProvider extends TabResumptionDataProvider
         implements DataChangedObserver {
-    // Duration after initial suggestion for which non-permission data changes will be ignored,
-    // thus allowing enforcing data stability.
-    private static final long STABLE_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(10);
-
     private final ForeignSessionTabResumptionDataSource mDataSource;
     private final Runnable mCleanupCallback;
 
-    // State to enforce suggestions stability.
-    private long mLastWriteTimeMs;
-
     /**
      * @param dataSource Non-owned data source instance that may be shared.
      * @param cleanupCallback To be invoked in destroy() for potential cleanup of external data.
@@ -73,19 +66,13 @@
 
         // Results may be empty.
         suggestionsCallback.onResult(suggestions);
-        mLastWriteTimeMs = mDataSource.getCurrentTimeMs();
     }
 
     /** Implements {@link ForeignSessionTabResumptionDataSource.DataChangedObserver} */
     @Override
     public void onForeignSessionDataChanged(boolean isPermissionUpdate) {
-        if (isPermissionUpdate || !shouldEnforceStability()) {
+        if (isPermissionUpdate || !mIsStable) {
             dispatchStatusChangedCallback();
         }
     }
-
-    private boolean shouldEnforceStability() {
-        long currentTimeMs = mDataSource.getCurrentTimeMs();
-        return mLastWriteTimeMs != 0 && currentTimeMs - mLastWriteTimeMs > STABLE_THRESHOLD_MS;
-    }
 }
diff --git a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionDataProvider.java b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionDataProvider.java
index cc43b38..3e54cae 100644
--- a/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionDataProvider.java
+++ b/chrome/browser/tab_resumption/java/src/org/chromium/chrome/browser/tab_resumption/TabResumptionDataProvider.java
@@ -15,12 +15,15 @@
 
     @Nullable protected Runnable mStatusChangedCallback;
 
+    // A flag to prevent the provider from triggering non-permission updates.
+    protected boolean mIsStable;
+
     TabResumptionDataProvider() {}
 
     public abstract void destroy();
 
     /**
-     * Main entry point to trigger suggestion fetch, and asynchronously passes the result to
+     * Main entry point to trigger suggestion fetch, with results asynchronously passed to
      * `suggestionsCallback`. Suggestions can be null or empty if unavailable. If avialble, the
      * suggestions are filtered and sorted, with the most relevant one appearing first.
      *
@@ -37,6 +40,11 @@
         mStatusChangedCallback = statusChangedCallback;
     }
 
+    /** Sets or clears `mIsStable` flag. */
+    public void setIsStable(boolean isStable) {
+        mIsStable = isStable;
+    }
+
     /** Called by derived classes to signal significant status change requiring UI update. */
     protected void dispatchStatusChangedCallback() {
         if (mStatusChangedCallback != null) {
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 f95a53b..debbbdc 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
@@ -8,6 +8,8 @@
 import android.content.res.Resources;
 import android.text.TextUtils;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType;
 import org.chromium.chrome.browser.tab_resumption.TabResumptionModuleUtils.SuggestionClickCallback;
@@ -18,15 +20,13 @@
 /** The Mediator for the tab resumption module. */
 public class TabResumptionModuleMediator {
 
-    @interface ModuleState {
-        // Module is at initial state, awaiting results.
+    @interface ModuleStage {
+        // Module is just initialized, awaiting suggestions.
         int INIT = 0;
-        // Module not shown due to empty suggestions, but can still show.
-        int TENTATIVE_GONE = 1;
-        // Module shows suggestion, but may still hide.
-        int SHOWN = 2;
-        // Module is hidden and stays that way.
-        int GONE = 3;
+        // Module got tentatively suggestions, awaiting final suggestions.
+        int TENTATIVE = 1;
+        // Module suggestions are stable, but can still get hidden if shown.
+        int STABLE = 2;
     }
 
     private static final int MAX_TILES_NUMBER = 2;
@@ -38,7 +38,8 @@
     protected final UrlImageProvider mUrlImageProvider;
     protected final SuggestionClickCallback mSuggestionClickCallback;
 
-    private @ModuleState int mModuleState;
+    private @ModuleStage int mModuleStage;
+    private boolean mDoShow;
 
     public TabResumptionModuleMediator(
             Context context,
@@ -53,7 +54,7 @@
         mDataProvider = dataProvider;
         mUrlImageProvider = urlImageProvider;
         mSuggestionClickCallback = suggestionClickCallback;
-        mModuleState = ModuleState.INIT;
+        mModuleStage = ModuleStage.INIT;
 
         mModel.set(TabResumptionModuleProperties.URL_IMAGE_PROVIDER, mUrlImageProvider);
         mModel.set(TabResumptionModuleProperties.CLICK_CALLBACK, mSuggestionClickCallback);
@@ -68,7 +69,7 @@
 
     /**
      * Fetches new suggestions, creates SuggestionBundle, then updates `mModel`. If no data is
-     * available then hides the module.
+     * available then hides the module. See onSuggestionReceived() for details.
      */
     void loadModule() {
         // In each module instance, loadModule() can get called a few times:
@@ -81,7 +82,7 @@
         //   trigger module refresh, brings control flow back here (after noticeable delay). The
         //   resulting suggestions would be fresher than Initial call's.
         //   * Use: Provides up-to-date suggestions.
-        //   * Possible results: {(never called), nothing, something}.
+        //   * Possible results: {nothing, something, (never called)}.
         //
         // Meanwhile, Magic Stack expects the following ModuleDelegate callbacks:
         // * onDataReady(): To show module, and unblock Magic Stack wait.
@@ -91,62 +92,24 @@
         // onDataReady() and onDataFetchFailed() are mutually exclusive; removeModule() can only
         // be called once, and only if onDataReady() is called.
         //
-        // A state machine manages how different Initial / Update call cases trigger the above.
+        // Expected cases (Initial call --> update call):
+        // * Init nothing --> nothing: onDataFetchFailed() on update call.
+        // * Init nothing --> something: onDataReady() on update call.
+        // * Init nothing --> (never called): Magic stack times out and hides module.
+        // * Init something --> nothing: onDataReady() on init call; removeModule() on update call.
+        // * Init something --> something: onDataReady() on init call, rerender on update call.
+        // * Init something --> (never called): onDataReady() on init call.
+        // Special cases:
+        // * Init any --> (after long delay) something: Should treat as Init any --> (never called).
+        // * Init any --> something --> nothing: Corresponds to the case where data permission
+        //   changes and the module should disappear: removeModule() on second update call.
+        // * Init any --> something --> something: Update when module is stable: The second
+        //   update call should be ignored.
 
-        // ModuleState.GONE is a terminal state: Don't bother fetching.
-        if (mModuleState == ModuleState.GONE) return;
+        // If module is stable and hidden, stay that way and don't bother fetching.
+        if (mModuleStage == ModuleStage.STABLE && !mDoShow) return;
 
-        mDataProvider.fetchSuggestions(
-                (List<SuggestionEntry> suggestions) -> {
-                    int nextModuleState =
-                            (suggestions != null && suggestions.size() > 0)
-                                    ? ModuleState.SHOWN
-                                    : ModuleState.GONE;
-
-                    // State machine transition, which can result in `mModuleDelegate` calls
-                    if (mModuleState == ModuleState.INIT) {
-                        // Initial call.
-                        if (nextModuleState == ModuleState.SHOWN) {
-                            mModuleDelegate.onDataReady(getModuleType(), mModel);
-                        } else { // No data: Don't give up yet; still have retry opportunity.
-                            nextModuleState = ModuleState.TENTATIVE_GONE;
-                        }
-                    } else if (mModuleState == ModuleState.TENTATIVE_GONE) {
-                        // Update call after Initial call has suggested nothing.
-                        if (nextModuleState == ModuleState.SHOWN) {
-                            mModuleDelegate.onDataReady(getModuleType(), mModel);
-                        } else { // Now sure there's no data, so hide.
-                            mModuleDelegate.onDataFetchFailed(getModuleType());
-                        }
-                    } else if (mModuleState == ModuleState.SHOWN) {
-                        // Update call after Initial call has suggested something.
-                        if (nextModuleState == ModuleState.GONE) {
-                            // Transition from SHOWN to GONE.
-                            mModuleDelegate.removeModule(getModuleType());
-                        }
-                    }
-
-                    if (nextModuleState == ModuleState.SHOWN) {
-                        // TODO(crbug.com/1515325): Record metrics here.
-                        Resources res = mContext.getResources();
-                        SuggestionBundle bundle = makeSuggestionBundle(suggestions);
-                        String title =
-                                res.getQuantityString(
-                                        R.plurals.home_modules_tab_resumption_title,
-                                        bundle.entries.size());
-                        // Trigger render.
-                        mModel.set(TabResumptionModuleProperties.SUGGESTION_BUNDLE, bundle);
-                        mModel.set(TabResumptionModuleProperties.TITLE, title);
-                        mModel.set(TabResumptionModuleProperties.IS_VISIBLE, true);
-                    } else {
-                        // Trigger render.
-                        mModel.set(TabResumptionModuleProperties.SUGGESTION_BUNDLE, null);
-                        mModel.set(TabResumptionModuleProperties.TITLE, null);
-                        mModel.set(TabResumptionModuleProperties.IS_VISIBLE, false);
-                    }
-
-                    mModuleState = nextModuleState;
-                });
+        mDataProvider.fetchSuggestions(this::onSuggestionReceived);
     }
 
     /**
@@ -187,4 +150,67 @@
                 .getQuantityString(
                         R.plurals.home_modules_context_menu_hide_tab, bundle.entries.size());
     }
+
+    /** Computes and sets UI properties and triggers render. */
+    private void setPropertiesAndTriggerRender(List<SuggestionEntry> suggestions) {
+        @Nullable SuggestionBundle bundle = null;
+        @Nullable String title = null;
+        boolean isVisible = false;
+        if (suggestions != null && suggestions.size() > 0) {
+            Resources res = mContext.getResources();
+            bundle = makeSuggestionBundle(suggestions);
+            title =
+                    res.getQuantityString(
+                            R.plurals.home_modules_tab_resumption_title, bundle.entries.size());
+            isVisible = true;
+        }
+        mModel.set(TabResumptionModuleProperties.SUGGESTION_BUNDLE, bundle);
+        mModel.set(TabResumptionModuleProperties.TITLE, title);
+        mModel.set(TabResumptionModuleProperties.IS_VISIBLE, isVisible);
+    }
+
+    /**
+     * Handles `suggestions` data passed from mDataProvider.fetchSuggestions() by advancing
+     * `mModuleStage`, updating `mDoShow`, and making appropriate `mModuleDelegate` calls.
+     */
+    private void onSuggestionReceived(List<SuggestionEntry> suggestions) {
+        boolean nextDoShow = (suggestions != null && suggestions.size() > 0);
+
+        if (mModuleStage == ModuleStage.INIT) {
+            if (nextDoShow) {
+                mModuleDelegate.onDataReady(getModuleType(), mModel);
+            }
+            setPropertiesAndTriggerRender(suggestions);
+            mModuleStage = ModuleStage.TENTATIVE;
+
+        } else if (mModuleStage == ModuleStage.TENTATIVE) {
+            if (nextDoShow) {
+                if (!mDoShow) {
+                    mModuleDelegate.onDataReady(getModuleType(), mModel);
+                } // Else skip; onDataReady() was already called.
+            } else {
+                if (mDoShow) {
+                    mModuleDelegate.removeModule(getModuleType());
+                } else {
+                    mModuleDelegate.onDataFetchFailed(getModuleType());
+                }
+            }
+            setPropertiesAndTriggerRender(suggestions);
+            mDataProvider.setIsStable(true);
+            mModuleStage = ModuleStage.STABLE;
+
+        } else if (mModuleStage == ModuleStage.STABLE) {
+            // `mDataProvider` is made stable in the TENTATIVE stage, so we'd expect the flow to
+            // only reaches here if `suggestions` is empty. However, to account for possible race
+            // condition it's better to be defensive and check again.
+            if (!nextDoShow) {
+                if (mDoShow) {
+                    mModuleDelegate.removeModule(getModuleType());
+                }
+                setPropertiesAndTriggerRender(null);
+            } // Else enforce stability, and don't trigger render.
+        }
+
+        mDoShow = nextDoShow;
+    }
 }
diff --git a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/ForeignSessionTabResumptionDataProviderTest.java b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/ForeignSessionTabResumptionDataProviderTest.java
index 6359e6a3..00779499 100644
--- a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/ForeignSessionTabResumptionDataProviderTest.java
+++ b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/ForeignSessionTabResumptionDataProviderTest.java
@@ -26,7 +26,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
 
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
@@ -95,18 +94,16 @@
 
         // 3s elapsed, same as before; if ForeignSessionTabResumptionDataSource receives new
         // data at this time, it can still cause module refresh.
-        when(mSource.getCurrentTimeMs()).thenReturn(CURRENT_TIME_MS + TimeUnit.SECONDS.toMillis(3));
         mDataProvider.onForeignSessionDataChanged(/* isPermissionUpdate= */ false);
         Assert.assertEquals(3, mStatusChangedCallbackCounter);
         mDataProvider.onForeignSessionDataChanged(/* isPermissionUpdate= */ true);
         Assert.assertEquals(4, mStatusChangedCallbackCounter);
 
-        // 1min elapsed, well beyond lock threshold.
-        when(mSource.getCurrentTimeMs()).thenReturn(CURRENT_TIME_MS + TimeUnit.MINUTES.toMillis(1));
-        // Data is now locked: Non-permission update no longer cause module refresh.
+        // Make provider stable, so it only causes refresh for permission update (login or sync
+        // state changes).
+        mDataProvider.setIsStable(true);
         mDataProvider.onForeignSessionDataChanged(/* isPermissionUpdate= */ false);
         Assert.assertEquals(4, mStatusChangedCallbackCounter);
-        // Permission update (login or sync state change) can cause module refresh.
         mDataProvider.onForeignSessionDataChanged(/* isPermissionUpdate= */ true);
         Assert.assertEquals(5, mStatusChangedCallbackCounter);
     }
diff --git a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMediatorUnitTest.java b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMediatorUnitTest.java
index a8f224b3..9f3f314 100644
--- a/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMediatorUnitTest.java
+++ b/chrome/browser/tab_resumption/junit/src/org/chromium/chrome/browser/tab_resumption/TabResumptionModuleMediatorUnitTest.java
@@ -163,8 +163,7 @@
         Assert.assertEquals(
                 "Continue with this tab", mModel.get(TabResumptionModuleProperties.TITLE));
 
-        SuggestionBundle bundle =
-                (SuggestionBundle) mModel.get(TabResumptionModuleProperties.SUGGESTION_BUNDLE);
+        SuggestionBundle bundle = getSuggestionBundle();
         Assert.assertEquals(CURRENT_TIME_MS, bundle.referenceTimeMs);
         Assert.assertEquals(1, bundle.entries.size());
         Assert.assertEquals(entryValid, bundle.entries.get(0));
@@ -201,8 +200,7 @@
         Assert.assertEquals(
                 "Continue with these tabs", mModel.get(TabResumptionModuleProperties.TITLE));
 
-        SuggestionBundle bundle =
-                (SuggestionBundle) mModel.get(TabResumptionModuleProperties.SUGGESTION_BUNDLE);
+        SuggestionBundle bundle = getSuggestionBundle();
         Assert.assertEquals(CURRENT_TIME_MS, bundle.referenceTimeMs);
         Assert.assertEquals(2, bundle.entries.size());
         Assert.assertEquals(entryNewest, bundle.entries.get(0));
@@ -215,7 +213,7 @@
         List<SuggestionEntry> initialSuggestions = new ArrayList<SuggestionEntry>();
         List<SuggestionEntry> updateSuggestions1 = new ArrayList<SuggestionEntry>();
 
-        // Initial call --> nothing: Don't show, wait some more.
+        // Initial call = nothing: Don't show, wait some more.
         mMediator.loadModule();
         verify(mDataProvider, times(1)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(0).onResult(initialSuggestions);
@@ -223,7 +221,7 @@
 
         // If no Update call: Wait until Magic Stack timeout.
 
-        // Update call --> nothing: Call onDataFetchFailed(), gone indefinitely.
+        // Update call = nothing: Call onDataFetchFailed(), gone indefinitely.
         mMediator.loadModule();
         verify(mDataProvider, times(2)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(1).onResult(updateSuggestions1);
@@ -234,9 +232,9 @@
     @SmallTest
     public void testInitialNothingUpdateSomething() {
         List<SuggestionEntry> initialSuggestions = new ArrayList<SuggestionEntry>();
-        List<SuggestionEntry> updateSuggestions1 = Arrays.asList(makeValidEntry());
+        List<SuggestionEntry> updateSuggestions1 = Arrays.asList(makeValidEntry(0));
 
-        // Initial call --> nothing: Don't show, wait some more.
+        // Initial call = nothing: Don't show, wait some more.
         mMediator.loadModule();
         verify(mDataProvider, times(1)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(0).onResult(initialSuggestions);
@@ -244,28 +242,30 @@
 
         // If no Update call: Wait until Magic Stack timeout.
 
-        // Update call --> something: Call onDataReady() and show.
+        // Update call = something: Call onDataReady() and show.
         mMediator.loadModule();
         verify(mDataProvider, times(2)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(1).onResult(updateSuggestions1);
         checkModuleState(true, 1, 0, 0);
+        Assert.assertEquals("Google Dog", getSuggestionBundle().entries.get(0).title);
     }
 
     @Test
     @SmallTest
     public void testInitialSomethingUpdateNothing() {
-        List<SuggestionEntry> initialSuggestions = Arrays.asList(makeValidEntry());
+        List<SuggestionEntry> initialSuggestions = Arrays.asList(makeValidEntry(1));
         List<SuggestionEntry> updateSuggestions1 = new ArrayList<SuggestionEntry>();
 
-        // Initial call --> something: Call onDataReady() and show (tentative).
+        // Initial call = something: Call onDataReady() and show (tentative).
         mMediator.loadModule();
         verify(mDataProvider, times(1)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(0).onResult(initialSuggestions);
         checkModuleState(true, 1, 0, 0);
+        Assert.assertEquals("Google Cat", getSuggestionBundle().entries.get(0).title);
 
         // If no Update call: Data is new enough, show indefinitely.
 
-        // Update call --> nothing: Call removeModule(), gone indefinitely.
+        // Update call = nothing: Call removeModule(), gone indefinitely.
         mMediator.loadModule();
         verify(mDataProvider, times(2)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(1).onResult(updateSuggestions1);
@@ -279,32 +279,39 @@
     @Test
     @SmallTest
     public void testInitialSomethingUpdateSomething() {
-        List<SuggestionEntry> initialSuggestions = Arrays.asList(makeValidEntry());
-        List<SuggestionEntry> updateSuggestions1 = Arrays.asList(makeValidEntry());
-        List<SuggestionEntry> updateSuggestions2 = Arrays.asList(makeValidEntry());
+        List<SuggestionEntry> initialSuggestions = Arrays.asList(makeValidEntry(0));
+        List<SuggestionEntry> updateSuggestions1 =
+                Arrays.asList(makeValidEntry(1), makeValidEntry(0));
+        List<SuggestionEntry> updateSuggestions2 = Arrays.asList(makeValidEntry(0));
         List<SuggestionEntry> updateSuggestions3 = new ArrayList<SuggestionEntry>();
 
-        // Initial call --> something: Call onDataReady() and show (tentative).
+        // Initial call = something: Call onDataReady() and show (tentative).
         mMediator.loadModule();
         verify(mDataProvider, times(1)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(0).onResult(initialSuggestions);
         checkModuleState(true, 1, 0, 0);
+        Assert.assertEquals("Google Dog", getSuggestionBundle().entries.get(0).title);
 
         // If no Update call: Data is new enough, show indefinitely.
 
-        // Update call --> something: Show.
+        // Update call = something: Show. Results not stable.
         mMediator.loadModule();
         verify(mDataProvider, times(2)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(1).onResult(updateSuggestions1);
         checkModuleState(true, 1, 0, 0);
+        Assert.assertEquals("Google Cat", getSuggestionBundle().entries.get(0).title);
+        Assert.assertEquals("Google Dog", getSuggestionBundle().entries.get(1).title);
 
-        // Rare case of more Update call --> something: Show.
+        // Rare case of more Update call = something: Show, but results don't change since it
+        // should be stable.
         mMediator.loadModule();
         verify(mDataProvider, times(3)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(2).onResult(updateSuggestions2);
         checkModuleState(true, 1, 0, 0);
+        Assert.assertEquals("Google Cat", getSuggestionBundle().entries.get(0).title);
+        Assert.assertEquals("Google Dog", getSuggestionBundle().entries.get(1).title);
 
-        // Rare case of more Update call --> nothing: Call removeModule(), gone indefinitely.
+        // Rare case of more Update call = nothing: Call removeModule(), gone indefinitely.
         mMediator.loadModule();
         verify(mDataProvider, times(4)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
         mFetchSuggestionCallbackCaptor.getAllValues().get(3).onResult(updateSuggestions3);
@@ -315,11 +322,14 @@
         verify(mDataProvider, times(4)).fetchSuggestions(mFetchSuggestionCallbackCaptor.capture());
     }
 
-    private SuggestionEntry makeValidEntry() {
+    private SuggestionEntry makeValidEntry(int index) {
+        assert index == 0 || index == 1;
+        GURL[] urlChoices = {JUnitTestGURLs.GOOGLE_URL_DOG, JUnitTestGURLs.GOOGLE_URL_CAT};
+        String[] titleChoices = {"Google Dog", "Google Cat"};
         return new SuggestionEntry(
                 /* sourceName= */ "Desktop",
-                /* url= */ JUnitTestGURLs.GOOGLE_URL_DOG,
-                /* title= */ "Google Dog",
+                /* url= */ urlChoices[index],
+                /* title= */ titleChoices[index],
                 /* timestamp= */ makeTimestamp(16, 0, 0),
                 /* id= */ 45);
     }
@@ -334,4 +344,8 @@
         verify(mModuleDelegate, times(expectOnDataFetchFailedCalls)).onDataFetchFailed(anyInt());
         verify(mModuleDelegate, times(expectRemoveModuleCalls)).removeModule(anyInt());
     }
+
+    private SuggestionBundle getSuggestionBundle() {
+        return (SuggestionBundle) mModel.get(TabResumptionModuleProperties.SUGGESTION_BUNDLE);
+    }
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index dfdd00d..d925e27 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -827,8 +827,6 @@
       "android/autofill/card_unmask_prompt_view_android.h",
       "android/autofill/credit_card_scanner_view_android.cc",
       "android/autofill/credit_card_scanner_view_android.h",
-      "android/autofill/facilitated_payment_bottom_sheet_bridge.cc",
-      "android/autofill/facilitated_payment_bottom_sheet_bridge.h",
       "android/autofill/otp_verification_dialog_view_android.cc",
       "android/autofill/otp_verification_dialog_view_android.h",
       "android/autofill/save_update_address_profile_flow_manager.cc",
@@ -1624,8 +1622,6 @@
       "unload_controller.h",
       "user_education/polling_idle_observer.cc",
       "user_education/polling_idle_observer.h",
-      "user_education/scoped_new_badge_tracker.cc",
-      "user_education/scoped_new_badge_tracker.h",
       "user_education/show_promo_in_page.cc",
       "user_education/show_promo_in_page.h",
       "user_education/start_tutorial_in_page.cc",
diff --git a/chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge.cc b/chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge.cc
deleted file mode 100644
index 1e30811..0000000
--- a/chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge.cc
+++ /dev/null
@@ -1,36 +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 "chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge.h"
-
-#include "base/android/jni_android.h"
-#include "chrome/android/chrome_jni_headers/FacilitatedPaymentBottomSheetBridge_jni.h"
-#include "content/public/browser/web_contents.h"
-
-namespace autofill {
-
-FacilitatedPaymentBottomSheetBridge::FacilitatedPaymentBottomSheetBridge()
-    : java_bridge_(Java_FacilitatedPaymentBottomSheetBridge_Constructor(
-          base::android::AttachCurrentThread())) {}
-
-FacilitatedPaymentBottomSheetBridge::~FacilitatedPaymentBottomSheetBridge() = default;
-
-bool FacilitatedPaymentBottomSheetBridge::RequestShowContent(content::WebContents* web_contents) {
-  if (!web_contents) {
-    return false;
-  }
-
-  base::android::ScopedJavaLocalRef<jobject> java_web_contents =
-      web_contents->GetJavaWebContents();
-  if (!java_web_contents) {
-    return false;
-  }
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-
-  return Java_FacilitatedPaymentBottomSheetBridge_requestShowContent(env, java_bridge_,
-      java_web_contents);
-}
-
-}  // namespace autofill
diff --git a/chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge.h b/chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge.h
deleted file mode 100644
index c6db1681..0000000
--- a/chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge.h
+++ /dev/null
@@ -1,40 +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 CHROME_BROWSER_UI_ANDROID_AUTOFILL_FACILITATED_PAYMENT_BOTTOM_SHEET_BRIDGE_H_
-#define CHROME_BROWSER_UI_ANDROID_AUTOFILL_FACILITATED_PAYMENT_BOTTOM_SHEET_BRIDGE_H_
-
-#include <jni.h>
-
-#include "base/android/scoped_java_ref.h"
-#include "ui/android/window_android.h"
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-namespace autofill {
-
-// Bridge class providing an entry point to trigger the facilitated payment
-// bottom sheet on Android.
-class FacilitatedPaymentBottomSheetBridge {
- public:
-  FacilitatedPaymentBottomSheetBridge();
-
-  FacilitatedPaymentBottomSheetBridge(
-      const FacilitatedPaymentBottomSheetBridge&) = delete;
-  FacilitatedPaymentBottomSheetBridge& operator=(
-      const FacilitatedPaymentBottomSheetBridge&) = delete;
-
-  ~FacilitatedPaymentBottomSheetBridge();
-
-  bool RequestShowContent(content::WebContents* web_contents);
-
- private:
-  base::android::ScopedJavaGlobalRef<jobject> java_bridge_;
-};
-
-}  // namespace autofill
-
-#endif  // CHROME_BROWSER_UI_ANDROID_AUTOFILL_FACILITATED_PAYMENT_BOTTOM_SHEET_BRIDGE_H_
diff --git a/chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge_unittest.cc b/chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge_unittest.cc
deleted file mode 100644
index 87e4b31..0000000
--- a/chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge_unittest.cc
+++ /dev/null
@@ -1,26 +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 "chrome/browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge.h"
-
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "content/public/browser/browser_context.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace autofill {
-namespace {
-
-using FacilitatedPaymentBottomSheetBridgeTest = ChromeRenderViewHostTestHarness;
-
-TEST_F(FacilitatedPaymentBottomSheetBridgeTest, RequestShowContent) {
-  FacilitatedPaymentBottomSheetBridge bridge;
-
-  bool did_show = bridge.RequestShowContent(web_contents());
-
-  EXPECT_FALSE(did_show);
-}
-
-}  // namespace
-}  // namespace autofill
-
diff --git a/chrome/browser/ui/android/edge_to_edge/OWNERS b/chrome/browser/ui/android/edge_to_edge/OWNERS
index 0b65700..49b8de4 100644
--- a/chrome/browser/ui/android/edge_to_edge/OWNERS
+++ b/chrome/browser/ui/android/edge_to_edge/OWNERS
@@ -1,3 +1,2 @@
-donnd@chromium.org
 twellington@chromium.org
 
diff --git a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
index 45c65bf9..44c32df 100644
--- a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
+++ b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
@@ -264,7 +264,10 @@
                 mEdgeToEdgeOSWrapper.setOnApplyWindowInsetsListener(
                         rootView, mWindowInsetsConsumer);
             }
-        } else {
+        } else if (mSystemInsets != null) {
+            // It's possible for toEdge to change more than once prior to the first time
+            // #handleWindowInsets is called. #handleWindowInsets will call #adjustEdges using
+            // the current mIsActivityToEdge once insets are available.
             adjustEdges(toEdge, viewId, webContents);
         }
     }
diff --git a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerTest.java b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerTest.java
index 2145c92..e75841f 100644
--- a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerTest.java
+++ b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerTest.java
@@ -89,8 +89,12 @@
         return new Object[] {false, true};
     }
 
+    private static final int TOP_INSET = 113;
+    private static final int BOTTOM_INSET = 59;
+
     @SuppressLint("NewApi")
-    private static final Insets SYSTEM_INSETS = Insets.of(0, 113, 0, 59); // Typical.
+    private static final Insets SYSTEM_INSETS =
+            Insets.of(0, TOP_INSET, 0, BOTTOM_INSET); // Typical.
 
     @Rule(order = -2)
     public BaseRobolectricTestRule mBaseRule = new BaseRobolectricTestRule();
@@ -467,6 +471,51 @@
                         Robolectric.buildActivity(AppCompatActivity.class).setup().get()));
     }
 
+    // Regression test for https://crbug.com/329875254.
+    @Test
+    public void testViewportFitAfterListenerSet_ToNormal() {
+        when(mTab.isNativePage()).thenReturn(false);
+        mTabProvider.set(mTab);
+        verifyInteractions(mTab);
+        assertFalse("Shouldn't be toEdge.", mEdgeToEdgeControllerImpl.isToEdge());
+
+        // Simulate a viewport fit change to kick off WindowInsetConsumer being hooked up.
+        mEdgeToEdgeControllerImpl.getWebContentsObserver().viewportFitChanged(ViewportFit.COVER);
+        // Simulate another viewport fit change prior to #handleWindowInsets being called.
+        mEdgeToEdgeControllerImpl.getWebContentsObserver().viewportFitChanged(ViewportFit.CONTAIN);
+
+        // Simulate insets being available.
+        assertNotNull(mWindowInsetsListenerCaptor.getValue());
+        mWindowInsetsListenerCaptor.getValue().onApplyWindowInsets(mViewMock, mWindowInsetsMock);
+        assertFalse(
+                "Shouldn't be toEdge after toggling viewport-fit.",
+                mEdgeToEdgeControllerImpl.isToEdge());
+        verify(mOsWrapper).setPadding(any(), eq(0), eq(TOP_INSET), eq(0), eq(BOTTOM_INSET));
+    }
+
+    @Test
+    public void testViewportFitAfterListenerSet_ToEdge() {
+        when(mTab.isNativePage()).thenReturn(false);
+        mTabProvider.set(mTab);
+        verifyInteractions(mTab);
+        assertFalse("Shouldn't be toEdge.", mEdgeToEdgeControllerImpl.isToEdge());
+
+        // Simulate a viewport fit change to kick off WindowInsetConsumer being hooked up.
+        mEdgeToEdgeControllerImpl.getWebContentsObserver().viewportFitChanged(ViewportFit.COVER);
+        // Simulate another viewport fit change prior to #handleWindowInsets being called.
+        mEdgeToEdgeControllerImpl.getWebContentsObserver().viewportFitChanged(ViewportFit.CONTAIN);
+        // Go back to edge.
+        mEdgeToEdgeControllerImpl.getWebContentsObserver().viewportFitChanged(ViewportFit.COVER);
+
+        // Simulate insets being available.
+        assertNotNull(mWindowInsetsListenerCaptor.getValue());
+        mWindowInsetsListenerCaptor.getValue().onApplyWindowInsets(mViewMock, mWindowInsetsMock);
+        assertTrue(
+                "Should be toEdge after toggling viewport-fit.",
+                mEdgeToEdgeControllerImpl.isToEdge());
+        verify(mOsWrapper).setPadding(any(), eq(0), eq(TOP_INSET), eq(0), eq(0));
+    }
+
     void assertToEdgeExpectations() {
         assertNotNull(mWindowInsetsListenerCaptor.getValue());
         mWindowInsetsListenerCaptor.getValue().onApplyWindowInsets(mViewMock, mWindowInsetsMock);
diff --git a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
index b5d3538a..05b166d5 100644
--- a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
+++ b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
@@ -15,7 +15,6 @@
     <dimen name="toolbar_url_focus_translation_x">10dp</dimen>
     <dimen name="toolbar_url_focus_height_increase_active_color">8dp</dimen>
     <dimen name="toolbar_url_focus_height_increase_no_active_color">10dp</dimen>
-    <dimen name="toolbar_url_focus_bottom_padding">2dp</dimen>
 
     <!-- Bottom Toolbar -->
     <dimen name="split_toolbar_button_height">56dp</dimen>
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java
index 2d4df15..bdf6721 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java
@@ -394,7 +394,15 @@
     private static void requestCellInfoUpdate(
             TelephonyManager telephonyManager, Callback<List<CellInfo>> callback) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-            ApiHelperForQ.requestCellInfoUpdate(telephonyManager, callback);
+            try {
+                ApiHelperForQ.requestCellInfoUpdate(telephonyManager, callback);
+            } catch (IllegalStateException e) {
+                // TelephonyManager#requestCellInfoUpdate() throws IllegalStateException when
+                // Telephony is unavailable. It doesn't make sense to pass the call to the
+                // TelephonyManager#getAllCellInfo(), because given the same exact conditions it
+                // will return null, too.
+                callback.onResult(null);
+            }
             return;
         }
         callback.onResult(telephonyManager.getAllCellInfo());
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java
index 54f7a0bf..410d6d4 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java
@@ -10,7 +10,9 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -32,11 +34,13 @@
 import android.telephony.CellIdentityGsm;
 import android.telephony.CellIdentityLte;
 import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
 import android.telephony.CellInfoCdma;
 import android.telephony.CellInfoGsm;
 import android.telephony.CellInfoLte;
 import android.telephony.CellInfoWcdma;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.CellInfoCallback;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -59,12 +63,13 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 /** Robolectric tests for {@link PlatformNetworksManager}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(sdk = 29, manifest = Config.NONE)
+@Config(sdk = Build.VERSION_CODES.Q, manifest = Config.NONE)
 @LooperMode(LooperMode.Mode.LEGACY)
 public class PlatformNetworksManagerTest {
     private static final VisibleWifi CONNECTED_WIFI =
@@ -292,6 +297,33 @@
     }
 
     @Test
+    public void testGetAllVisibleCells_telephonyIsShuttingDown() {
+        doThrow(new IllegalStateException())
+                .when(mTelephonyManager)
+                .requestCellInfoUpdate(any(), any());
+        PlatformNetworksManager.getAllVisibleCells(
+                mContext, mTelephonyManager, mVisibleCellCallback);
+        verify(mVisibleCellCallback).onResult(mVisibleCellsArgument.capture());
+        assertEquals(0, mVisibleCellsArgument.getValue().size());
+    }
+
+    @Test
+    public void testGetAllVisibleCells_successfulOperation() {
+        ArgumentCaptor<CellInfoCallback> captor = ArgumentCaptor.forClass(CellInfoCallback.class);
+        PlatformNetworksManager.getAllVisibleCells(
+                mContext, mTelephonyManager, mVisibleCellCallback);
+        verify(mTelephonyManager).requestCellInfoUpdate(any(), captor.capture());
+        verify(mVisibleCellCallback, never()).onResult(any());
+
+        // Emit update.
+        List<CellInfo> list = List.of(mCellInfoLte, mCellInfoGsm);
+        captor.getValue().onCellInfo(list);
+        verify(mVisibleCellCallback).onResult(mVisibleCellsArgument.capture());
+        assertEquals(2, mVisibleCellsArgument.getValue().size());
+        assertEquals(Set.of(GSM_CELL, LTE_CELL), Set.copyOf(mVisibleCellsArgument.getValue()));
+    }
+
+    @Test
     public void testGetConnectedWifi_BeforeS() {
         VisibleWifi visibleWifi = PlatformNetworksManager.getConnectedWifi(mContext);
         assertEquals(CONNECTED_WIFI, visibleWifi);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplier.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplier.java
index f978b169..b431fbc 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplier.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplier.java
@@ -38,6 +38,7 @@
     private @NonNull RoundedIconGenerator mIconGenerator;
     private @Nullable LargeIconBridge mIconBridge;
     private @Nullable ImageFetcher mImageFetcher;
+    private boolean mNativeInitialized;
 
     /**
      * Constructor.
@@ -79,6 +80,11 @@
         mPendingImageRequests.clear();
     }
 
+    /** Notify OmniboxImageSupplier that certain native-requiring calls are now ready for use. */
+    public void onNativeInitialized() {
+        mNativeInitialized = true;
+    }
+
     /**
      * Notify that the current User profile has changed.
      *
@@ -136,6 +142,11 @@
      * @param callback The callback that will be invoked with the result.
      */
     public void generateFavicon(@NonNull GURL url, @NonNull Callback<Bitmap> callback) {
+        if (!mNativeInitialized) {
+            callback.onResult(null);
+            return;
+        }
+
         PostTask.postTask(
                 TaskTraits.UI_DEFAULT,
                 () -> {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplierUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplierUnitTest.java
index 7f6a072..afd5cbcc 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplierUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplierUnitTest.java
@@ -140,12 +140,24 @@
     }
 
     @Test
-    public void generateFavicon() {
+    public void generateFavicon_beforeNativeInitialized() {
         doReturn(mBitmap1).when(mIconGenerator).generateIconForUrl(NAV_URL);
 
         mSupplier.generateFavicon(NAV_URL, mCallback1);
         ShadowLooper.runUiThreadTasks();
 
+        verifyReturnedIcon(null);
+        verifyNoOtherInteractionsAndClearInteractions();
+    }
+
+    @Test
+    public void generateFavicon_afterNativeInitialized() {
+        doReturn(mBitmap1).when(mIconGenerator).generateIconForUrl(NAV_URL);
+
+        mSupplier.onNativeInitialized();
+        mSupplier.generateFavicon(NAV_URL, mCallback1);
+        ShadowLooper.runUiThreadTasks();
+
         verify(mIconGenerator, times(1)).generateIconForUrl(NAV_URL);
         verifyReturnedIcon(mBitmap1);
         verifyNoOtherInteractionsAndClearInteractions();
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
index 9f682ce..dbf4e07 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
@@ -194,6 +194,9 @@
     /** Signals that native initialization has completed. */
     void onNativeInitialized() {
         mHeaderProcessor.onNativeInitialized();
+        if (mImageSupplier != null) {
+            mImageSupplier.onNativeInitialized();
+        }
         mUseNativeGrouping =
                 ChromeFeatureList.isEnabled(
                         ChromeFeatureList.OMNIBOX_SUGGESTION_GROUPING_FOR_NON_ZPS);
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index fdf6096..f50b859 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -2208,6 +2208,9 @@
       <message name="IDS_SYNC_SETTINGS" desc="Title for preference which enables sync'ing of settings. [CHAR_LIMIT=32]">
         Settings
       </message>
+      <message name="IDS_SYNC_APPS" desc="Title for preference which enables sync'ing of WebAPKs. [CHAR_LIMIT=32]">
+        Apps
+      </message>
       <message name="IDS_SYNC_PAYMENTS_INTEGRATION" desc="Title for preference which enables import of Google Pay data for Autofill. 'Google Pay' should not be translated as it is the product name.">
         Payment methods, offers, and addresses using Google Pay
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SYNC_APPS.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SYNC_APPS.png.sha1
new file mode 100644
index 0000000..259632f
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SYNC_APPS.png.sha1
@@ -0,0 +1 @@
+012dd7dddce145cd3eadbb6b981ac7729c05d53c
\ No newline at end of file
diff --git a/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.cc b/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.cc
index 56632fb6..4c2f38d 100644
--- a/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.cc
+++ b/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.cc
@@ -483,9 +483,14 @@
     const std::string& app_id) {
   TRACE_EVENT0("ui", "ChromeSavedDeskDelegate::GetAppShortName");
   std::string name;
-  auto* app_service_proxy = apps::AppServiceProxyFactory::GetForProfile(
-      ProfileManager::GetActiveUserProfile());
-  DCHECK(app_service_proxy);
+  auto* profile = ProfileManager::GetActiveUserProfile();
+  auto* app_service_proxy =
+      apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)
+          ? apps::AppServiceProxyFactory::GetForProfile(profile)
+          : nullptr;
+  if (!app_service_proxy) {
+    return name;
+  }
 
   app_service_proxy->AppRegistryCache().ForOneApp(
       app_id,
diff --git a/chrome/browser/ui/ash/download_status/display_manager.cc b/chrome/browser/ui/ash/download_status/display_manager.cc
index 6ccb85c..a661ec0 100644
--- a/chrome/browser/ui/ash/download_status/display_manager.cc
+++ b/chrome/browser/ui/ash/download_status/display_manager.cc
@@ -39,7 +39,7 @@
 // Constants -------------------------------------------------------------------
 
 // Indicates an unknown total bytes count of `crosapi::mojom::DownloadStatus`.
-constexpr int64_t kUnknownTotalBytes = -1;
+constexpr int64_t kUnknownTotalBytes = 0;
 
 // Helpers ---------------------------------------------------------------------
 
@@ -97,18 +97,17 @@
 
   if (total_bytes && total_bytes < kUnknownTotalBytes) {
     LOG(ERROR) << "The total bytes count is invalid: expected to be a non "
-                  "negative value or -1 that indicates an unknown total bytes "
+                  "negative value or 0 that indicates an unknown total bytes "
                   "count; the actual value is "
                << GetPrintString(total_bytes);
   }
 
-  // `Progress` does not accept a negative total bytes count.
-  if (updated_total_bytes < 0) {
+  // Use `std::nullopt` to indicate an indeterminate total bytes count.
+  if (updated_total_bytes <= kUnknownTotalBytes) {
     updated_total_bytes = std::nullopt;
   }
 
-  const bool is_determinate =
-      received_bytes && total_bytes && total_bytes != kUnknownTotalBytes;
+  const bool is_determinate = updated_received_bytes && updated_total_bytes;
 
   if (is_determinate && received_bytes > total_bytes) {
     LOG(ERROR) << "For a download that is determinate, its received bytes "
@@ -126,8 +125,7 @@
     updated_received_bytes = updated_total_bytes =
         base::ranges::max({updated_received_bytes, updated_total_bytes,
                            std::optional<int64_t>(0)});
-  } else if (updated_total_bytes >= 0 &&
-             updated_received_bytes > updated_total_bytes) {
+  } else if (is_determinate && updated_received_bytes > updated_total_bytes) {
     updated_total_bytes = updated_received_bytes;
   }
 
diff --git a/chrome/browser/ui/ash/download_status/holding_space_display_client_browsertest.cc b/chrome/browser/ui/ash/download_status/holding_space_display_client_browsertest.cc
index 31dc2ab..021507cb 100644
--- a/chrome/browser/ui/ash/download_status/holding_space_display_client_browsertest.cc
+++ b/chrome/browser/ui/ash/download_status/holding_space_display_client_browsertest.cc
@@ -21,7 +21,10 @@
 #include "ash/public/cpp/rounded_image_view.h"
 #include "ash/style/dark_light_mode_controller_impl.h"
 #include "ash/system/holding_space/holding_space_item_view.h"
+#include "ash/system/progress_indicator/progress_indicator.h"
+#include "ash/test/ash_test_util.h"
 #include "ash/test/view_drawn_waiter.h"
+#include "base/callback_list.h"
 #include "base/run_loop.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -46,6 +49,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia.h"
@@ -643,24 +647,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(HoldingSpaceDisplayClientBrowserTest,
-                       IndeterminateDownload) {
-  // Create a download with an unknown total bytes count.
-  crosapi::mojom::DownloadStatusPtr download =
-      CreateInProgressDownloadStatus(ProfileManager::GetActiveUserProfile(),
-                                     /*extension=*/"txt", /*received_bytes=*/0);
-  Update(download->Clone());
-  test_api().Show();
-
-  // Verify the existence of a single download chip.
-  ASSERT_EQ(test_api().GetDownloadChips().size(), 1u);
-
-  // Complete the download and check the existence of the download chip.
-  download->state = crosapi::mojom::DownloadState::kComplete;
-  Update(download->Clone());
-  EXPECT_EQ(test_api().GetDownloadChips().size(), 1u);
-}
-
-IN_PROC_BROWSER_TEST_F(HoldingSpaceDisplayClientBrowserTest,
                        InProgressDownloadWithHiddenProgress) {
   // Create an in-progress download with an invisible progress. In reality, this
   // could happen when a download is blocked.
@@ -1014,4 +1000,64 @@
       /*expected_count=*/1);
 }
 
+// HoldingSpaceDisplayClientIndeterminateDownloadTest --------------------------
+
+enum class IndeterminateDownloadType {
+  kNullTotalByteSize,
+  kUnknownTotalByteSize,
+};
+
+// Verifies that holding space works as expected with indeterminate downloads.
+class HoldingSpaceDisplayClientIndeterminateDownloadTest
+    : public HoldingSpaceDisplayClientBrowserTest,
+      public testing::WithParamInterface<IndeterminateDownloadType> {
+ protected:
+  std::optional<int64_t> GetTotalByteSize() const {
+    switch (GetParam()) {
+      case IndeterminateDownloadType::kNullTotalByteSize:
+        return std::nullopt;
+      case IndeterminateDownloadType::kUnknownTotalByteSize:
+        return 0;
+    }
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    HoldingSpaceDisplayClientIndeterminateDownloadTest,
+    testing::Values(IndeterminateDownloadType::kNullTotalByteSize,
+                    IndeterminateDownloadType::kUnknownTotalByteSize));
+
+IN_PROC_BROWSER_TEST_P(HoldingSpaceDisplayClientIndeterminateDownloadTest,
+                       Basics) {
+  crosapi::mojom::DownloadStatusPtr download = CreateInProgressDownloadStatus(
+      ProfileManager::GetActiveUserProfile(),
+      /*extension=*/"txt", /*received_bytes=*/0, GetTotalByteSize());
+  Update(download->Clone());
+  test_api().Show();
+
+  // Verify the existence of a single download chip.
+  std::vector<views::View*> const chips = test_api().GetDownloadChips();
+  ASSERT_EQ(chips.size(), 1u);
+
+  ProgressIndicator* const progress_indicator = static_cast<ProgressIndicator*>(
+      FindLayerWithName(chips[0], ProgressIndicator::kClassName)->owner());
+  ASSERT_TRUE(progress_indicator);
+
+  // Wait until `progress_indicator` updates.
+  base::test::TestFuture<void> progress_change_waiter;
+  base::CallbackListSubscription subscription =
+      progress_indicator->AddProgressChangedCallback(
+          progress_change_waiter.GetRepeatingCallback());
+  ASSERT_TRUE(progress_change_waiter.Wait());
+
+  // The progress indicator should suggest an indeterminate download.
+  EXPECT_EQ(progress_indicator->progress(), std::nullopt);
+
+  // Complete the download and check the existence of the download chip.
+  download->state = crosapi::mojom::DownloadState::kComplete;
+  Update(download->Clone());
+  EXPECT_EQ(test_api().GetDownloadChips().size(), 1u);
+}
+
 }  // namespace ash::download_status
diff --git a/chrome/browser/ui/ash/download_status/notification_display_client_browsertest.cc b/chrome/browser/ui/ash/download_status/notification_display_client_browsertest.cc
index 1200f68..fb931b6 100644
--- a/chrome/browser/ui/ash/download_status/notification_display_client_browsertest.cc
+++ b/chrome/browser/ui/ash/download_status/notification_display_client_browsertest.cc
@@ -672,46 +672,6 @@
               ElementsAre(Field(&ui::FileInfo::path, *download->full_path)));
 }
 
-// Verifies that the notification of a download with an unknown total bytes
-// count works as expected.
-IN_PROC_BROWSER_TEST_F(NotificationDisplayClientBrowserTest,
-                       IndeterminateDownload) {
-  std::string notification_id;
-  EXPECT_CALL(service_observer(), OnNotificationDisplayed)
-      .WillOnce(WithArg<0>(
-          [&notification_id](const message_center::Notification& notification) {
-            notification_id = notification.id();
-          }));
-
-  Profile* const profile = ProfileManager::GetActiveUserProfile();
-  crosapi::mojom::DownloadStatusPtr download =
-      CreateInProgressDownloadStatus(profile,
-                                     /*extension=*/"txt",
-                                     /*received_bytes=*/0);
-
-  Update(download->Clone());
-  Mock::VerifyAndClearExpectations(&service_observer());
-
-  // Verify that the notification view of an in-progress download has a visible
-  // progress bar.
-  AshNotificationView* popup_view = GetPopupView(profile, notification_id);
-  ASSERT_TRUE(popup_view);
-  const views::ProgressBar* const progress_bar =
-      popup_view->progress_bar_view_for_testing();
-  ASSERT_TRUE(progress_bar);
-  EXPECT_TRUE(progress_bar->GetVisible());
-
-  // Complete the download. Check the existence of the associated notification.
-  MarkDownloadStatusCompleted(*download);
-  Update(download->Clone());
-  EXPECT_THAT(GetDisplayedNotificationIds(), Contains(notification_id));
-
-  // Verify that the notification view of a completed download does not have
-  // a progress bar.
-  popup_view = GetPopupView(profile, notification_id);
-  ASSERT_TRUE(popup_view);
-  EXPECT_FALSE(popup_view->progress_bar_view_for_testing());
-}
 
 // Verifies that when an in-progress download is interrupted, its notification
 // should be removed.
@@ -981,4 +941,72 @@
             1);
 }
 
+// NotificationDisplayClientIndeterminateDownloadTest --------------------------
+
+enum class IndeterminateDownloadType {
+  kNullTotalByteSize,
+  kUnknownTotalByteSize,
+};
+
+// Verifies the notification of an indeterminate download works as expected.
+class NotificationDisplayClientIndeterminateDownloadTest
+    : public NotificationDisplayClientBrowserTest,
+      public testing::WithParamInterface<IndeterminateDownloadType> {
+ protected:
+  std::optional<int64_t> GetTotalByteSize() const {
+    switch (GetParam()) {
+      case IndeterminateDownloadType::kNullTotalByteSize:
+        return std::nullopt;
+      case IndeterminateDownloadType::kUnknownTotalByteSize:
+        return 0;
+    }
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    NotificationDisplayClientIndeterminateDownloadTest,
+    testing::Values(IndeterminateDownloadType::kNullTotalByteSize,
+                    IndeterminateDownloadType::kUnknownTotalByteSize));
+
+IN_PROC_BROWSER_TEST_P(NotificationDisplayClientIndeterminateDownloadTest,
+                       Basics) {
+  std::string notification_id;
+  EXPECT_CALL(service_observer(), OnNotificationDisplayed)
+      .WillOnce(WithArg<0>(
+          [&notification_id](const message_center::Notification& notification) {
+            notification_id = notification.id();
+          }));
+
+  Profile* const profile = ProfileManager::GetActiveUserProfile();
+  crosapi::mojom::DownloadStatusPtr download =
+      CreateInProgressDownloadStatus(profile,
+                                     /*extension=*/"txt",
+                                     /*received_bytes=*/0, GetTotalByteSize());
+
+  Update(download->Clone());
+  Mock::VerifyAndClearExpectations(&service_observer());
+
+  // Verify that the notification view of an in-progress download has a visible
+  // indeterminate progress bar.
+  AshNotificationView* popup_view = GetPopupView(profile, notification_id);
+  ASSERT_TRUE(popup_view);
+  const views::ProgressBar* const progress_bar =
+      popup_view->progress_bar_view_for_testing();
+  ASSERT_TRUE(progress_bar);
+  EXPECT_EQ(progress_bar->GetValue(), -1);
+  EXPECT_TRUE(progress_bar->GetVisible());
+
+  // Complete the download. Check the existence of the associated notification.
+  MarkDownloadStatusCompleted(*download);
+  Update(download->Clone());
+  EXPECT_THAT(GetDisplayedNotificationIds(), Contains(notification_id));
+
+  // Verify that the notification view of a completed download does not have
+  // a progress bar.
+  popup_view = GetPopupView(profile, notification_id);
+  ASSERT_TRUE(popup_view);
+  EXPECT_FALSE(popup_view->progress_bar_view_for_testing());
+}
+
 }  // namespace ash::download_status
diff --git a/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc b/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc
index c658297..6614817 100644
--- a/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc
+++ b/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc
@@ -42,8 +42,6 @@
 
     // NetworkConnect:
     void DisconnectFromNetworkId(const std::string& network_id) override {}
-    void SetTechnologyEnabled(const NetworkTypePattern& technology,
-                              bool enabled_state) override {}
     void ShowMobileSetup(const std::string& network_id) override {}
     void ShowCarrierAccountDetail(const std::string& network_id) override {}
     void ShowCarrierUnlockNotification() override {}
diff --git a/chrome/browser/ui/autofill/autofill_context_menu_manager.cc b/chrome/browser/ui/autofill/autofill_context_menu_manager.cc
index 3307ff7..f26428c 100644
--- a/chrome/browser/ui/autofill/autofill_context_menu_manager.cc
+++ b/chrome/browser/ui/autofill/autofill_context_menu_manager.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/user_education/scoped_new_badge_tracker.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/autofill/content/browser/content_autofill_client.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
@@ -28,6 +27,7 @@
 #include "components/autofill/core/browser/field_type_utils.h"
 #include "components/autofill/core/browser/form_types.h"
 #include "components/autofill/core/browser/metrics/fallback_autocomplete_unrecognized_metrics.h"
+#include "components/autofill/core/browser/metrics/manual_fallback_metrics.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/common/aliases.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -300,11 +300,19 @@
             [](PersonalDataManager* pdm,
                AutofillClient::AddressPromptUserDecision decision,
                base::optional_ref<const AutofillProfile> profile) {
-              if (decision == AutofillClient::AddressPromptUserDecision::
-                                  kEditAccepted &&
-                  pdm && profile.has_value()) {
+              bool new_address_saved =
+                  decision ==
+                  AutofillClient::AddressPromptUserDecision::kEditAccepted;
+              if (new_address_saved && profile.has_value()) {
                 pdm->AddProfile(*profile);
               }
+
+              LogAddNewAddressPromptOutcome(
+                  new_address_saved
+                      ? autofill_metrics::AutofillAddNewAddressPromptOutcome::
+                            kSaved
+                      : autofill_metrics::AutofillAddNewAddressPromptOutcome::
+                            kCanceled);
             },
             // `PersonalDataManager`, as a keyed service, will always outlive
             // the bubble, which is bound to a tab.
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 1251662..2ab19516 100644
--- a/chrome/browser/ui/autofill/autofill_context_menu_manager_browsertest.cc
+++ b/chrome/browser/ui/autofill/autofill_context_menu_manager_browsertest.cc
@@ -7,6 +7,7 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/test/metrics/histogram_tester.h"
@@ -28,6 +29,7 @@
 #include "components/autofill/content/browser/test_content_autofill_client.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
+#include "components/autofill/core/browser/metrics/manual_fallback_metrics.h"
 #include "components/autofill/core/browser/personal_data_manager_test_utils.h"
 #include "components/autofill/core/browser/test_autofill_manager_waiter.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
@@ -543,31 +545,79 @@
       IDC_CONTENT_CONTEXT_AUTOFILL_FALLBACK_ADDRESS);
 }
 
+class AddNewAddressBubbleTest : public UnclassifiedFieldsTest {
+ public:
+  void SetUpOnMainThread() override {
+    UnclassifiedFieldsTest::SetUpOnMainThread();
+
+    autofill_client()->GetPersonalDataManager()->SetAutofillProfileEnabled(
+        true);
+
+    FormData form = CreateAndAttachUnclassifiedForm();
+    autofill_context_menu_manager()->set_params_for_testing(
+        CreateContextMenuParams(form.renderer_id, form.fields[0].renderer_id));
+    autofill_context_menu_manager()->AppendItems();
+
+    ASSERT_EQ(AddressBubblesController::FromWebContents(web_contents()),
+              nullptr);
+    EXPECT_CALL(*driver(), RendererShouldTriggerSuggestions).Times(0);
+
+    autofill_context_menu_manager()->ExecuteCommand(
+        IDC_CONTENT_CONTEXT_AUTOFILL_FALLBACK_ADDRESS);
+
+    ASSERT_NE(bubble_controller(), nullptr);
+  }
+
+ protected:
+  AddressBubblesController* bubble_controller() {
+    return AddressBubblesController::FromWebContents(web_contents());
+  }
+};
+
 // Tests that when the address manual fallback entry is selected and there are
 // no saved profiles, the "Add new address" bubble is triggered.
 IN_PROC_BROWSER_TEST_F(
-    UnclassifiedFieldsTest,
+    AddNewAddressBubbleTest,
     UnclassifiedFormShown_AddressFallbackTriggersAddNewAddressBubble) {
-  autofill_client()->GetPersonalDataManager()->SetAutofillProfileEnabled(true);
-  FormData form = CreateAndAttachUnclassifiedForm();
-  autofill_context_menu_manager()->set_params_for_testing(
-      CreateContextMenuParams(form.renderer_id, form.fields[0].renderer_id));
-  autofill_context_menu_manager()->AppendItems();
-
-  ASSERT_EQ(AddressBubblesController::FromWebContents(web_contents()), nullptr);
-  EXPECT_CALL(*driver(), RendererShouldTriggerSuggestions).Times(0);
-  autofill_context_menu_manager()->ExecuteCommand(
-      IDC_CONTENT_CONTEXT_AUTOFILL_FALLBACK_ADDRESS);
-
   // Expect that when the entry is selected, the "add new address" bubble is
   // triggered.
-  auto* controller = AddressBubblesController::FromWebContents(web_contents());
-  ASSERT_NE(controller, nullptr);
   EXPECT_EQ(
-      controller->GetPageActionIconTootip(),
+      bubble_controller()->GetPageActionIconTootip(),
       l10n_util::GetStringUTF16(IDS_AUTOFILL_ADD_NEW_ADDRESS_PROMPT_TITLE));
 }
 
+// Tests that the "Autofill.ManualFallback.AddNewAddressPromptShown" metric is
+// sent when the user accepts the prompt and saves an address via the editor.
+IN_PROC_BROWSER_TEST_F(AddNewAddressBubbleTest,
+                       UnclassifiedFormShown_AddAddressMetricsAreSentOnSave) {
+  base::HistogramTester histogram_tester;
+
+  // Imitate the user's decision.
+  bubble_controller()->OnUserDecision(
+      AutofillClient::AddressPromptUserDecision::kEditAccepted, std::nullopt);
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ManualFallback.AddNewAddressPromptShown",
+      autofill_metrics::AutofillAddNewAddressPromptOutcome::kSaved,
+      /*expected_bucket_count=*/1);
+}
+
+// Tests that the "Autofill.ManualFallback.AddNewAddressPromptShown" metric is
+// sent when the user declines the prompt.
+IN_PROC_BROWSER_TEST_F(AddNewAddressBubbleTest,
+                       UnclassifiedFormShown_AddAddressMetricsAreSentOnCancel) {
+  base::HistogramTester histogram_tester;
+
+  // Imitate the user's decision.
+  bubble_controller()->OnUserDecision(
+      AutofillClient::AddressPromptUserDecision::kDeclined, std::nullopt);
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ManualFallback.AddNewAddressPromptShown",
+      autofill_metrics::AutofillAddNewAddressPromptOutcome::kCanceled,
+      /*expected_bucket_count=*/1);
+}
+
 // Tests that when the payments manual fallback entry for the unclassified
 // fields is selected, suggestions are triggered with correct field global id
 // and suggestions trigger source.
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index c4adf7c..b008044 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -62,6 +62,9 @@
 #include "components/password_manager/core/common/password_manager_features.h"
 
 using FillingSource = ManualFillingController::FillingSource;
+#else
+#include "chrome/browser/user_education/user_education_service.h"
+#include "components/compose/core/browser/compose_features.h"
 #endif
 
 using base::WeakPtr;
@@ -442,6 +445,12 @@
     return;
   }
 
+#if !BUILDFLAG(IS_ANDROID)
+  UserEducationService::MaybeNotifyPromoFeatureUsed(
+      web_contents_->GetBrowserContext(),
+      compose::features::kEnableComposeNudge);
+#endif
+
   // Use a copy instead of a reference here. Under certain circumstances,
   // `DidAcceptSuggestion()` can call `SetSuggestions()` and invalidate the
   // reference.
diff --git a/chrome/browser/ui/commerce/commerce_ui_tab_helper_unittest.cc b/chrome/browser/ui/commerce/commerce_ui_tab_helper_unittest.cc
index 9f2d63e..db20c66 100644
--- a/chrome/browser/ui/commerce/commerce_ui_tab_helper_unittest.cc
+++ b/chrome/browser/ui/commerce/commerce_ui_tab_helper_unittest.cc
@@ -71,8 +71,12 @@
  public:
   CommerceUiTabHelperTest()
       : shopping_service_(std::make_unique<MockShoppingService>()),
-        bookmark_model_(bookmarks::TestBookmarkClient::CreateModel()),
-        image_fetcher_(std::make_unique<image_fetcher::MockImageFetcher>()) {}
+        image_fetcher_(std::make_unique<image_fetcher::MockImageFetcher>()) {
+    auto client = std::make_unique<bookmarks::TestBookmarkClient>();
+    client->SetIsSyncFeatureEnabledIncludingBookmarks(true);
+    bookmark_model_ =
+        bookmarks::TestBookmarkClient::CreateModelWithClient(std::move(client));
+  }
 
   CommerceUiTabHelperTest(const CommerceUiTabHelperTest&) = delete;
   CommerceUiTabHelperTest operator=(const CommerceUiTabHelperTest&) =
diff --git a/chrome/browser/ui/download/download_bubble_security_view_info.cc b/chrome/browser/ui/download/download_bubble_security_view_info.cc
index a1165376..372687a 100644
--- a/chrome/browser/ui/download/download_bubble_security_view_info.cc
+++ b/chrome/browser/ui/download/download_bubble_security_view_info.cc
@@ -398,6 +398,9 @@
       PopulateSecondarySubpageButton(
           l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_OPEN_UPDATED),
           DownloadCommands::Command::BYPASS_DEEP_SCANNING);
+      PopulateLearnMoreLink(l10n_util::GetStringUTF16(
+                                IDS_DOWNLOAD_BUBBLE_SUBPAGE_DEEP_SCANNING_LINK),
+                            DownloadCommands::LEARN_MORE_SCANNING);
       return;
     }
     case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING: {
@@ -419,6 +422,10 @@
           l10n_util::GetStringUTF16(
               IDS_DOWNLOAD_BUBBLE_BYPASS_LOCAL_DECRYPTION),
           DownloadCommands::Command::KEEP);
+      PopulateLearnMoreLink(
+          l10n_util::GetStringUTF16(
+              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_BLOCKED_LEARN_MORE_LINK),
+          DownloadCommands::LEARN_MORE_SCANNING);
       return;
     }
     case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
@@ -439,6 +446,10 @@
             l10n_util::GetStringUTF16(
                 IDS_DOWNLOAD_BUBBLE_SUBPAGE_IMMEDIATE_DEEP_SCAN_BYPASS),
             DownloadCommands::Command::BYPASS_DEEP_SCANNING);
+        PopulateLearnMoreLink(
+            l10n_util::GetStringUTF16(
+                IDS_DOWNLOAD_BUBBLE_SUBPAGE_DEEP_SCANNING_LINK),
+            DownloadCommands::LEARN_MORE_SCANNING);
       } else {
         warning_summary_ = l10n_util::GetStringUTF16(
             IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING);
diff --git a/chrome/browser/ui/download/download_item_mode.cc b/chrome/browser/ui/download/download_item_mode.cc
index d833b51..2e234ab 100644
--- a/chrome/browser/ui/download/download_item_mode.cc
+++ b/chrome/browser/ui/download/download_item_mode.cc
@@ -8,7 +8,7 @@
 
 namespace download {
 
-DownloadItemMode GetDesiredDownloadItemMode(DownloadUIModel* download) {
+DownloadItemMode GetDesiredDownloadItemMode(const DownloadUIModel* download) {
   if (download->IsInsecure()) {
     const bool warn = download->GetInsecureDownloadStatus() ==
                       download::DownloadItem::InsecureDownloadStatus::WARN;
diff --git a/chrome/browser/ui/download/download_item_mode.h b/chrome/browser/ui/download/download_item_mode.h
index 89848b7..951cf0c 100644
--- a/chrome/browser/ui/download/download_item_mode.h
+++ b/chrome/browser/ui/download/download_item_mode.h
@@ -20,7 +20,7 @@
 };
 
 // Returns the mode that best reflects the current model state.
-DownloadItemMode GetDesiredDownloadItemMode(DownloadUIModel* download);
+DownloadItemMode GetDesiredDownloadItemMode(const DownloadUIModel* download);
 
 }  // namespace download
 
diff --git a/chrome/browser/ui/profiles/profile_picker.h b/chrome/browser/ui/profiles/profile_picker.h
index 76957ce0..90a34595 100644
--- a/chrome/browser/ui/profiles/profile_picker.h
+++ b/chrome/browser/ui/profiles/profile_picker.h
@@ -249,18 +249,6 @@
       base::OnceCallback<void(ReauthUIError)> on_error_callback);
 #endif
 
-  // Switch to the flow that comes when the user decides to create a profile
-  // without signing in.
-  // `profile_color` is the profile's color. It is undefined for the default
-  // theme.
-  // `profile_picked_time_on_startup` is the time when the user picked a
-  // profile to open, to measure browser startup performance. It is only set
-  // when the picker is shown on startup.
-  static void SwitchToSignedOutPostIdentityFlow(
-      std::optional<SkColor> profile_color,
-      base::TimeTicks profile_picked_time_on_startup,
-      base::OnceCallback<void(bool)> switch_finished_callback);
-
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   // Starts the flow to set-up a signed-in profile. `signed_in_profile` must
   // have an unconsented primary account.
diff --git a/chrome/browser/ui/startup/default_browser_prompt_manager.cc b/chrome/browser/ui/startup/default_browser_prompt_manager.cc
index c8ada411..982cd50 100644
--- a/chrome/browser/ui/startup/default_browser_prompt_manager.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt_manager.cc
@@ -65,7 +65,9 @@
 }
 
 bool DefaultBrowserPromptManager::ShouldTrackBrowser(Browser* browser) {
-  return browser->is_type_normal();
+  return browser->is_type_normal() &&
+         !browser->profile()->IsIncognitoProfile() &&
+         !browser->profile()->IsGuestSession();
 }
 
 void DefaultBrowserPromptManager::OnTabStripModelChanged(
diff --git a/chrome/browser/ui/tabs/organization/request_factory.cc b/chrome/browser/ui/tabs/organization/request_factory.cc
index 1f958769..1265e50 100644
--- a/chrome/browser/ui/tabs/organization/request_factory.cc
+++ b/chrome/browser/ui/tabs/organization/request_factory.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/ui/tabs/organization/logging_util.h"
 #include "chrome/browser/ui/tabs/organization/tab_organization_request.h"
 #include "chrome/browser/ui/tabs/organization/tab_organization_session.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "components/optimization_guide/core/model_quality/feature_type_map.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_model_executor.h"
@@ -171,6 +172,9 @@
     tab_organization_request.set_active_tab_id(request->base_tab_id().value());
   }
 
+  tab_organization_request.set_allow_reorganizing_existing_groups(
+      base::FeatureList::IsEnabled(features::kTabReorganization));
+
   OptimizationGuideKeyedService* optimization_guide_keyed_service =
       OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
   optimization_guide_keyed_service->ExecuteModel(
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 8cdbf6a..84ba116 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -72,6 +72,7 @@
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_tab_helper.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
@@ -100,6 +101,7 @@
 #include "components/webapps/browser/banners/app_banner_manager.h"
 #include "components/webapps/browser/banners/installable_web_app_check_result.h"
 #include "components/webapps/browser/banners/web_app_banner_data.h"
+#include "components/webapps/common/web_app_id.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/profiling.h"
@@ -205,6 +207,18 @@
     return std::u16string();
   }
 
+  // TODO(b/328077967): Support async nature of AppBannerManager pipeline runs
+  // with the menu model instead of needing this workaround to verify if an
+  // non-installable site is installed.
+  const webapps::AppId* app_id =
+      web_app::WebAppTabHelper::GetAppId(web_contents);
+  web_app::WebAppProvider* const provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(browser->profile());
+  if (app_id && provider->registrar_unsafe().GetAppUserDisplayMode(*app_id) !=
+                    web_app::mojom::UserDisplayMode::kBrowser) {
+    return std::u16string();
+  }
+
   webapps::AppBannerManager* banner =
       webapps::AppBannerManager::FromWebContents(web_contents);
   if (!banner) {
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index de4f80f..7744a9f 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -303,6 +303,10 @@
              "MultiTabOrganization",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kTabReorganization,
+             "TabReorganization",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 const base::FeatureParam<base::TimeDelta> kTabOrganizationTriggerPeriod{
     &kTabOrganization, "trigger_period", base::Hours(6)};
 
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 040433d..087732e 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -171,6 +171,8 @@
 
 BASE_DECLARE_FEATURE(kMultiTabOrganization);
 
+BASE_DECLARE_FEATURE(kTabReorganization);
+
 // The target (and minimum) interval between proactive nudge triggers. Measured
 // against a clock that only runs while Chrome is in the foreground.
 extern const base::FeatureParam<base::TimeDelta> kTabOrganizationTriggerPeriod;
diff --git a/chrome/browser/ui/user_education/scoped_new_badge_tracker.cc b/chrome/browser/ui/user_education/scoped_new_badge_tracker.cc
deleted file mode 100644
index fc9ab79..0000000
--- a/chrome/browser/ui/user_education/scoped_new_badge_tracker.cc
+++ /dev/null
@@ -1,16 +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/user_education/scoped_new_badge_tracker.h"
-
-#include "chrome/browser/feature_engagement/tracker_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-
-ScopedNewBadgeTracker::ScopedNewBadgeTracker(content::BrowserContext* profile)
-    : ScopedNewBadgeTrackerBase(
-          feature_engagement::TrackerFactory::GetForBrowserContext(profile)) {}
-
-ScopedNewBadgeTracker::ScopedNewBadgeTracker(Browser* browser)
-    : ScopedNewBadgeTracker(browser->profile()) {}
diff --git a/chrome/browser/ui/user_education/scoped_new_badge_tracker.h b/chrome/browser/ui/user_education/scoped_new_badge_tracker.h
deleted file mode 100644
index 10f6823..0000000
--- a/chrome/browser/ui/user_education/scoped_new_badge_tracker.h
+++ /dev/null
@@ -1,29 +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_USER_EDUCATION_SCOPED_NEW_BADGE_TRACKER_H_
-#define CHROME_BROWSER_UI_USER_EDUCATION_SCOPED_NEW_BADGE_TRACKER_H_
-
-#include "components/user_education/common/scoped_new_badge_tracker_base.h"
-
-namespace content {
-class BrowserContext;
-}
-
-class Browser;
-
-// Implementation of ScopedNewBadgeTrackerBase that allows you to pass a
-// Browser or Profile to the constructor.
-//
-// See documentation on ScopedNewBadgeTrackerBase.
-class ScopedNewBadgeTracker : public user_education::ScopedNewBadgeTrackerBase {
- public:
-  // Constructs a scoped tracker for browser with |profile|.
-  explicit ScopedNewBadgeTracker(content::BrowserContext* profile);
-
-  // Constructs a scoped tracker for browser from |browser|.
-  explicit ScopedNewBadgeTracker(Browser* browser);
-};
-
-#endif  // CHROME_BROWSER_UI_USER_EDUCATION_SCOPED_NEW_BADGE_TRACKER_H_
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 bca0e24..67fedc9 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
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/views/autofill/popup/popup_row_view.h"
 #include "chrome/browser/ui/views/autofill/popup/popup_row_with_button_view.h"
 #include "chrome/browser/ui/views/autofill/popup/popup_view_utils.h"
+#include "chrome/browser/user_education/user_education_service.h"
 #include "components/autofill/core/browser/filling_product.h"
 #include "components/autofill/core/browser/metrics/autofill_metrics.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
@@ -336,21 +337,13 @@
           a11y_selection_delegate, selection_delegate, controller, line_number,
           CreatePasswordPopupRowContentView(suggestion));
     case PopupItemId::kCompose: {
-      auto tracker = std::make_unique<ScopedNewBadgeTracker>(
-          controller->GetWebContents()->GetBrowserContext());
-      const bool show_new_badge = tracker->TryShowNewBadge(
-          feature_engagement::kIPHComposeNewBadgeFeature,
-          &compose::features::kEnableComposeNudge);
-      auto new_badge_tracker =
-          PopupRowView::ScopedNewBadgeTrackerWithAcceptAction(
-              std::move(tracker),
-              /*action_name=*/"compose_activated");
-      auto row_view = std::make_unique<PopupRowView>(
+      const bool show_new_badge = UserEducationService::MaybeShowNewBadge(
+          controller->GetWebContents()->GetBrowserContext(),
+          compose::features::kEnableComposeNudge);
+      return std::make_unique<PopupRowView>(
           a11y_selection_delegate, selection_delegate, controller, line_number,
           CreateComposePopupRowContentView(suggestion, show_new_badge));
-      row_view->set_new_badge_tracker(std::move(new_badge_tracker));
-      return row_view;
-    };
+    }
     default:
       return std::make_unique<PopupRowView>(
           a11y_selection_delegate, selection_delegate, controller, line_number,
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils_browsertest.cc b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils_browsertest.cc
index ac0a617b..726db0d7 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils_browsertest.cc
@@ -18,6 +18,7 @@
 #include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/user_education/common/new_badge_controller.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/test/widget_test.h"
@@ -92,6 +93,9 @@
     : public UiBrowserTest,
       public ::testing::WithParamInterface<TestParams> {
  public:
+  CreatePopupRowViewTest() = default;
+  ~CreatePopupRowViewTest() override = default;
+
   static std::string GetTestName(
       const testing::TestParamInfo<TestParams>& info) {
     const std::string suggestion_part =
@@ -172,6 +176,8 @@
   NiceMock<MockSelectionDelegate> mock_selection_delegate_;
   base::test::ScopedFeatureList feature_list{
       features::kAutofillShowAutocompleteDeleteButton};
+  user_education::NewBadgeController::TestLock disable_new_badges_ =
+      user_education::NewBadgeController::DisableNewBadgesForTesting();
 };
 
 IN_PROC_BROWSER_TEST_P(CreatePopupRowViewTest, SuggestionRowUiTest) {
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_view.cc b/chrome/browser/ui/views/autofill/popup/popup_row_view.cc
index 762865ac..b304e32 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_row_view.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_row_view.cc
@@ -15,7 +15,6 @@
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
-#include "chrome/browser/ui/user_education/scoped_new_badge_tracker.h"
 #include "chrome/browser/ui/views/autofill/popup/popup_cell_utils.h"
 #include "chrome/browser/ui/views/autofill/popup/popup_row_content_view.h"
 #include "chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.h"
@@ -152,30 +151,6 @@
   }
 }
 
-PopupRowView::ScopedNewBadgeTrackerWithAcceptAction::
-    ScopedNewBadgeTrackerWithAcceptAction(
-        std::unique_ptr<ScopedNewBadgeTracker> tracker,
-        const char* action_name)
-    : tracker_(std::move(tracker)), action_name_(action_name) {
-  CHECK(tracker_);
-}
-
-PopupRowView::ScopedNewBadgeTrackerWithAcceptAction::
-    ~ScopedNewBadgeTrackerWithAcceptAction() = default;
-
-PopupRowView::ScopedNewBadgeTrackerWithAcceptAction::
-    ScopedNewBadgeTrackerWithAcceptAction(
-        ScopedNewBadgeTrackerWithAcceptAction&&) = default;
-
-PopupRowView::ScopedNewBadgeTrackerWithAcceptAction&
-PopupRowView::ScopedNewBadgeTrackerWithAcceptAction::operator=(
-    ScopedNewBadgeTrackerWithAcceptAction&&) = default;
-
-void PopupRowView::ScopedNewBadgeTrackerWithAcceptAction::
-    OnSuggestionAccepted() {
-  tracker_->ActionPerformed(action_name_);
-}
-
 PopupRowView::PopupRowView(
     AccessibilitySelectionDelegate& a11y_selection_delegate,
     SelectionDelegate& selection_delegate,
@@ -448,9 +423,6 @@
   if (!controller_) {
     return;
   }
-  if (new_badge_tracker_) {
-    new_badge_tracker_->OnSuggestionAccepted();
-  }
   controller_->AcceptSuggestion(line_number_);
 }
 
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_view.h b/chrome/browser/ui/views/autofill/popup/popup_row_view.h
index efe78050..d0a173a 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_row_view.h
+++ b/chrome/browser/ui/views/autofill/popup/popup_row_view.h
@@ -12,7 +12,6 @@
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
-#include "chrome/browser/ui/user_education/scoped_new_badge_tracker.h"
 #include "chrome/browser/ui/views/autofill/popup/popup_view_utils.h"
 #include "content/public/common/input/native_web_keyboard_event.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -25,8 +24,6 @@
 struct NativeWebKeyboardEvent;
 }  // namespace content
 
-class ScopedNewBadgeTracker;
-
 namespace autofill {
 
 class AutofillPopupController;
@@ -74,30 +71,6 @@
                                  PopupCellSelectionSource source) = 0;
   };
 
-  // The tracker for a "new" badge that a row might have.
-  class ScopedNewBadgeTrackerWithAcceptAction {
-   public:
-    ScopedNewBadgeTrackerWithAcceptAction(
-        std::unique_ptr<ScopedNewBadgeTracker> tracker,
-        const char* action_name);
-    ~ScopedNewBadgeTrackerWithAcceptAction();
-
-    ScopedNewBadgeTrackerWithAcceptAction(
-        ScopedNewBadgeTrackerWithAcceptAction&&);
-    ScopedNewBadgeTrackerWithAcceptAction& operator=(
-        ScopedNewBadgeTrackerWithAcceptAction&&);
-
-    // Notifies the tracker that the accept action was performed, i.e. the
-    // feature was opened.
-    void OnSuggestionAccepted();
-
-   private:
-    // The actual badge tracker.
-    std::unique_ptr<ScopedNewBadgeTracker> tracker_;
-    // The name of the action that is triggered on accepting the suggestion.
-    const char* action_name_;
-  };
-
   PopupRowView(AccessibilitySelectionDelegate& a11y_selection_delegate,
                SelectionDelegate& selection_delegate,
                base::WeakPtr<AutofillPopupController> controller,
@@ -107,11 +80,6 @@
   PopupRowView& operator=(const PopupRowView&) = delete;
   ~PopupRowView() override;
 
-  void set_new_badge_tracker(
-      std::optional<ScopedNewBadgeTrackerWithAcceptAction> new_badge_tracker) {
-    new_badge_tracker_ = std::move(new_badge_tracker);
-  }
-
   // views::View:
   bool OnMouseDragged(const ui::MouseEvent& event) override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
@@ -173,9 +141,6 @@
   const base::WeakPtr<AutofillPopupController> controller_;
   // The position of the row in the vertical list of suggestions.
   const int line_number_;
-  // A tracker for "new" badges inside a cell. If set, it logs a performed
-  // action on accepting the suggestion.
-  std::optional<ScopedNewBadgeTrackerWithAcceptAction> new_badge_tracker_;
 
   // Which (if any) cell of this row is currently selected.
   std::optional<CellType> selected_cell_;
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
index 721a814..e32f158 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
@@ -92,7 +92,7 @@
 
 SavedTabGroupButton::SavedTabGroupButton(
     const SavedTabGroup& group,
-    base::RepeatingCallback<content::PageNavigator*()> page_navigator,
+    base::RepeatingCallback<content::PageNavigator*()> page_navigator_callback,
     PressedCallback callback,
     Browser* browser,
     bool animations_enabled)
@@ -104,7 +104,7 @@
       browser_(*browser),
       service_(
           *SavedTabGroupServiceFactory::GetForProfile(browser_->profile())),
-      page_navigator_callback_(std::move(page_navigator)),
+      page_navigator_callback_(std::move(page_navigator_callback)),
       context_menu_controller_(
           this,
           base::BindRepeating(
@@ -172,6 +172,12 @@
   return false;
 }
 
+bool SavedTabGroupButton::IsTriggerableEvent(const ui::Event& e) {
+  return e.type() == ui::ET_GESTURE_TAP ||
+         e.type() == ui::ET_GESTURE_TAP_DOWN ||
+         event_utils::IsPossibleDispositionEvent(e);
+}
+
 void SavedTabGroupButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   views::MenuButton::GetAccessibleNodeData(node_data);
   node_data->SetNameChecked(GetAccessibleNameForButton());
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.h b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.h
index 039bc61..b5991b8 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.h
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "chrome/browser/ui/views/event_utils.h"
 #include "components/saved_tab_groups/saved_tab_group.h"
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
@@ -59,6 +60,9 @@
   // views::View
   bool OnKeyPressed(const ui::KeyEvent& event) override;
 
+  // views::LabelButton
+  bool IsTriggerableEvent(const ui::Event& e) override;
+
   // views::DragController
   void WriteDragDataForView(View* sender,
                             const gfx::Point& press_pt,
diff --git a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view_unittest.cc b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view_unittest.cc
index adb7fb9..8131ec4b 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view_unittest.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/test/mock_callback.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/browser/sync/local_or_syncable_bookmark_sync_service_factory.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_editor_view.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/views/chrome_test_widget.h"
@@ -15,6 +16,7 @@
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/sync_bookmarks/bookmark_sync_service.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
@@ -98,6 +100,10 @@
   virtual void SetUpDependencies() {
     bookmark_model_ = BookmarkModelFactory::GetForBrowserContext(profile());
     bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model_);
+
+    // Pretend sync is on for bookmarks, required for price tracking.
+    LocalOrSyncableBookmarkSyncServiceFactory::GetForProfile(profile())
+        ->SetIsTrackingMetadataForTesting();
   }
 
   raw_ptr<bookmarks::BookmarkModel, DanglingUntriaged> bookmark_model_;
diff --git a/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc
index 3eeff29b..e6d7a74 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/commerce/shopping_service_factory.h"
 #include "chrome/browser/image_fetcher/image_fetcher_service_factory.h"
 #include "chrome/browser/profiles/profile_key.h"
+#include "chrome/browser/sync/local_or_syncable_bookmark_sync_service_factory.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/commerce/commerce_ui_tab_helper.h"
 #include "chrome/browser/ui/commerce/mock_commerce_ui_tab_helper.h"
@@ -37,6 +38,7 @@
 #include "components/omnibox/browser/vector_icons.h"
 #include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/sync_bookmarks/bookmark_sync_service.h"
 #include "content/public/test/browser_test.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -90,6 +92,11 @@
     InteractiveBrowserTest::SetUpOnMainThread();
 
     SetUpTabHelperAndShoppingService();
+
+    // Pretend sync is on for bookmarks, required for price tracking.
+    LocalOrSyncableBookmarkSyncServiceFactory::GetForProfile(
+        browser()->profile())
+        ->SetIsTrackingMetadataForTesting();
   }
 
   void SetUpInProcessBrowserTestFixture() override {
diff --git a/chrome/browser/ui/views/commerce/price_tracking_view_unittest.cc b/chrome/browser/ui/views/commerce/price_tracking_view_unittest.cc
index 70b14dd..14279639 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_view_unittest.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_view_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/commerce/shopping_service_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/browser/sync/local_or_syncable_bookmark_sync_service_factory.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/views/chrome_test_widget.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
@@ -22,6 +23,7 @@
 #include "components/commerce/core/test_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/sync_bookmarks/bookmark_sync_service.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
 #include "ui/events/event.h"
@@ -75,6 +77,10 @@
         BookmarkModelFactory::GetForBrowserContext(profile());
     bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model);
 
+    // Pretend sync is on for bookmarks.
+    LocalOrSyncableBookmarkSyncServiceFactory::GetForProfile(profile())
+        ->SetIsTrackingMetadataForTesting();
+
     bookmarks::AddIfNotBookmarked(bookmark_model, GURL(kTestURL),
                                   std::u16string());
 
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc b/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc
index 9986c37..bbe0aea 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc
@@ -37,13 +37,12 @@
 #include "ui/views/widget/any_widget_observer.h"
 #include "url/gurl.h"
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_MAC)
-#include "chrome/browser/ui/browser_commands.h"
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/ui/chromeos/test_util.h"
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chrome/browser/ui/views/frame/immersive_mode_controller_chromeos.h"
-#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
+#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/ui/browser_commands.h"
 #endif
 
 namespace {
@@ -248,19 +247,16 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_MAC)
-  auto ToggleFullscreen() {
+  auto EnterImmersiveFullscreen() {
+    return [&]() {
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-    auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
-    chromeos::ImmersiveFullscreenControllerTestApi(
-        static_cast<ImmersiveModeControllerChromeos*>(
-            browser_view->immersive_mode_controller())
-            ->controller())
-        .SetupForTest();
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-    return [&]() { ui_test_utils::ToggleFullscreenModeAndWait(browser()); };
+      ChromeOSBrowserUITest::EnterImmersiveFullscreenMode(browser());
+#else  // BUILDFLAG(IS_MAC)
+      ui_test_utils::ToggleFullscreenModeAndWait(browser());
+#endif
+    };
   }
 
-#if !BUILDFLAG(IS_CHROMEOS_LACROS)
   auto IsInImmersiveFullscreen() {
     return [&]() {
       auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
@@ -268,7 +264,6 @@
              browser_view->immersive_mode_controller()->IsEnabled();
     };
   }
-#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_MAC)
 
   bool IsPartialViewEnabled() {
@@ -410,13 +405,7 @@
 IN_PROC_BROWSER_TEST_F(DownloadBubbleInteractiveUiTest,
                        ToolbarIconShownAfterImmersiveFullscreenDownload) {
   RunTestSequence(
-      Do(ToggleFullscreen()),
-#if !BUILDFLAG(IS_CHROMEOS_LACROS)
-      // This cannot be enabled yet for ChromeOS because it would be flaky, due
-      // to the delay between server and client agreeing on immersive state.
-      // TODO(crbug.com/1448281): Enable this check for ChromeOS.
-      Check(IsInImmersiveFullscreen()),
-#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
+      Do(EnterImmersiveFullscreen()), Check(IsInImmersiveFullscreen()),
       // No download toolbar icon should be present before the download.
       EnsureNotPresent(kToolbarDownloadButtonElementId),
       // Download a file to make the partial bubble show up, if enabled.
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_security_view.cc b/chrome/browser/ui/views/download/bubble/download_bubble_security_view.cc
index 7eeffc4..f9a2f8a 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_security_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_security_view.cc
@@ -316,38 +316,6 @@
   // Layout will stretch it back out into any additional space available.
   paragraphs_->SizeToFit(GetMinimumLabelWidth());
 
-  // TODO(chlily): Implement deep_scanning_link_ as a learn_more_link_.
-  if (info_->danger_type() == download::DownloadDangerType::
-                                  DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING ||
-      info_->danger_type() ==
-          download::DownloadDangerType::
-              DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING) {
-    std::u16string link_text =
-        info_->danger_type() ==
-                download::DownloadDangerType::
-                    DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING
-            ? l10n_util::GetStringUTF16(
-                  IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_BLOCKED_LEARN_MORE_LINK)
-            : l10n_util::GetStringUTF16(
-                  IDS_DOWNLOAD_BUBBLE_SUBPAGE_DEEP_SCANNING_LINK);
-    deep_scanning_link_->SetText(link_text);
-    gfx::Range link_range(0, link_text.length());
-    // Unretained is safe because `delegate_` outlives this, which owns
-    // `deep_scanning_link_`.
-    views::StyledLabel::RangeStyleInfo link_style =
-        views::StyledLabel::RangeStyleInfo::CreateForLink(
-            base::BindRepeating(&DownloadBubbleSecurityView::Delegate::
-                                    ProcessSecuritySubpageButtonPress,
-                                base::Unretained(delegate_), content_id(),
-                                DownloadCommands::LEARN_MORE_SCANNING));
-    deep_scanning_link_->AddStyleRange(link_range, link_style);
-    deep_scanning_link_->SetVisible(true);
-    deep_scanning_link_->SizeToFit(GetMinimumLabelWidth());
-    deep_scanning_link_->PreferredSizeChanged();
-  } else {
-    deep_scanning_link_->SetVisible(false);
-  }
-
   if (info_->learn_more_link()) {
     learn_more_link_->SetText(info_->learn_more_link()->label_and_link_text);
     size_t link_start_offset =
@@ -448,16 +416,6 @@
                               paragraphs_->GetLineHeight() / 2));
   }
 
-  // TODO(chlily): Implement deep_scanning_link_ as a learn_more_link_.
-  deep_scanning_link_ =
-      wrapper->AddChildView(std::make_unique<views::StyledLabel>());
-  deep_scanning_link_->SetTextContext(views::style::CONTEXT_DIALOG_BODY_TEXT);
-  deep_scanning_link_->SetDefaultTextStyle(views::style::STYLE_PRIMARY);
-  // `deep_scanning_link_` is after `paragraphs_`, and we should have the
-  // paragraph spacing between them.
-  deep_scanning_link_->SetProperty(
-      views::kMarginsKey, gfx::Insets().set_top(kAfterParagraphSpacing));
-
   learn_more_link_ =
       wrapper->AddChildView(std::make_unique<views::StyledLabel>());
   learn_more_link_->SetTextContext(views::style::CONTEXT_DIALOG_BODY_TEXT);
@@ -731,7 +689,6 @@
   secondary_styled_label_->PreferredSizeChanged();
 
   title_->SetText(std::u16string());
-  deep_scanning_link_->SetText(std::u16string());
 
   PreferredSizeChanged();
 }
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_security_view.h b/chrome/browser/ui/views/download/bubble/download_bubble_security_view.h
index 0af4592..ecfcf01 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_security_view.h
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_security_view.h
@@ -179,8 +179,6 @@
   raw_ptr<views::ImageView> secondary_icon_ = nullptr;
   raw_ptr<views::StyledLabel> secondary_styled_label_ = nullptr;
   raw_ptr<views::ImageButton> back_button_ = nullptr;
-  // TODO(chlily): Implement deep_scanning_link_ as a learn_more_link_.
-  raw_ptr<views::StyledLabel> deep_scanning_link_ = nullptr;
   raw_ptr<views::StyledLabel> learn_more_link_ = nullptr;
   raw_ptr<views::ProgressBar> progress_bar_ = nullptr;
   raw_ptr<DownloadBubblePasswordPromptView> password_prompt_ = nullptr;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index ec256189..f764d5e 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -5237,17 +5237,17 @@
   }
   auto* const service =
       UserEducationServiceFactory::GetForBrowserContext(GetProfile());
-  if (service->new_badge_registry() &&
+  if (service && service->new_badge_registry() &&
       service->new_badge_registry()->IsFeatureRegistered(feature)) {
     service->new_badge_controller()->NotifyFeatureUsedIfValid(feature);
   }
 }
 
 bool BrowserView::MaybeShowNewBadgeFor(const base::Feature& feature) {
-  auto* const controller =
-      UserEducationServiceFactory::GetForBrowserContext(GetProfile())
-          ->new_badge_controller();
-  return controller && controller->MaybeShowNewBadge(feature);
+  auto* const service =
+      UserEducationServiceFactory::GetForBrowserContext(GetProfile());
+  return service && service->new_badge_controller() &&
+         service->new_badge_controller()->MaybeShowNewBadge(feature);
 }
 
 bool BrowserView::DoCutCopyPasteForWebContents(WebContents* contents,
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos_browsertest.cc
index 89b495c0..4c32d35 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos_browsertest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_chromeos.h"
 
-#include "ash/public/cpp/test/shell_test_api.h"
 #include "base/memory/raw_ptr.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "build/build_config.h"
@@ -151,7 +150,7 @@
   // The window header should be above the web contents.
   int header_height = GetBoundsInWidget(contents_web_view).y();
 
-  ui_test_utils::ToggleFullscreenModeAndWait(browser());
+  EnterImmersiveFullscreenMode(browser());
   EXPECT_TRUE(browser_view()->GetWidget()->IsFullscreen());
   EXPECT_TRUE(controller()->IsEnabled());
   EXPECT_FALSE(controller()->IsRevealed());
@@ -182,7 +181,7 @@
 
   // Exit immersive fullscreen. The web contents should be back below the window
   // header.
-  ui_test_utils::ToggleFullscreenModeAndWait(browser());
+  ExitImmersiveFullscreenMode(browser());
   EXPECT_FALSE(browser_view()->GetWidget()->IsFullscreen());
   EXPECT_FALSE(controller()->IsEnabled());
   EXPECT_FALSE(tabstrip->GetVisible());
@@ -203,8 +202,7 @@
   // Verify that after entering tablet mode, immersive mode is enabled, and the
   // the associated window's top inset is 0 (the top of the window is not
   // visible).
-  ASSERT_NO_FATAL_FAILURE(
-      ash::ShellTestApi().SetTabletModeEnabledForTest(true));
+  EnterTabletMode();
   EXPECT_TRUE(controller()->IsEnabled());
   EXPECT_EQ(0, aura_window->GetProperty(aura::client::kTopViewInset));
 
@@ -221,14 +219,12 @@
   // mode.
   ui_test_utils::ToggleFullscreenModeAndWait(browser());
   EXPECT_TRUE(controller()->IsEnabled());
-  ASSERT_NO_FATAL_FAILURE(
-      ash::ShellTestApi().SetTabletModeEnabledForTest(false));
+  ExitTabletMode();
   EXPECT_TRUE(controller()->IsEnabled());
 
   // Verify that immersive mode remains if the browser was fullscreened when
   // entering tablet mode.
-  ASSERT_NO_FATAL_FAILURE(
-      ash::ShellTestApi().SetTabletModeEnabledForTest(true));
+  EnterTabletMode();
   EXPECT_TRUE(controller()->IsEnabled());
 
   // Verify that if the browser is not fullscreened, upon exiting tablet mode,
@@ -236,8 +232,7 @@
   // greater than 0 (the top of the window is visible).
   ui_test_utils::ToggleFullscreenModeAndWait(browser());
   EXPECT_TRUE(controller()->IsEnabled());
-  ASSERT_NO_FATAL_FAILURE(
-      ash::ShellTestApi().SetTabletModeEnabledForTest(false));
+  ExitTabletMode();
   EXPECT_FALSE(controller()->IsEnabled());
 
   EXPECT_GT(aura_window->GetProperty(aura::client::kTopViewInset), 0);
@@ -268,7 +263,7 @@
   EXPECT_TRUE(frame_test_api.size_button()->GetVisible());
 
   // Verify the size button is hidden in tablet mode.
-  ash::ShellTestApi().SetTabletModeEnabledForTest(true);
+  EnterTabletMode();
   frame_test_api.EndAnimations();
 
   EXPECT_TRUE(frame_test_api.size_button()->GetVisible());
@@ -277,7 +272,7 @@
 
   // Verify the size button is visible in clamshell mode, and that it does not
   // cover the other two buttons.
-  ash::ShellTestApi().SetTabletModeEnabledForTest(false);
+  ExitTabletMode();
   frame_test_api.EndAnimations();
 
   EXPECT_TRUE(frame_test_api.size_button()->GetVisible());
@@ -294,7 +289,7 @@
 IN_PROC_BROWSER_TEST_F(ImmersiveModeControllerChromeosWebAppBrowserTest,
                        FrameLayoutStartInTabletMode) {
   // Start in tablet mode
-  ash::ShellTestApi().SetTabletModeEnabledForTest(true);
+  EnterTabletMode();
 
   // Launch app window while in tablet mode
   LaunchAppBrowser(false);
@@ -311,7 +306,7 @@
 
   // Verify the size button is visible in clamshell mode, and that it does not
   // cover the other two buttons.
-  ash::ShellTestApi().SetTabletModeEnabledForTest(false);
+  ExitTabletMode();
   VerifyButtonsInImmersiveMode(browser_view);
 }
 
@@ -321,8 +316,14 @@
 // but still drawn. In this case, we should have a null anchor view so that the
 // bubble gets placed in the default top left corner. Regression test for
 // https://crbug.com/1087143.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+// TODO(crbug.com/329759044): Enable when bug is fixed.
+#define MAYBE_PermissionsBubbleAnchor DISABLED_PermissionsBubbleAnchor
+#else
+#define MAYBE_PermissionsBubbleAnchor PermissionsBubbleAnchor
+#endif
 IN_PROC_BROWSER_TEST_F(ImmersiveModeControllerChromeosWebAppBrowserTest,
-                       PermissionsBubbleAnchor) {
+                       MAYBE_PermissionsBubbleAnchor) {
   LaunchAppBrowser();
   auto test_api =
       std::make_unique<test::PermissionRequestManagerTestApi>(browser());
diff --git a/chrome/browser/ui/views/infobars/default_browser_infobar_interactive_uitest.cc b/chrome/browser/ui/views/infobars/default_browser_infobar_interactive_uitest.cc
index 0dac296..8e64f50c1 100644
--- a/chrome/browser/ui/views/infobars/default_browser_infobar_interactive_uitest.cc
+++ b/chrome/browser/ui/views/infobars/default_browser_infobar_interactive_uitest.cc
@@ -7,15 +7,18 @@
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_browser_main.h"
 #include "chrome/browser/chrome_browser_main_extra_parts.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/accelerator_utils.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/startup/default_browser_prompt.h"
 #include "chrome/browser/ui/startup/infobar_utils.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/infobars/confirm_infobar.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
@@ -29,6 +32,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/accelerators/accelerator.h"
 #include "ui/base/interaction/element_identifier.h"
 
 namespace {
@@ -88,6 +92,21 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DefaultBrowserInfobarWithRefreshInteractiveTest,
+                       DoesNotShowDefaultBrowserPromptOnIncognitoTab) {
+  ui::Accelerator incognito_accelerator;
+  chrome::AcceleratorProviderForBrowser(browser())->GetAcceleratorForCommandId(
+      IDC_NEW_INCOGNITO_WINDOW, &incognito_accelerator);
+
+  ShowPromptForTesting();
+  RunTestSequence(
+      WaitForShow(ConfirmInfoBar::kInfoBarElementId), FlushEvents(),
+      SendAccelerator(kBrowserViewElementId, incognito_accelerator),
+      InAnyContext(
+          WaitForShow(kBrowserViewElementId).SetTransitionOnlyOnEvent(true)),
+      InSameContext(EnsureNotPresent(ConfirmInfoBar::kInfoBarElementId)));
+}
+
+IN_PROC_BROWSER_TEST_F(DefaultBrowserInfobarWithRefreshInteractiveTest,
                        RemovesAllBrowserPromptsOnAccept) {
   ShowPromptForTesting();
   RunTestSequence(
diff --git a/chrome/browser/ui/views/location_bar/selected_keyword_view_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/selected_keyword_view_interactive_uitest.cc
index 8d807fb..1632884 100644
--- a/chrome/browser/ui/views/location_bar/selected_keyword_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/location_bar/selected_keyword_view_interactive_uitest.cc
@@ -37,16 +37,8 @@
 // extension's omnibox keyword. When the extension's omnibox keyword is
 // activated, then the selected keyword label in the omnibox should be the
 // extension's short name.
-// TODO(https://crbug.com/1407072): Flaky on Mac.
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_TestSelectedKeywordViewIsExtensionShortname \
-  DISABLED_TestSelectedKeywordViewIsExtensionShortname
-#else
-#define MAYBE_TestSelectedKeywordViewIsExtensionShortname \
-  TestSelectedKeywordViewIsExtensionShortname
-#endif
 IN_PROC_BROWSER_TEST_F(SelectedKeywordViewTest,
-                       MAYBE_TestSelectedKeywordViewIsExtensionShortname) {
+                       TestSelectedKeywordViewIsExtensionShortname) {
   const extensions::Extension* extension =
       InstallExtension(test_data_dir_.AppendASCII("omnibox"), 1);
   ASSERT_NE(extension, nullptr);
diff --git a/chrome/browser/ui/views/media_preview/camera_preview/video_stream_coordinator.cc b/chrome/browser/ui/views/media_preview/camera_preview/video_stream_coordinator.cc
index 0a7c2725..8c233bb6 100644
--- a/chrome/browser/ui/views/media_preview/camera_preview/video_stream_coordinator.cc
+++ b/chrome/browser/ui/views/media_preview/camera_preview/video_stream_coordinator.cc
@@ -10,6 +10,7 @@
 
 #include "chrome/browser/ui/views/media_preview/camera_preview/video_format_comparison.h"
 #include "chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.h"
+#include "chrome/browser/ui/views/media_preview/media_preview_metrics.h"
 #include "content/public/browser/context_factory.h"
 #include "media/capture/video_capture_types.h"
 
@@ -85,6 +86,18 @@
     // TODO(b/329312235): Collect actual Frame Per Seconds value using
     // `video_frame_handler_->GetActualParams()`.
 
+    // Retrieve the settings actually being sent by the handler. If something
+    // else has the stream open when the media preview requests it, then the
+    // requested settings are ignored and the existing settings are used
+    // instead.
+    std::optional<media::VideoCaptureParams> actual_params =
+        video_frame_handler_->GetActualParams();
+    if (actual_params) {
+      media_preview_metrics::RecordPreviewCameraPixelHeight(
+          metrics_context_,
+          actual_params->requested_format.frame_size.height());
+    }
+
     // Close frame handling and move the object to another thread to allow it
     // to finish processing frames that are in progress. If this isn't done,
     // then allocated buffers can be left dangling until the video stream is
diff --git a/chrome/browser/ui/views/media_preview/media_preview_metrics.cc b/chrome/browser/ui/views/media_preview/media_preview_metrics.cc
index 9ff4cf48..1d0fad0 100644
--- a/chrome/browser/ui/views/media_preview/media_preview_metrics.cc
+++ b/chrome/browser/ui/views/media_preview/media_preview_metrics.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
 
 namespace {
 
@@ -77,4 +78,24 @@
   base::UmaHistogramCustomCounts(metric_name, devices, 0, 5, 5);
 }
 
+void RecordPreviewCameraPixelHeight(Context context, int pixelHeight) {
+  std::string context_metric_id;
+  switch (context.ui_location) {
+    case media_preview_metrics::UiLocation::kPermissionPrompt:
+      context_metric_id = "MediaPreviews.UI.Permissions.Camera.PixelHeight";
+      break;
+    case media_preview_metrics::UiLocation::kPageInfo:
+      context_metric_id = "MediaPreviews.UI.PageInfo.Camera.PixelHeight";
+      break;
+    default:
+#if DCHECK_IS_ON()
+      NOTREACHED_NORETURN() << "Context ui_location is unknown";
+#else
+      LOG(ERROR) << "Context ui_location is unknown";
+      return;
+#endif
+  }
+  base::UmaHistogramCustomCounts(context_metric_id, pixelHeight, 0, 1080, 8);
+}
+
 }  // namespace media_preview_metrics
diff --git a/chrome/browser/ui/views/media_preview/media_preview_metrics.h b/chrome/browser/ui/views/media_preview/media_preview_metrics.h
index f562768e..d1eded82 100644
--- a/chrome/browser/ui/views/media_preview/media_preview_metrics.h
+++ b/chrome/browser/ui/views/media_preview/media_preview_metrics.h
@@ -21,6 +21,7 @@
 void RecordPageInfoCameraNumInUseDevices(int devices);
 void RecordPageInfoMicNumInUseDevices(int devices);
 void RecordDeviceSelectionTotalDevices(Context context, int devices);
+void RecordPreviewCameraPixelHeight(Context context, int pixelHeight);
 
 }  // namespace media_preview_metrics
 
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_interactive_uitest.cc
similarity index 100%
rename from chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc
rename to chrome/browser/ui/views/media_router/media_router_dialog_controller_views_interactive_uitest.cc
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
index d8c202b..9c0ed9cd 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
@@ -703,7 +703,14 @@
 #endif
 }
 
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest, SignedInChangeIcon) {
+// TODO(crbug/330202396): Flaky on chromium/ci/win-asan. Disable for Windows.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_SignedInChangeIcon DISABLED_SignedInChangeIcon
+#else
+#define MAYBE_SignedInChangeIcon SignedInChangeIcon
+#endif
+IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
+                       MAYBE_SignedInChangeIcon) {
   ASSERT_FALSE(IsSignedInImageUsed());
 
   AccountInfo account_info = SigninAndWait(u"test@gmail.com");
@@ -1121,8 +1128,16 @@
 }
 
 // Sync Pause/Error has priority over WorkBadge.
+// TODO(crbug/330202396): Flaky on chromium/ci/win-asan. Disable for Windows.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_WorkBadgeTransientModeAndSyncPause \
+  DISABLED_WorkBadgeTransientModeAndSyncPause
+#else
+#define MAYBE_WorkBadgeTransientModeAndSyncPause \
+  WorkBadgeTransientModeAndSyncPause
+#endif
 IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonEnterpriseBadgingBrowserTest,
-                       WorkBadgeTransientModeAndSyncPause) {
+                       MAYBE_WorkBadgeTransientModeAndSyncPause) {
   AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
   ASSERT_TRUE(avatar_button->GetText().empty());
 
diff --git a/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc
index fc67f4d..830fa69b 100644
--- a/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc
@@ -137,14 +137,3 @@
       /*step_switch_finished_callback=*/StepSwitchFinishedCallback(),
       /*pop_step_callback=*/base::OnceClosure());
 }
-
-void ProfileManagementFlowController::CreateSignedOutFlowWebContents(
-    Profile* profile) {
-  signed_out_flow_web_contents_ =
-      content::WebContents::Create(content::WebContents::CreateParams(profile));
-}
-
-content::WebContents*
-ProfileManagementFlowController::GetSignedOutFlowWebContents() const {
-  return signed_out_flow_web_contents_.get();
-}
diff --git a/chrome/browser/ui/views/profiles/profile_management_flow_controller.h b/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
index 5d48626..afaa989 100644
--- a/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
@@ -11,7 +11,6 @@
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/views/profiles/profile_management_types.h"
 #include "chrome/browser/ui/views/profiles/profile_picker_web_contents_host.h"
-#include "content/public/browser/web_contents.h"
 
 class Profile;
 class ProfileManagementStepController;
@@ -142,13 +141,6 @@
 
   ProfilePickerWebContentsHost* host() { return host_; }
 
-  // Creates the web contents associated with `profile` and stores them in
-  // `signed_out_flow_web_contents_`.
-  void CreateSignedOutFlowWebContents(Profile* profile);
-
-  // Returns a pointer to `signed_out_flow_web_contents_`.
-  content::WebContents* GetSignedOutFlowWebContents() const;
-
  private:
   // Called after a browser is open. Clears the host and then runs the callback.
   void CloseHostAndRunCallback(
@@ -157,10 +149,6 @@
 
   Step current_step_ = Step::kUnknown;
 
-  // The signed out flow web contents are used in some steps inside
-  // `initialized_steps_`. They have to be destroyed after `initialized_steps_`.
-  std::unique_ptr<content::WebContents> signed_out_flow_web_contents_;
-
   raw_ptr<ProfilePickerWebContentsHost> host_;
   ClearHostClosure clear_host_callback_;
 
diff --git a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
index c735e8d..a8b93f0 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
@@ -10,7 +10,6 @@
 #include "base/functional/callback_forward.h"
 #include "base/functional/callback_helpers.h"
 #include "base/functional/overloaded.h"
-#include "base/not_fatal_until.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "chrome/browser/browser_process.h"
@@ -537,52 +536,32 @@
   return signed_in_flow;
 }
 
-void ProfilePickerFlowController::SwitchToSignedOutPostIdentityFlow(
-    Profile* profile,
-    PostHostClearedCallback post_host_cleared_callback,
-    StepSwitchFinishedCallback step_switch_finished_callback) {
-  CHECK(profile);
-  created_profile_ = profile->GetWeakPtr();
-  CreateSignedOutFlowWebContents(created_profile_.get());
-
-  HandleIdentityStepsCompleted(
-      created_profile_.get(), std::move(post_host_cleared_callback),
-      /*is_continue_callback=*/false, std::move(step_switch_finished_callback));
-}
-
 base::queue<ProfileManagementFlowController::Step>
 ProfilePickerFlowController::RegisterPostIdentitySteps(
     PostHostClearedCallback post_host_cleared_callback) {
   CHECK(created_profile_);
   base::queue<ProfileManagementFlowController::Step> post_identity_steps;
 
-  content::WebContents* web_contents = nullptr;
   if (weak_signed_in_flow_controller_) {
+    auto search_engine_choice_step_completed = base::BindOnce(
+        &ProfilePickerFlowController::AdvanceToNextPostIdentityStep,
+        base::Unretained(this));
     // TODO(crbug.com/1501785): Find a way to get the web contents without
     // relying on the weak ptr.
-    web_contents = weak_signed_in_flow_controller_->contents();
-    CHECK(web_contents, base::NotFatalUntil::M127);
-  } else {
-    // TODO(crbug.com/1501785): Find another way to fetch the web contents.
-    web_contents = GetSignedOutFlowWebContents();
-    CHECK(web_contents, base::NotFatalUntil::M127);
+    SearchEngineChoiceDialogService* search_engine_choice_dialog_service =
+        SearchEngineChoiceDialogServiceFactory::GetForProfile(
+            created_profile_.get());
+    RegisterStep(
+        Step::kSearchEngineChoice,
+        ProfileManagementStepController::CreateForSearchEngineChoice(
+            host(), search_engine_choice_dialog_service,
+            weak_signed_in_flow_controller_->contents(),
+            SearchEngineChoiceDialogService::EntryPoint::kProfileCreation,
+            std::move(search_engine_choice_step_completed)));
+    post_identity_steps.emplace(
+        ProfileManagementFlowController::Step::kSearchEngineChoice);
   }
 
-  auto search_engine_choice_step_completed = base::BindOnce(
-      &ProfilePickerFlowController::AdvanceToNextPostIdentityStep,
-      base::Unretained(this));
-  SearchEngineChoiceDialogService* search_engine_choice_dialog_service =
-      SearchEngineChoiceDialogServiceFactory::GetForProfile(
-          created_profile_.get());
-  RegisterStep(
-      Step::kSearchEngineChoice,
-      ProfileManagementStepController::CreateForSearchEngineChoice(
-          host(), search_engine_choice_dialog_service, web_contents,
-          SearchEngineChoiceDialogService::EntryPoint::kProfileCreation,
-          std::move(search_engine_choice_step_completed)));
-  post_identity_steps.emplace(
-      ProfileManagementFlowController::Step::kSearchEngineChoice);
-
   RegisterStep(
       Step::kFinishFlow,
       ProfileManagementStepController::CreateForFinishFlowAndRunInBrowser(
diff --git a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h
index 10b9b67..4a3742b 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h
@@ -50,13 +50,6 @@
 
   base::FilePath GetSwitchProfilePathOrEmpty() const;
 
-  // Switch to the flow that is shown when the user decides to create a profile
-  // without signing in.
-  void SwitchToSignedOutPostIdentityFlow(
-      Profile* profile,
-      PostHostClearedCallback post_host_cleared_callback,
-      StepSwitchFinishedCallback step_switch_finished_callback);
-
  protected:
   // ProfileManagementFlowControllerImpl
   base::queue<ProfileManagementFlowController::Step> RegisterPostIdentitySteps(
diff --git a/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc b/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
index 3fd1f175..d4f26188 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
@@ -6,22 +6,16 @@
 
 #include "base/check.h"
 #include "base/run_loop.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/metrics/user_action_tester.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
-#include "chrome/browser/profiles/profile_metrics.h"
-#include "chrome/browser/search_engine_choice/search_engine_choice_dialog_service.h"
-#include "chrome/browser/search_engine_choice/search_engine_choice_dialog_service_factory.h"
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/profiles/profile_picker.h"
 #include "chrome/browser/ui/views/profiles/profile_picker_interactive_uitest_base.h"
 #include "chrome/browser/ui/views/profiles/profile_picker_test_base.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
-#include "components/search_engines/search_engines_switches.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
@@ -42,13 +36,9 @@
 
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kProfilePickerViewId);
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPickerWebContentsId);
-DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kButtonEnabled);
-DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kButtonDisabled);
 
 const WebContentsInteractionTestUtil::DeepQuery kSignInButton = {
     "profile-picker-app", "profile-type-choice", "#signInButton"};
-const WebContentsInteractionTestUtil::DeepQuery kContinueWithoutAccountButton =
-    {"profile-picker-app", "profile-type-choice", "#notNowButton"};
 const WebContentsInteractionTestUtil::DeepQuery kAddProfileButton = {
     "profile-picker-app", "profile-picker-main-view", "#addProfile"};
 
@@ -75,22 +65,6 @@
       this};
 };
 
-struct TestParam {
-  std::string test_suffix;
-  bool with_search_engine_choice_step = false;
-};
-
-std::string ParamToTestSuffix(const ::testing::TestParamInfo<TestParam>& info) {
-  return info.param.test_suffix;
-}
-
-// Permutations of supported parameters.
-const TestParam kTestParams[] = {
-    {.test_suffix = "Default"},
-    {.test_suffix = "WithSearchEngineChoiceStep",
-     .with_search_engine_choice_step = true},
-};
-
 }  // namespace
 
 struct NavState {
@@ -178,135 +152,6 @@
   }
 };
 
-class ProfilePickerParametrizedInteractiveUiTest
-    : public ProfilePickerInteractiveUiTest,
-      public testing::WithParamInterface<TestParam> {
- public:
-  ProfilePickerParametrizedInteractiveUiTest() {
-    std::vector<base::test::FeatureRef> enabled_features;
-    std::vector<base::test::FeatureRef> disabled_features;
-
-    if (WithSearchEngineChoiceStep()) {
-      scoped_chrome_build_override_ = std::make_unique<base::AutoReset<bool>>(
-          SearchEngineChoiceDialogServiceFactory::
-              ScopedChromeBuildOverrideForTesting(
-                  /*force_chrome_build=*/true));
-      enabled_features.push_back(switches::kSearchEngineChoiceTrigger);
-    } else {
-      disabled_features.push_back(switches::kSearchEngineChoiceTrigger);
-    }
-
-    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
-  }
-
-  void SetUpOnMainThread() override {
-    ProfilePickerInteractiveUiTest::SetUpOnMainThread();
-
-    if (WithSearchEngineChoiceStep()) {
-      SearchEngineChoiceDialogService::SetDialogDisabledForTests(
-          /*dialog_disabled=*/false);
-    }
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    ProfilePickerInteractiveUiTest::SetUpCommandLine(command_line);
-
-    if (WithSearchEngineChoiceStep()) {
-      // Change the country to belgium because the search engine choice screen
-      // is only displayed for EEA countries.
-      command_line->AppendSwitchASCII(switches::kSearchEngineChoiceCountry,
-                                      "BE");
-    }
-  }
-
-  const base::HistogramTester& HistogramTester() const {
-    return histogram_tester_;
-  }
-
-  const base::UserActionTester& UserActionTester() const {
-    return user_action_tester_;
-  }
-
-  bool WithSearchEngineChoiceStep() const {
-    return GetParam().with_search_engine_choice_step;
-  }
-
-  auto WaitForButtonEnabled(const ui::ElementIdentifier web_contents_id,
-                            const DeepQuery& button_query) {
-    StateChange button_enabled;
-    button_enabled.event = kButtonEnabled;
-    button_enabled.where = button_query;
-    button_enabled.type = StateChange::Type::kExistsAndConditionTrue;
-    button_enabled.test_function = "(btn) => !btn.disabled";
-    return WaitForStateChange(web_contents_id, button_enabled);
-  }
-
-  auto WaitForButtonDisabled(const ui::ElementIdentifier web_contents_id,
-                             const DeepQuery& button_query) {
-    StateChange button_disabled;
-    button_disabled.event = kButtonDisabled;
-    button_disabled.where = button_query;
-    button_disabled.type = StateChange::Type::kExistsAndConditionTrue;
-    button_disabled.test_function = "(btn) => btn.disabled";
-    return WaitForStateChange(web_contents_id, button_disabled);
-  }
-
-  auto PressJsButton(const ui::ElementIdentifier web_contents_id,
-                     const DeepQuery& button_query) {
-    // This can close/navigate the current page, so don't wait for success.
-    return ExecuteJsAt(web_contents_id, button_query, "(btn) => btn.click()",
-                       ExecuteJsMode::kFireAndForget);
-  }
-
-  auto CompleteSearchEngineChoiceStep() {
-    const WebContentsInteractionTestUtil::DeepQuery
-        kSearchEngineChoiceActionButton{"search-engine-choice-app",
-                                        "#actionButton"};
-    const DeepQuery first_search_engine = {"search-engine-choice-app",
-                                           "cr-radio-button"};
-    const DeepQuery searchEngineChoiceList{"search-engine-choice-app",
-                                           "#choiceList"};
-    return Steps(
-        WaitForWebContentsNavigation(
-            kPickerWebContentsId, GURL(chrome::kChromeUISearchEngineChoiceURL)),
-
-        Do([&] {
-          HistogramTester().ExpectBucketCount(
-              search_engines::kSearchEngineChoiceScreenEventsHistogram,
-              search_engines::SearchEngineChoiceScreenEvents::
-                  kProfileCreationChoiceScreenWasDisplayed,
-              1);
-          EXPECT_EQ(UserActionTester().GetActionCount(
-                        "SearchEngineChoiceScreenShown"),
-                    1);
-        }),
-
-        // Click on "More" to scroll to the bottom of the search engine list.
-        PressJsButton(kPickerWebContentsId, kSearchEngineChoiceActionButton),
-
-        // The button should become disabled because we didn't make a choice.
-        WaitForButtonDisabled(kPickerWebContentsId,
-                              kSearchEngineChoiceActionButton),
-
-        PressJsButton(kPickerWebContentsId, kSearchEngineChoiceActionButton),
-        PressJsButton(kPickerWebContentsId, first_search_engine),
-        WaitForButtonEnabled(kPickerWebContentsId,
-                             kSearchEngineChoiceActionButton),
-        PressJsButton(kPickerWebContentsId, kSearchEngineChoiceActionButton));
-  }
-
- private:
-  base::UserActionTester user_action_tester_;
-  base::HistogramTester histogram_tester_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<base::AutoReset<bool>> scoped_chrome_build_override_;
-};
-
-INSTANTIATE_TEST_SUITE_P(,
-                         ProfilePickerParametrizedInteractiveUiTest,
-                         testing::ValuesIn(kTestParams),
-                         &ParamToTestSuffix);
-
 // Checks that the main picker view can be closed with keyboard shortcut.
 IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest, CloseWithKeyboard) {
   // Open a new picker.
@@ -521,49 +366,3 @@
 
   );
 }
-
-IN_PROC_BROWSER_TEST_P(ProfilePickerParametrizedInteractiveUiTest,
-                       ContinueWithoutAccount) {
-  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuManageProfiles,
-                     GURL("chrome://profile-picker"));
-
-  RunTestSequenceInContext(
-      views::ElementTrackerViews::GetContextForView(view()),
-
-      WaitForShow(kProfilePickerViewId),
-      InstrumentNonTabWebView(kPickerWebContentsId, web_view()),
-      WaitForWebContentsReady(kPickerWebContentsId,
-                              GURL("chrome://profile-picker")),
-      CheckResult(GetNavState(), Eq(NavState{.entry_count = 1,
-                                             .last_committed_entry_index = 0})),
-
-      // Advance to the profile type choice screen.
-      EnsurePresent(kPickerWebContentsId, kAddProfileButton),
-      PressJsButton(kPickerWebContentsId, kAddProfileButton),
-      WaitForStateChange(
-          kPickerWebContentsId,
-          UrlEntryMatches(GURL("chrome://profile-picker/new-profile"))),
-      CheckResult(GetNavState(), Eq(NavState{.entry_count = 2,
-                                             .last_committed_entry_index = 1})),
-
-      // Advance to the post signed out flow
-      WaitForStateChange(kPickerWebContentsId,
-                         Exists(kContinueWithoutAccountButton)),
-      PressJsButton(kPickerWebContentsId, kContinueWithoutAccountButton),
-
-      If([&] { return WithSearchEngineChoiceStep(); },
-         CompleteSearchEngineChoiceStep()));
-
-  WaitForPickerClosed();
-
-  HistogramTester().ExpectUniqueSample(
-      "Profile.AddNewUser", ProfileMetrics::ADD_NEW_PROFILE_PICKER_LOCAL, 1);
-
-  if (WithSearchEngineChoiceStep()) {
-    HistogramTester().ExpectBucketCount(
-        search_engines::kSearchEngineChoiceScreenEventsHistogram,
-        search_engines::SearchEngineChoiceScreenEvents::
-            kProfileCreationDefaultWasSet,
-        1);
-  }
-}
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index 90cd6b6..efb9705 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -14,9 +14,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/not_fatal_until.h"
 #include "base/notreached.h"
-#include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
@@ -27,19 +25,15 @@
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/signin_features.h"
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/signin/signin_util.h"
-#include "chrome/browser/themes/theme_service.h"
-#include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/views/accelerator_table.h"
 #include "chrome/browser/ui/views/profiles/profile_management_flow_controller.h"
 #include "chrome/browser/ui/views/profiles/profile_management_flow_controller_impl.h"
 #include "chrome/browser/ui/views/profiles/profile_picker_flow_controller.h"
-#include "chrome/browser/ui/webui/signin/profile_picker_handler.h"
 #include "chrome/browser/ui/webui/signin/profile_picker_ui.h"
 #include "chrome/browser/ui/webui/signin/signin_url_utils.h"
 #include "chrome/common/pref_names.h"
@@ -58,7 +52,6 @@
 #include "content/public/common/url_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/view.h"
@@ -217,18 +210,6 @@
 }
 #endif
 
-// static
-void ProfilePicker::SwitchToSignedOutPostIdentityFlow(
-    std::optional<SkColor> profile_color,
-    base::TimeTicks profile_picked_time_on_startup,
-    base::OnceCallback<void(bool)> switch_finished_callback) {
-  if (g_profile_picker_view) {
-    g_profile_picker_view->SwitchToSignedOutPostIdentityFlow(
-        profile_color, profile_picked_time_on_startup,
-        std::move(switch_finished_callback));
-  }
-}
-
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 // static
 void ProfilePicker::SwitchToSignedInFlow(std::optional<SkColor> profile_color,
@@ -528,85 +509,6 @@
   return this;
 }
 
-void ProfilePickerView::SwitchToSignedOutPostIdentityFlow(
-    std::optional<SkColor> profile_color,
-    base::TimeTicks profile_picked_time_on_startup,
-    base::OnceCallback<void(bool)> switch_finished_callback) {
-  size_t icon_index = profiles::GetPlaceholderAvatarIndex();
-
-  ProfileManager::CreateMultiProfileAsync(
-      g_browser_process->profile_manager()
-          ->GetProfileAttributesStorage()
-          .ChooseNameForNewProfile(icon_index),
-      icon_index, /*is_hidden=*/true,
-      base::BindOnce(&ProfilePickerView::OnLocalProfileInitialized,
-                     weak_ptr_factory_.GetWeakPtr(), profile_color,
-                     profile_picked_time_on_startup,
-                     std::move(switch_finished_callback)));
-}
-
-void ProfilePickerView::OnLocalProfileInitialized(
-    std::optional<SkColor> profile_color,
-    base::TimeTicks profile_picked_time_on_startup,
-    base::OnceCallback<void(bool)> switch_finished_callback,
-    Profile* profile) {
-  if (!profile) {
-    NOTREACHED() << "Local fail in creating new profile";
-    std::move(switch_finished_callback).Run(false);
-    return;
-  }
-  CHECK(!signin_util::IsForceSigninEnabled(), base::NotFatalUntil::M127);
-
-  // Apply a new color to the profile or use the default theme.
-  // TODO(b/328587059): Share the theme color logic with the same code in
-  // `profile_picker_flow_controller.cc`.
-  auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
-  if (profile_color.has_value()) {
-    if (features::IsChromeWebuiRefresh2023()) {
-      theme_service->SetUserColorAndBrowserColorVariant(
-          *profile_color, ui::mojom::BrowserColorVariant::kTonalSpot);
-    } else {
-      theme_service->BuildAutogeneratedThemeFromColor(*profile_color);
-    }
-  } else {
-    theme_service->UseDefaultTheme();
-  }
-
-  // TODO(https://crbug.com/1282157): Add shortcut creation.
-  // Skip the FRE for this profile as sign-in was offered as part of the flow.
-  profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, true);
-  GetProfilePickerFlowController()->SwitchToSignedOutPostIdentityFlow(
-      profile,
-      PostHostClearedCallback(base::BindOnce(
-          &ProfilePickerView::ShowLocalProfileCustomization,
-          weak_ptr_factory_.GetWeakPtr(), profile_picked_time_on_startup)),
-      std::move(switch_finished_callback));
-}
-
-void ProfilePickerView::ShowLocalProfileCustomization(
-    base::TimeTicks profile_picked_time_on_startup,
-    Browser* browser) {
-  if (!browser) {
-    // TODO(crbug.com/1374315): Make sure we do something or log an error if
-    // opening a browser window was not possible.
-    return;
-  }
-
-  DCHECK(browser->window());
-  Profile* profile = browser->profile();
-
-  TRACE_EVENT1("browser", "ProfilePickerView::ShowLocalProfileCustomization",
-               "profile_path", profile->GetPath().AsUTF8Unsafe());
-
-  if (!profile_picked_time_on_startup.is_null()) {
-    ProfilePickerHandler::BeginFirstWebContentsProfiling(
-        browser, profile_picked_time_on_startup);
-  }
-
-  browser->signin_view_controller()->ShowModalProfileCustomizationDialog(
-      /*is_local_profile_creation=*/true);
-}
-
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 void ProfilePickerView::SetNativeToolbarVisible(bool visible) {
   if (!visible) {
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.h b/chrome/browser/ui/views/profiles/profile_picker_view.h
index 32c69f7..f39459c 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.h
@@ -32,7 +32,6 @@
 class ScopedProfileKeepAlive;
 class ProfileManagementFlowController;
 class ProfilePickerFlowController;
-class Browser;
 
 namespace content {
 struct ContextMenuParams;
@@ -179,31 +178,6 @@
   // first time `ShowScreen()`.
   void FinishInit();
 
-  // Switch to the flow that comes when the user decides to create a profile
-  // without signing in.
-  // `profile_color` is the profile's color. It is undefined for the default
-  // theme.
-  // `profile_picked_time_on_startup` is the time when the user picked a
-  // profile to open, to measure browser startup performance. It is only set
-  // when the picker is shown on startup.
-  void SwitchToSignedOutPostIdentityFlow(
-      std::optional<SkColor> profile_color,
-      base::TimeTicks profile_picked_time_on_startup,
-      base::OnceCallback<void(bool)> switch_finished_callback);
-
-  // Callback used when the profile is created in the signed out flow.
-  void OnLocalProfileInitialized(
-      std::optional<SkColor> profile_color,
-      base::TimeTicks profile_picked_time_on_startup,
-      base::OnceCallback<void(bool)> switch_finished_callback,
-      Profile* profile);
-
-  // Callback used when the browser is launched after finishing the signed out
-  // flow.
-  void ShowLocalProfileCustomization(
-      base::TimeTicks profile_picked_time_on_startup,
-      Browser* browser);
-
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   // Switches the layout to the sign-in screen (and creates a new profile or
   // load an existing one based on the `profile_info` content).
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
index 255b2fd9..29bcfde 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -611,7 +611,8 @@
   void CreateLocalProfile() {
     base::Value::List args;
     args.Append(base::Value());
-    profile_picker_handler()->HandleContinueWithoutAccount(args);
+    profile_picker_handler()->HandleCreateProfileAndOpenCustomizationDialog(
+        args);
   }
 
   // Simulates a click on "Done" on the Profile Customization to confirm the
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
index 23e8291..74c3b7c6 100644
--- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
+++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
@@ -332,6 +332,10 @@
     return SidePanelUtil::GetSidePanelCoordinatorForBrowser(browser());
   }
 
+  SidePanelCoordinator* side_panel_coordinator(Browser* browser) {
+    return SidePanelUtil::GetSidePanelCoordinatorForBrowser(browser);
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
@@ -1377,18 +1381,22 @@
 
  protected:
   // Loads up a stub side panel extension.
-  const Extension* LoadSidePanelExtension() {
+  const Extension* LoadSidePanelExtension(bool allow_in_incognito = false,
+                                          bool split_mode = false) {
     TestExtensionDir test_dir;
     static constexpr char kManifest[] =
         R"({
              "name": "Side Panel Extension",
              "manifest_version": 3,
              "version": "0.1",
-             "permissions": ["sidePanel"]
+             "permissions": ["sidePanel"],
+             "incognito" : "%s"
            })";
-    test_dir.WriteManifest(kManifest);
+    test_dir.WriteManifest(
+        base::StringPrintf(kManifest, split_mode ? "split" : "spanning"));
     test_dir.WriteFile(FILE_PATH_LITERAL("panel.html"), "<html>hello</html>");
-    const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+    const Extension* extension = LoadExtension(
+        test_dir.UnpackedPath(), {.allow_in_incognito = allow_in_incognito});
     test_dirs_.push_back(std::move(test_dir));
     return extension;
   }
@@ -1409,10 +1417,20 @@
   }
 
   void RunOpenPanelForTab(const Extension& extension, int tab_id) {
-    RunOpenPanel(extension, tab_id, /*window_id=*/std::nullopt);
+    RunOpenPanel(extension, tab_id, /*window_id=*/std::nullopt, profile());
   }
   void RunOpenPanelForWindow(const Extension& extension, int window_id) {
-    RunOpenPanel(extension, /*tab_id=*/std::nullopt, window_id);
+    RunOpenPanel(extension, /*tab_id=*/std::nullopt, window_id, profile());
+  }
+  void RunOpenPanelForTabAndProfile(const Extension& extension,
+                                    int tab_id,
+                                    Profile* profile) {
+    RunOpenPanel(extension, tab_id, /*window_id=*/std::nullopt, profile);
+  }
+  void RunOpenPanelForWindowAndProfile(const Extension& extension,
+                                       int window_id,
+                                       Profile* profile) {
+    RunOpenPanel(extension, /*tab_id=*/std::nullopt, window_id, profile);
   }
 
   int GetCurrentWindowId() { return ExtensionTabUtil::GetWindowId(browser()); }
@@ -1420,7 +1438,8 @@
  private:
   void RunOpenPanel(const Extension& extension,
                     std::optional<int> tab_id,
-                    std::optional<int> window_id) {
+                    std::optional<int> window_id,
+                    Profile* profile) {
     auto function = base::MakeRefCounted<SidePanelOpenFunction>();
     function->set_extension(&extension);
 
@@ -1435,8 +1454,7 @@
     base::JSONWriter::Write(base::Value::List().Append(std::move(options)),
                             &args_str);
     function->set_user_gesture(true);
-    EXPECT_TRUE(
-        api_test_utils::RunFunction(function.get(), args_str, profile()))
+    EXPECT_TRUE(api_test_utils::RunFunction(function.get(), args_str, profile))
         << function->GetError();
   }
 
@@ -1461,6 +1479,42 @@
 }
 
 // Tests that calling `sidePanel.open()` for an extension with a global panel
+// registered opens the panel on the specified tab when using an incognito
+// window. Regression test for https://crbug.com/329211590.
+IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
+                       OpenSidePanel_OpenGlobalPanelOnActiveTab_Incognito) {
+  const Extension* extension =
+      LoadSidePanelExtension(/*allow_in_incognito=*/true, /*split_mode=*/true);
+  ASSERT_TRUE(extension);
+  // Register a global side panel.
+  RunSetOptions(*extension, /*tab_id=*/std::nullopt, "panel.html",
+                /*enabled=*/true);
+
+  // For clarity sake, use a named reference to the non-incognito browser.
+  Browser* non_incognito_browser = browser();
+
+  // Open a tab in an incognito browser window to use.
+  Browser* incognito_browser =
+      OpenURLOffTheRecord(browser()->profile(), GURL("about:blank"));
+  ASSERT_TRUE(incognito_browser);
+  int incognito_tab_id = ExtensionTabUtil::GetTabId(
+      incognito_browser->tab_strip_model()->GetActiveWebContents());
+
+  EXPECT_FALSE(side_panel_coordinator(incognito_browser)->IsSidePanelShowing());
+  EXPECT_FALSE(
+      side_panel_coordinator(non_incognito_browser)->IsSidePanelShowing());
+
+  // Run `sidePanel.open()` for the incognito profile. The panel should only
+  // open in the incognito browser and not the non-incognito browser.
+  RunOpenPanelForTabAndProfile(*extension, incognito_tab_id,
+                               incognito_browser->profile());
+  EXPECT_TRUE(side_panel_coordinator(incognito_browser)
+                  ->IsSidePanelEntryShowing(GetKey(extension->id())));
+  EXPECT_FALSE(side_panel_coordinator(non_incognito_browser)
+                   ->IsSidePanelEntryShowing(GetKey(extension->id())));
+}
+
+// Tests that calling `sidePanel.open()` for an extension with a global panel
 // registered opens the panel on all tabs (since the registration is global,
 // rather than contextual).
 IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
@@ -1753,6 +1807,41 @@
       GetKey(extension->id())));
 }
 
+// Tests calling `sidePanel.open()` with a given window ID for an incognito
+// window will open the side panel in that window when there is no active side
+// panel.
+IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
+                       OpenSidePanel_WindowId_OpenWithNoActivePanel_Incognito) {
+  const Extension* extension =
+      LoadSidePanelExtension(/*allow_in_incognito=*/true, /*split_mode=*/true);
+  ASSERT_TRUE(extension);
+  // Register a global side panel.
+  RunSetOptions(*extension, /*tab_id=*/std::nullopt, "panel.html",
+                /*enabled=*/true);
+
+  // For clarity sake, use a named reference to the non-incognito browser.
+  Browser* non_incognito_browser = browser();
+
+  // Open an incognito browser window to use and get the window id.
+  Browser* incognito_browser =
+      OpenURLOffTheRecord(browser()->profile(), GURL("about:blank"));
+  ASSERT_TRUE(incognito_browser);
+  int incognito_window_id = ExtensionTabUtil::GetWindowId(incognito_browser);
+
+  EXPECT_FALSE(side_panel_coordinator(incognito_browser)->IsSidePanelShowing());
+  EXPECT_FALSE(
+      side_panel_coordinator(non_incognito_browser)->IsSidePanelShowing());
+
+  // Run `sidePanel.open()`. The panel should open in the active tab of the
+  // incognito browser.
+  RunOpenPanelForWindowAndProfile(*extension, incognito_window_id,
+                                  incognito_browser->profile());
+  EXPECT_TRUE(side_panel_coordinator(incognito_browser)
+                  ->IsSidePanelEntryShowing(GetKey(extension->id())));
+  EXPECT_FALSE(side_panel_coordinator(non_incognito_browser)
+                   ->IsSidePanelEntryShowing(GetKey(extension->id())));
+}
+
 // Tests calling `sidePanel.open()` with a given window ID will override an
 // active global side panel in that window.
 IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
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 ece3c5b..ab51f66c 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
@@ -97,6 +97,7 @@
   }
 
   extension_loader_ = EmbeddedA11yExtensionLoader::GetInstance();
+  extension_loader_->Init();
 }
 
 void ReadAnythingCoordinator::InitModelWithUserPrefs() {
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 48f7638..9cd16a0 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -48,8 +48,10 @@
 #include "components/user_education/common/feature_promo_specification.h"
 #include "components/user_education/common/help_bubble_factory_registry.h"
 #include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/common/new_badge_specification.h"
 #include "components/user_education/common/tutorial_description.h"
 #include "components/user_education/common/tutorial_registry.h"
+#include "components/user_education/common/user_education_features.h"
 #include "components/user_education/common/user_education_metadata.h"
 #include "components/user_education/views/help_bubble_delegate.h"
 #include "components/user_education/views/help_bubble_factory_views.h"
@@ -1168,7 +1170,20 @@
 }
 
 void MaybeRegisterNewBadges(user_education::NewBadgeRegistry& registry) {
-  // TODO(dfried): Register "New" Badges here.
+  if (registry.IsFeatureRegistered(
+          user_education::features::kNewBadgeTestFeature)) {
+    return;
+  }
+
+  registry.RegisterFeature(user_education::NewBadgeSpecification(
+      user_education::features::kNewBadgeTestFeature,
+      user_education::Metadata(124, "Frizzle Team",
+                               "Used to test \"New\" Badge logic.")));
+
+  registry.RegisterFeature(user_education::NewBadgeSpecification(
+      compose::features::kEnableCompose, user_education::Metadata()));
+  registry.RegisterFeature(user_education::NewBadgeSpecification(
+      compose::features::kEnableComposeNudge, user_education::Metadata()));
 }
 
 std::unique_ptr<BrowserFeaturePromoController> CreateUserEducationResources(
@@ -1192,7 +1207,10 @@
       user_education_service->feature_promo_registry());
   MaybeRegisterChromeTutorials(user_education_service->tutorial_registry());
   CHECK(user_education_service->new_badge_registry());
+
   MaybeRegisterNewBadges(*user_education_service->new_badge_registry());
+  user_education_service->new_badge_controller()->InitData();
+
   return std::make_unique<BrowserFeaturePromoController>(
       browser_view,
       feature_engagement::TrackerFactory::GetForBrowserContext(profile),
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service_browsertest.cc b/chrome/browser/ui/views/user_education/browser_user_education_service_browsertest.cc
index 91f0f15..984549f0 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service_browsertest.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service_browsertest.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 "chrome/browser/ui/views/user_education/browser_user_education_service.h"
+
 #include <cstdint>
 #include <map>
 #include <optional>
@@ -568,3 +570,74 @@
   EXPECT_EQ(feature_engagement::SnoozeParams(), config.snooze_params);
   EXPECT_TRUE(config.groups.empty());
 }
+
+class BrowserUserEducationServiceNewBadgeBrowserTest
+    : public InProcessBrowserTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  BrowserUserEducationServiceNewBadgeBrowserTest() {
+    if (GetParam()) {
+      feature_list_.InitAndEnableFeature(
+          user_education::features::kNewBadgeTestFeature);
+    }
+  }
+
+  ~BrowserUserEducationServiceNewBadgeBrowserTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         BrowserUserEducationServiceNewBadgeBrowserTest,
+                         testing::Bool());
+
+IN_PROC_BROWSER_TEST_P(BrowserUserEducationServiceNewBadgeBrowserTest,
+                       ShowsNewBadge) {
+  // Ensure both ways to check the badge work as expected.
+  EXPECT_EQ(GetParam(), browser()->window()->MaybeShowNewBadgeFor(
+                            user_education::features::kNewBadgeTestFeature));
+  EXPECT_EQ(GetParam(), UserEducationService::MaybeShowNewBadge(
+                            browser()->profile(),
+                            user_education::features::kNewBadgeTestFeature));
+
+  // Ensure that the feature can be marked as used.
+  for (int i = 0; i < user_education::features::GetNewBadgeFeatureUsedCount();
+       i += 2) {
+    browser()->window()->NotifyPromoFeatureUsed(
+        user_education::features::kNewBadgeTestFeature);
+    UserEducationService::MaybeNotifyPromoFeatureUsed(
+        browser()->profile(), user_education::features::kNewBadgeTestFeature);
+  }
+
+  // The badge should now be blocked.
+  EXPECT_FALSE(browser()->window()->MaybeShowNewBadgeFor(
+      user_education::features::kNewBadgeTestFeature));
+  EXPECT_FALSE(UserEducationService::MaybeShowNewBadge(
+      browser()->profile(), user_education::features::kNewBadgeTestFeature));
+}
+
+IN_PROC_BROWSER_TEST_P(BrowserUserEducationServiceNewBadgeBrowserTest,
+                       IncognitoDoesNotShowBadge) {
+  // Both ways to check the badge should return false for an OTR profile.
+  auto* const incog = CreateIncognitoBrowser();
+  EXPECT_FALSE(incog->window()->MaybeShowNewBadgeFor(
+      user_education::features::kNewBadgeTestFeature));
+  EXPECT_FALSE(UserEducationService::MaybeShowNewBadge(
+      incog->profile(), user_education::features::kNewBadgeTestFeature));
+
+  // Ensure that the feature can be marked as used.
+  for (int i = 0; i < user_education::features::GetNewBadgeFeatureUsedCount();
+       i += 2) {
+    browser()->window()->NotifyPromoFeatureUsed(
+        user_education::features::kNewBadgeTestFeature);
+    UserEducationService::MaybeNotifyPromoFeatureUsed(
+        browser()->profile(), user_education::features::kNewBadgeTestFeature);
+  }
+
+  // The badge should still be blocked.
+  EXPECT_FALSE(incog->window()->MaybeShowNewBadgeFor(
+      user_education::features::kNewBadgeTestFeature));
+  EXPECT_FALSE(UserEducationService::MaybeShowNewBadge(
+      incog->profile(), user_education::features::kNewBadgeTestFeature));
+}
diff --git a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc
index 8f519ff..d2c58f6 100644
--- a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc
@@ -8,9 +8,11 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/url_identity.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h"
@@ -58,6 +60,8 @@
 
 namespace {
 
+bool g_auto_accept_diy_dialog_for_testing = false;
+
 std::u16string GetTrimmedAppTitle(std::u16string app_title) {
   base::TrimWhitespace(app_title, base::TRIM_ALL, &app_title);
   return app_title;
@@ -120,9 +124,11 @@
     icon_view->SetImage(ui::ImageModel::FromImageSkia(icon_image));
     AddChildView(icon_view.release());
 
+    text_tracker_->data = web_app::NormalizeSuggestedAppTitle(app_title);
+
     AddChildView(views::Builder<views::Textfield>()
                      .CopyAddressTo(&title_field_)
-                     .SetText(web_app::NormalizeSuggestedAppTitle(app_title))
+                     .SetText(text_tracker_->data)
                      .SetAccessibleName(l10n_util::GetStringUTF16(
                          IDS_DIY_APP_AX_BUBBLE_NAME_LABEL))
                      .SetController(this)
@@ -212,7 +218,8 @@
   }
 
   auto* browser_context = web_contents->GetBrowserContext();
-  PrefService* prefs = Profile::FromBrowserContext(browser_context)->GetPrefs();
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  PrefService* prefs = profile->GetPrefs();
 
 #if BUILDFLAG(IS_CHROMEOS)
   if (base::FeatureList::IsEnabled(metrics::structured::kAppDiscoveryLogging)) {
@@ -230,9 +237,17 @@
   gfx::ImageSkia icon_image(std::make_unique<WebAppInfoImageSource>(
                                 kIconSize, web_app_info->icon_bitmaps.any),
                             gfx::Size(kIconSize, kIconSize));
-  auto app_name = web_app_info->title;
   GURL start_url = web_app_info->start_url;
 
+  // Fallback to using the document title if the web_app_info->title is not
+  // populated, as the document title is always guaranteed to exist.
+  std::u16string app_name = web_app_info->title;
+  if (app_name.empty()) {
+    app_name = UrlIdentity::CreateFromUrl(profile, start_url,
+                                          {UrlIdentity::Type::kDefault}, {})
+                   .name;
+  }
+
   DiyAppTitleFieldTextTracker data =
       base::MakeRefCounted<base::RefCountedData<std::u16string>>();
 
@@ -279,6 +294,14 @@
   dialog_coordinator->StartTracking(dialog_delegate);
 
   base::RecordAction(base::UserMetricsAction("WebAppDiyInstallShown"));
+
+  if (g_auto_accept_diy_dialog_for_testing) {
+    dialog_delegate->AcceptDialog();
+  }
+}
+
+void SetAutoAcceptDiyAppsInstallDialogForTesting(bool auto_accept) {
+  g_auto_accept_diy_dialog_for_testing = auto_accept;
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog_browsertest.cc
index 0145e582..83fbbcc9 100644
--- a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog_browsertest.cc
@@ -2,8 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
+
 #include "base/functional/callback_helpers.h"
 #include "base/test/metrics/user_action_tester.h"
+#include "base/test/test_future.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
@@ -35,7 +38,11 @@
   void ShowUi(const std::string& name) override {
     auto install_info = std::make_unique<WebAppInstallInfo>(
         GenerateManifestIdFromStartUrlOnly(GURL("https://example.com")));
-    install_info->title = u"test";
+
+    if (name != "empty_name") {
+      install_info->title = u"test";
+    }
+
     install_info->description = u"This is a test app";
     install_info->start_url = GURL("https://example.com");
     install_info->is_diy_app = true;
@@ -49,9 +56,17 @@
 
     ShowDiyAppInstallDialog(browser()->tab_strip_model()->GetWebContentsAt(0),
                             std::move(install_info), std::move(install_tracker),
-                            base::DoNothing(),
+                            std::move(install_callback_),
                             PwaInProductHelpState::kNotShown);
   }
+
+  void OverrideDialogCallback(
+      AppInstallationAcceptanceCallback install_callback) {
+    install_callback_ = std::move(install_callback);
+  }
+
+ private:
+  AppInstallationAcceptanceCallback install_callback_ = base::DoNothing();
 };
 
 IN_PROC_BROWSER_TEST_F(WebAppDiyInstallDialogBrowserTest, InvokeUiBasic) {
@@ -142,6 +157,31 @@
   EXPECT_EQ(1, action_tester.GetActionCount("WebAppDiyInstallCancelled"));
 }
 
+IN_PROC_BROWSER_TEST_F(WebAppDiyInstallDialogBrowserTest, InvokeUi_empty_name) {
+  base::UserActionTester action_tester;
+  EXPECT_TRUE(
+      ui_test_utils::NavigateToURL(browser(), GURL("https://example.com")));
+
+  base::test::TestFuture<bool, std::unique_ptr<WebAppInstallInfo>>
+      dialog_future;
+  OverrideDialogCallback(dialog_future.GetCallback());
+
+  views::NamedWidgetShownWaiter widget_waiter(
+      views::test::AnyWidgetTestPasskey{}, "WebAppDiyInstallDialog");
+  ShowUi("empty_name");
+  views::Widget* widget = widget_waiter.WaitIfNeededAndGet();
+
+  views::test::WidgetDestroyedWaiter destroy_waiter(widget);
+  views::test::AcceptDialog(widget);
+  destroy_waiter.Wait();
+  EXPECT_TRUE(dialog_future.Wait());
+
+  auto dialog_results = dialog_future.Take();
+  EXPECT_TRUE(std::get<bool>(dialog_results));
+  EXPECT_EQ(std::get<std::unique_ptr<WebAppInstallInfo>>(dialog_results)->title,
+            u"example.com");
+}
+
 }  // namespace
 
 }  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index 81ae992..f6bb1ed 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -757,9 +757,13 @@
 }
 
 void WaitForAndAcceptInstallDialogForSite(InstallableSite site) {
+  std::string simple_dialog_name =
+      base::FeatureList::IsEnabled(features::kWebAppUniversalInstall)
+          ? "WebAppSimpleInstallDialog"
+          : "PWAConfirmationBubbleView";
   std::string widget_name = site == InstallableSite::kScreenshots
                                 ? "WebAppDetailedInstallDialog"
-                                : "PWAConfirmationBubbleView";
+                                : simple_dialog_name;
   views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
                                        widget_name);
   views::Widget* widget = waiter.WaitIfNeededAndGet();
diff --git a/chrome/browser/ui/views/webauthn/pin_options_button.cc b/chrome/browser/ui/views/webauthn/pin_options_button.cc
index 224f5147..476fcbb 100644
--- a/chrome/browser/ui/views/webauthn/pin_options_button.cc
+++ b/chrome/browser/ui/views/webauthn/pin_options_button.cc
@@ -6,20 +6,19 @@
 
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/models/simple_menu_model.h"
-#include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/menu/menu_runner.h"
 
 PinOptionsButton::PinOptionsButton(const std::u16string& label,
                                    base::RepeatingCallback<void(bool)> callback)
-    : MenuButton(base::BindRepeating(&PinOptionsButton::ButtonPressed,
-                                     base::Unretained(this)),
-                 label),
+    : views::MdTextButtonWithDownArrow(
+          base::BindRepeating(&PinOptionsButton::ButtonPressed,
+                              base::Unretained(this)),
+          label),
       callback_(std::move(callback)),
       menu_model_(std::make_unique<ui::SimpleMenuModel>(this)) {
   SetAccessibleName(label);
   SetFocusBehavior(FocusBehavior::ALWAYS);
 
-  // TODO(rgod): Add border and icon.
   // TODO(rgod): Define actual command ids instead of integers.
   menu_model_->AddItem(1, u"6 digits (UT)");
   menu_model_->AddItem(2, u"Alphanumeric (UT)");
@@ -27,22 +26,13 @@
 
 PinOptionsButton::~PinOptionsButton() = default;
 
-bool PinOptionsButton::IsGroupFocusTraversable() const {
-  return false;
-}
-
 void PinOptionsButton::ButtonPressed() {
   menu_runner_ = std::make_unique<views::MenuRunner>(
       menu_model_.get(),
       views::MenuRunner::COMBOBOX | views::MenuRunner::HAS_MNEMONICS);
-
-  gfx::Point screen_loc;
-  views::View::ConvertPointToScreen(this, &screen_loc);
-  gfx::Rect bounds(screen_loc, this->size());
-
-  menu_runner_->RunMenuAt(GetWidget()->GetTopLevelWidget(), button_controller(),
-                          bounds, views::MenuAnchorPosition::kTopLeft,
-                          ui::MENU_SOURCE_NONE);
+  menu_runner_->RunMenuAt(
+      GetWidget(), /*button_controller=*/nullptr, GetBoundsInScreen(),
+      views::MenuAnchorPosition::kTopLeft, ui::MENU_SOURCE_NONE);
 }
 
 void PinOptionsButton::ExecuteCommand(int command_id, int event_flags) {
diff --git a/chrome/browser/ui/views/webauthn/pin_options_button.h b/chrome/browser/ui/views/webauthn/pin_options_button.h
index 55ce360..ec390fc7 100644
--- a/chrome/browser/ui/views/webauthn/pin_options_button.h
+++ b/chrome/browser/ui/views/webauthn/pin_options_button.h
@@ -5,9 +5,9 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_WEBAUTHN_PIN_OPTIONS_BUTTON_H_
 #define CHROME_BROWSER_UI_VIEWS_WEBAUTHN_PIN_OPTIONS_BUTTON_H_
 
+#include "chrome/browser/ui/views/controls/md_text_button_with_down_arrow.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/models/simple_menu_model.h"
-#include "ui/views/controls/button/menu_button.h"
 
 namespace views {
 class MenuRunner;
@@ -15,9 +15,9 @@
 
 // Defines a button visible in the GPM pin creation dialog, that upon pressing
 // displays a menu with pin format options, allowing to pick one.
-class PinOptionsButton : public views::MenuButton,
+class PinOptionsButton : public views::MdTextButtonWithDownArrow,
                          public ui::SimpleMenuModel::Delegate {
-  METADATA_HEADER(PinOptionsButton, views::MenuButton)
+  METADATA_HEADER(PinOptionsButton, views::MdTextButtonWithDownArrow)
 
  public:
   PinOptionsButton(const std::u16string& label,
@@ -26,9 +26,6 @@
   PinOptionsButton& operator=(const PinOptionsButton&) = delete;
   ~PinOptionsButton() override;
 
-  // views::MenuButton:
-  bool IsGroupFocusTraversable() const override;
-
   // ui::SimpleMenuModel::Delegate:
   void ExecuteCommand(int command_id, int event_flags) override;
 
diff --git a/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc b/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
index 8d51f837..8c21af0f 100644
--- a/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
+++ b/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/browser_test.h"
@@ -102,14 +103,20 @@
 };
 
 // Tests that creating a shortcut app but not installing a PWA is available for
-// a non-installable site.
+// a non-installable site, unless the universal install feature flag is enabled.
 IN_PROC_BROWSER_TEST_F(PWAMixedContentBrowserTest,
                        ShortcutMenuOptionsForNonInstallableSite) {
   EXPECT_FALSE(
       NavigateAndAwaitInstallabilityCheck(browser(), GetMixedContentAppURL()));
 
   EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, browser()), kEnabled);
-  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kNotPresent);
+
+  AppMenuCommandState expected_command_state =
+      base::FeatureList::IsEnabled(features::kWebAppUniversalInstall)
+          ? kEnabled
+          : kNotPresent;
+  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()),
+            expected_command_state);
 }
 
 // Tests that mixed content is loaded inside PWA windows.
diff --git a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc
index 0486d08..3935f909 100644
--- a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc
+++ b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc
@@ -29,6 +29,7 @@
 #include "components/webapps/common/web_app_id.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/navigation_simulator.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "third_party/blink/public/common/features_generated.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -190,4 +191,20 @@
   return app_id;
 }
 
+void SimulateIsolatedWebAppNavigation(content::WebContents* web_contents,
+                                      const GURL& url) {
+  auto navigation =
+      content::NavigationSimulator::CreateBrowserInitiated(url, web_contents);
+  navigation->SetTransition(ui::PAGE_TRANSITION_TYPED);
+  // We need to inject the COI headers here because they're normally injected
+  // by IsolatedWebAppURLLoader, which is skipped when simulating navigations.
+  navigation->SetResponseHeaders(
+      net::HttpResponseHeaders::Builder(net::HttpVersion(1, 1), "200 OK")
+          .AddHeader("Cross-Origin-Opener-Policy", "same-origin")
+          .AddHeader("Cross-Origin-Embedder-Policy", "require-corp")
+          .AddHeader("Cross-Origin-Resource-Policy", "same-origin")
+          .Build());
+  navigation->Commit();
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h
index 4c9dc18..c34f64d 100644
--- a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h
+++ b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h
@@ -99,6 +99,13 @@
         IwaStorageOwnedBundle{/*dir_name_ascii=*/"", /*dev_mode=*/false},
         base::Version("1.0.0")));
 
+// Simulates navigating `web_contents` main frame to the provided isolated-app:
+// URL for unit tests. `TestWebContents::NavigateAndCommit` won't work for IWAs
+// because they require COI headers, but the IsolatedWebAppURLLoaderFactory
+// that injects them isn't run in RenderViewHostTestHarness-based unit tests.
+void SimulateIsolatedWebAppNavigation(content::WebContents* web_contents,
+                                      const GURL& url);
+
 // TODO(cmfcmf): Move more test utils into this `test` namespace
 namespace test {
 
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 057e4b02..9ac1295 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -1051,7 +1051,12 @@
       https_server()->GetURL("/banners/no_manifest_test_page.html"));
 
   EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, new_browser), kEnabled);
-  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, new_browser), kNotPresent);
+  AppMenuCommandState install_pwa_state =
+      base::FeatureList::IsEnabled(features::kWebAppUniversalInstall)
+          ? kEnabled
+          : kNotPresent;
+  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, new_browser),
+            install_pwa_state);
   EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, new_browser),
             kNotPresent);
 }
@@ -1299,7 +1304,15 @@
                     .GetCommandsInstallingForWebContentsForTesting());
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, WindowsOffsetForMultiWindowPWA) {
+// TODO(b/330221671): Deflake and re-enable.
+#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
+#define MAYBE_WindowsOffsetForMultiWindowPWA \
+  DISABLED_WindowsOffsetForMultiWindowPWA
+#else
+#define MAYBE_WindowsOffsetForMultiWindowPWA WindowsOffsetForMultiWindowPWA
+#endif
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest,
+                       MAYBE_WindowsOffsetForMultiWindowPWA) {
   const GURL app_url(kExampleURL);
   const webapps::AppId app_id = InstallPWA(app_url);
 
@@ -1767,7 +1780,7 @@
   web_app_info->scope = app_url.GetWithoutFilename();
   web_app_info->display_mode = DisplayMode::kBrowser;
   web_app_info->user_display_mode = mojom::UserDisplayMode::kStandalone;
-  web_app_info->title = u"A Shortcut App";
+  web_app_info->title = u"A Non installable App";
   const webapps::AppId app_id = InstallWebApp(std::move(web_app_info));
 
   base::HistogramTester tester;
@@ -2084,7 +2097,11 @@
       "manifest_test_page.html?manifest=manifest_display_browser.json");
   NavigateAndAwaitInstallabilityCheck(browser(), url);
 
-  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kNotPresent);
+  AppMenuCommandState install_state =
+      base::FeatureList::IsEnabled(features::kWebAppUniversalInstall)
+          ? kEnabled
+          : kNotPresent;
+  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), install_state);
 
   // Install using Create Shortcut.
   SetAutoAcceptWebAppDialogForTesting(/*auto_accept=*/true,
@@ -2097,7 +2114,8 @@
 
   // Navigate to this site again and install should still be disabled.
   Browser* new_browser = NavigateInNewWindowAndAwaitInstallabilityCheck(url);
-  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, new_browser), kNotPresent);
+  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, new_browser),
+            install_state);
 }
 
 IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, WindowControlsOverlay) {
diff --git a/chrome/browser/ui/web_applications/web_app_controller_browsertest.h b/chrome/browser/ui/web_applications/web_app_controller_browsertest.h
index 457c983e..303bcb8 100644
--- a/chrome/browser/ui/web_applications/web_app_controller_browsertest.h
+++ b/chrome/browser/ui/web_applications/web_app_controller_browsertest.h
@@ -18,6 +18,10 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/ui/chromeos/test_util.h"
+#endif
+
 class Profile;
 
 namespace base {
@@ -33,7 +37,11 @@
 class WebAppProvider;
 
 // Base class for tests of user interface support for web applications.
+#if BUILDFLAG(IS_CHROMEOS)
+class WebAppControllerBrowserTest : public ChromeOSBrowserUITest {
+#else
 class WebAppControllerBrowserTest : public InProcessBrowserTest {
+#endif
  public:
   WebAppControllerBrowserTest();
   WebAppControllerBrowserTest(const WebAppControllerBrowserTest&) = delete;
diff --git a/chrome/browser/ui/web_applications/web_app_dialogs.h b/chrome/browser/ui/web_applications/web_app_dialogs.h
index c68ea4ac..1a405f2 100644
--- a/chrome/browser/ui/web_applications/web_app_dialogs.h
+++ b/chrome/browser/ui/web_applications/web_app_dialogs.h
@@ -132,7 +132,7 @@
 void SetAutoAcceptWebAppDialogForTesting(bool auto_accept,
                                          bool auto_open_in_window);
 
-// Sets an override title for the installation.
+// Sets an override title for the Create Shortcut confirmation view.
 void SetOverrideTitleForTesting(const char* title_to_use);
 
 // Describes the state of in-product-help being shown to the user.
@@ -181,6 +181,10 @@
 // without any user interaction.
 void SetAutoAcceptPWAInstallConfirmationForTesting(bool auto_accept);
 
+// Sets whether |ShowDiyInstallDialogForWebApps| should accept immediately
+// without any user interaction.
+void SetAutoAcceptDiyAppsInstallDialogForTesting(bool auto_accept);
+
 // Sets whether the bubble should close when it is not in an active window
 // during testing.
 base::AutoReset<bool> SetDontCloseOnDeactivateForTesting();
diff --git a/chrome/browser/ui/web_applications/web_app_title_browsertest.cc b/chrome/browser/ui/web_applications/web_app_title_browsertest.cc
index 716164a..9438b2b 100644
--- a/chrome/browser/ui/web_applications/web_app_title_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_title_browsertest.cc
@@ -26,7 +26,13 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_F(WebAppTitleBrowserTest, ValidAppTitle) {
+// TODO(b/330201484): Deflake and re-enable.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_ValidAppTitle DISABLED_ValidAppTitle
+#else
+#define MAYBE_ValidAppTitle ValidAppTitle
+#endif
+IN_PROC_BROWSER_TEST_F(WebAppTitleBrowserTest, MAYBE_ValidAppTitle) {
   const GURL app_url =
       https_server()->GetURL("/web_apps/page_with_app_title.html");
   const std::u16string app_title = u"A Web App";
diff --git a/chrome/browser/ui/webui/ash/cros_components_browsertest.cc b/chrome/browser/ui/webui/ash/cros_components_browsertest.cc
index 547fc4f..dc4265f 100644
--- a/chrome/browser/ui/webui/ash/cros_components_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/cros_components_browsertest.cc
@@ -207,8 +207,7 @@
         .gtest_name = "CrosIconButton",
     },
     {
-        .script_src =
-            "chrome://resources/cros_components/dropdown/dropdown.js",
+        .script_src = "chrome://resources/cros_components/dropdown/dropdown.js",
         .component_name = "cros-dropdown",
         .gtest_name = "CrosDropdown",
     },
@@ -219,26 +218,22 @@
         .gtest_name = "CrosDropdownOption",
     },
     {
-        .script_src =
-            "chrome://resources/cros_components/tabs/tabs.js",
+        .script_src = "chrome://resources/cros_components/tabs/tabs.js",
         .component_name = "cros-tabs",
         .gtest_name = "CrosTabs",
     },
     {
-        .script_src =
-            "chrome://resources/cros_components/tabs/tab.js",
+        .script_src = "chrome://resources/cros_components/tabs/tab.js",
         .component_name = "cros-tab",
         .gtest_name = "CrosTab",
     },
     {
-        .script_src =
-            "chrome://resources/cros_components/menu/menu.js",
+        .script_src = "chrome://resources/cros_components/menu/menu.js",
         .component_name = "cros-menu",
         .gtest_name = "CrosMenu",
     },
     {
-        .script_src =
-            "chrome://resources/cros_components/menu/menu_item.js",
+        .script_src = "chrome://resources/cros_components/menu/menu_item.js",
         .component_name = "cros-menu-item",
         .gtest_name = "CrosMenuItem",
     },
@@ -255,8 +250,7 @@
         .gtest_name = "CrosSubMenuItem",
     },
     {
-        .script_src =
-            "chrome://resources/cros_components/snackbar/snackbar.js",
+        .script_src = "chrome://resources/cros_components/snackbar/snackbar.js",
         .component_name = "cros-snackbar",
         .gtest_name = "CrosSnackbar",
     },
@@ -266,6 +260,12 @@
         .component_name = "cros-snackbar-item",
         .gtest_name = "CrosSnackbarItem",
     },
+    {
+        .script_src =
+            "chrome://resources/cros_components/orca_feedback/orca-feedback.js",
+        .component_name = "mako-orca-feedback",
+        .gtest_name = "CrosOrcaFeedbackItem",
+    },
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
diff --git a/chrome/browser/ui/webui/ash/extended_updates/extended_updates_ui.cc b/chrome/browser/ui/webui/ash/extended_updates/extended_updates_ui.cc
index 95a6782..e7db25c 100644
--- a/chrome/browser/ui/webui/ash/extended_updates/extended_updates_ui.cc
+++ b/chrome/browser/ui/webui/ash/extended_updates/extended_updates_ui.cc
@@ -4,8 +4,8 @@
 
 #include "chrome/browser/ui/webui/ash/extended_updates/extended_updates_ui.h"
 
-#include "ash/constants/ash_features.h"
 #include "base/containers/span.h"
+#include "chrome/browser/ash/extended_updates/extended_updates_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/webui_url_constants.h"
@@ -40,8 +40,7 @@
 
 bool ExtendedUpdatesUIConfig::IsWebUIEnabled(
     content::BrowserContext* browser_context) {
-  // TODO(b/322418004): Also gate on user pref.
-  return ash::features::IsExtendedUpdatesRequireOptInEnabled();
+  return ash::IsExtendedUpdatesOptInEligible(browser_context);
 }
 
 }  // namespace ash::extended_updates
diff --git a/chrome/browser/ui/webui/ash/settings/pages/device/device_section.cc b/chrome/browser/ui/webui/ash/settings/pages/device/device_section.cc
index 9312019..13262d6 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/device/device_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/device/device_section.cc
@@ -833,9 +833,14 @@
       {"perDeviceKeyboardKeyEscape",
        IDS_SETTINGS_PER_DEVICE_KEYBOARD_KEY_ESCAPE},
       {"perDeviceKeyboardKeyMeta", IDS_SETTINGS_PER_DEVICE_KEYBOARD_KEY_META},
+      {"perDeviceKeyboardKeyRightAlt",
+       IDS_SETTINGS_PER_DEVICE_KEYBOARD_KEY_RIGHT_ALT},
   };
   html_source->AddLocalizedStrings(keyboard_strings);
 
+  html_source->AddBoolean("enableModifierSplit",
+                          ash::features::IsModifierSplitEnabled());
+
   if (Shell::Get()->keyboard_capability()->HasLauncherButtonOnAnyKeyboard()) {
     html_source->AddLocalizedString(
         "keyboardBlockMetaFunctionKeyRewrites",
diff --git a/chrome/browser/ui/webui/ash/settings/pages/multidevice/multidevice_section.cc b/chrome/browser/ui/webui/ash/settings/pages/multidevice/multidevice_section.cc
index 0a95cfd6..01b7a97 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/multidevice/multidevice_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/multidevice/multidevice_section.cc
@@ -549,6 +549,8 @@
        IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_PROHIBITED_TOOLTIP},
       {"multideviceItemDisabledByPhoneAdminTooltip",
        IDS_SETTINGS_MULTIDEVICE_ITEM_DISABLED_BY_PHONE_ADMIN_TOOLTIP},
+      {"multideviceInstantHotspotItemTitle",
+       IDS_SETTINGS_MULTIDEVICE_INSTANT_HOTSPOT},
       {"multideviceInstantTetheringItemTitle",
        IDS_SETTINGS_MULTIDEVICE_INSTANT_TETHERING},
       {"multideviceInstantTetheringItemSummary",
diff --git a/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc b/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc
index 0f2dad2..3bdbdb9 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc
@@ -524,6 +524,12 @@
        IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_ONLY_ALLOWED_FOR_SYSTEM},
       {"geolocationAccessLevelDisallowed",
        IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DISALLOWED},
+      {"geolocationAllowedModeDescription",
+       IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ALLOWED},
+      {"geolocationOnlyAllowedForSystemModeDescription",
+       IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_ONLY_ALLOWED_FOR_SYSTEM},
+      {"geolocationBlockedModeDescription",
+       IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCESS_LEVEL_DESCRIPTION_DISALLOWED},
       {"geolocationAccuracyToggleText",
        IDS_OS_SETTINGS_PRIVACY_HUB_GEOLOCATION_ACCURACY_TOGGLE_TEXT},
       {"geolocationAccuracyToggleTitle",
diff --git a/chrome/browser/ui/webui/history/history_ui.cc b/chrome/browser/ui/webui/history/history_ui.cc
index 8832313..c3cfeb05 100644
--- a/chrome/browser/ui/webui/history/history_ui.cc
+++ b/chrome/browser/ui/webui/history/history_ui.cc
@@ -171,6 +171,11 @@
         {"historyEmbeddingsSuggestion1", IDS_HISTORY_EMBEDDINGS_SUGGESTION_1},
         {"historyEmbeddingsSuggestion2", IDS_HISTORY_EMBEDDINGS_SUGGESTION_2},
         {"historyEmbeddingsSuggestion3", IDS_HISTORY_EMBEDDINGS_SUGGESTION_3},
+        {"historyEmbeddingsHeading", IDS_HISTORY_EMBEDDINGS_HEADING},
+        {"historyEmbeddingsFooter", IDS_HISTORY_EMBEDDINGS_FOOTER},
+        {"learnMore", IDS_LEARN_MORE},
+        {"thumbsUp", IDS_HISTORY_EMBEDDINGS_THUMBS_UP},
+        {"thumbsDown", IDS_HISTORY_EMBEDDINGS_THUMBS_DOWN},
     };
     source->AddLocalizedStrings(kHistoryEmbeddingsStrings);
   }
diff --git a/chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom b/chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom
index c4785f7..5f694c8 100644
--- a/chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom
+++ b/chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom
@@ -66,4 +66,10 @@
 
   // Clears all session-related User Education data.
   ClearSessionData() => (string error_message);
+
+  // Retrieves "New" Badge data.
+  GetNewBadges() => (array<FeaturePromoDemoPageInfo> new_badges);
+
+  // Clears "New" Badge data associated with a particular feature.
+  ClearNewBadgeData(string feature_name) => (string error_message);
 };
diff --git a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
index d849e47..05169911 100644
--- a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
+++ b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
@@ -57,6 +57,12 @@
   return service ? &service->feature_promo_registry() : nullptr;
 }
 
+user_education::NewBadgeRegistry* GetNewBadgeRegistry(Profile* profile) {
+  auto* const service =
+      UserEducationServiceFactory::GetForBrowserContext(profile);
+  return service ? service->new_badge_registry() : nullptr;
+}
+
 user_education::FeaturePromoStorageService* GetStorageService(
     Profile* profile) {
   auto* const service =
@@ -91,8 +97,14 @@
 
 const base::Feature* GetFeatureByName(const std::string& feature_name,
                                       Profile* profile) {
-  auto* const registry = GetFeaturePromoRegistry(profile);
-  if (registry) {
+  if (auto* const registry = GetFeaturePromoRegistry(profile)) {
+    for (const auto& [feature, spec] : registry->feature_data()) {
+      if (feature_name == feature->name) {
+        return feature;
+      }
+    }
+  }
+  if (auto* const registry = GetNewBadgeRegistry(profile)) {
     for (const auto& [feature, spec] : registry->feature_data()) {
       if (feature_name == feature->name) {
         return feature;
@@ -332,6 +344,19 @@
   return result;
 }
 
+auto GetNewBadgeData(
+    const base::Feature& feature,
+    const user_education::FeaturePromoStorageService* storage_service) {
+  std::vector<FeaturePromoDemoPageDataPtr> result;
+  const auto data = storage_service->ReadNewBadgeData(feature);
+  result.emplace_back(
+      FormatDemoPageData("Feature enabled at", data.feature_enabled_time));
+  result.emplace_back(FormatDemoPageData("Show count", data.show_count));
+  result.emplace_back(
+      FormatDemoPageData("Feature used count", data.used_count));
+  return result;
+}
+
 std::vector<std::string> GetTutorialInstructions(
     const user_education::TutorialDescription& desc) {
   std::vector<std::string> instructions;
@@ -510,7 +535,7 @@
     ClearFeaturePromoDataCallback callback) {
   const base::Feature* feature = GetFeatureByName(feature_name, profile_);
   if (!feature) {
-    std::move(callback).Run(std::string("Cannot find IPH."));
+    std::move(callback).Run(std::string("Cannot find IPH: ") + feature_name);
     return;
   }
 
@@ -550,3 +575,49 @@
 
   std::move(callback).Run(std::string());
 }
+
+void UserEducationInternalsPageHandlerImpl::GetNewBadges(
+    GetNewBadgesCallback callback) {
+  std::vector<FeaturePromoDemoPageInfoPtr> info_list;
+
+  auto* const registry = GetNewBadgeRegistry(profile_);
+  auto* const storage_service = GetStorageService(profile_);
+  if (registry) {
+    for (const auto& [feature, spec] : registry->feature_data()) {
+      info_list.emplace_back(FeaturePromoDemoPageInfo::New(
+          RemovePrefixAndCamelCase(feature->name, ""),
+          spec.metadata.additional_description, feature->name, "\"New\" Badge",
+          spec.metadata.launch_milestone,
+          GetSupportedPlatforms(spec.metadata.platforms),
+          GetRequiredFeatures(spec.metadata.required_features),
+          std::vector<std::string>(), "",
+          GetNewBadgeData(*feature, storage_service)));
+    }
+  }
+
+  return std::move(callback).Run(std::move(info_list));
+}
+
+void UserEducationInternalsPageHandlerImpl::ClearNewBadgeData(
+    const std::string& feature_name,
+    ClearNewBadgeDataCallback callback) {
+  const base::Feature* feature = GetFeatureByName(feature_name, profile_);
+  if (!feature) {
+    std::move(callback).Run(std::string("Cannot find feature: ") +
+                            feature_name);
+    return;
+  }
+
+  auto* const storage_service = GetStorageService(profile_);
+  if (!storage_service) {
+    std::move(callback).Run(std::string("No storage service."));
+    return;
+  }
+
+  auto data = storage_service->ReadNewBadgeData(*feature);
+  data.show_count = 0;
+  data.used_count = 0;
+  storage_service->SaveNewBadgeData(*feature, data);
+
+  std::move(callback).Run(std::string());
+}
diff --git a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.h b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.h
index 025654e1..ecc2343 100644
--- a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.h
+++ b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.h
@@ -47,6 +47,9 @@
   void ClearFeaturePromoData(const std::string& feature_name,
                              ClearFeaturePromoDataCallback callback) override;
   void ClearSessionData(ClearSessionDataCallback callback) override;
+  void GetNewBadges(GetNewBadgesCallback callback) override;
+  void ClearNewBadgeData(const std::string& feature_name,
+                         ClearNewBadgeDataCallback callback) override;
 
  private:
   raw_ptr<content::WebUI> web_ui_ = nullptr;
diff --git a/chrome/browser/ui/webui/searchbox/realbox_handler.cc b/chrome/browser/ui/webui/searchbox/realbox_handler.cc
index a5021496..1249fbc9 100644
--- a/chrome/browser/ui/webui/searchbox/realbox_handler.cc
+++ b/chrome/browser/ui/webui/searchbox/realbox_handler.cc
@@ -76,142 +76,142 @@
 
 // TODO(niharm): convert back to constexpr char[] once feature is cleaned up
 const char* kAnswerCurrencyIconResourceName =
-    "//resources/cr_components/omnibox/icons/currency.svg";
+    "//resources/cr_components/searchbox/icons/currency.svg";
 constexpr char kAnswerDefaultIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/default.svg";
+    "//resources/cr_components/searchbox/icons/default.svg";
 const char* kAnswerDictionaryIconResourceName =
-    "//resources/cr_components/omnibox/icons/definition.svg";
+    "//resources/cr_components/searchbox/icons/definition.svg";
 const char* kAnswerFinanceIconResourceName =
-    "//resources/cr_components/omnibox/icons/finance.svg";
+    "//resources/cr_components/searchbox/icons/finance.svg";
 const char* kAnswerSunriseIconResourceName =
-    "//resources/cr_components/omnibox/icons/sunrise.svg";
+    "//resources/cr_components/searchbox/icons/sunrise.svg";
 const char* kAnswerTranslationIconResourceName =
-    "//resources/cr_components/omnibox/icons/translation.svg";
+    "//resources/cr_components/searchbox/icons/translation.svg";
 const char* kAnswerWhenIsIconResourceName =
-    "//resources/cr_components/omnibox/icons/when_is.svg";
+    "//resources/cr_components/searchbox/icons/when_is.svg";
 const char* kBookmarkIconResourceName = "//resources/images/icon_bookmark.svg";
 const char* kCalculatorIconResourceName =
-    "//resources/cr_components/omnibox/icons/calculator.svg";
+    "//resources/cr_components/searchbox/icons/calculator.svg";
 const char* kChromeProductIconResourceName =
-    "//resources/cr_components/omnibox/icons/chrome_product.svg";
+    "//resources/cr_components/searchbox/icons/chrome_product.svg";
 const char* kClockIconResourceName = "//resources/images/icon_clock.svg";
 const char* kDinoIconResourceName =
-    "//resources/cr_components/omnibox/icons/dino.svg";
+    "//resources/cr_components/searchbox/icons/dino.svg";
 constexpr char kDriveDocsIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/drive_docs.svg";
+    "//resources/cr_components/searchbox/icons/drive_docs.svg";
 constexpr char kDriveFolderIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/drive_folder.svg";
+    "//resources/cr_components/searchbox/icons/drive_folder.svg";
 constexpr char kDriveFormIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/drive_form.svg";
+    "//resources/cr_components/searchbox/icons/drive_form.svg";
 constexpr char kDriveImageIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/drive_image.svg";
+    "//resources/cr_components/searchbox/icons/drive_image.svg";
 constexpr char kDriveLogoIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/drive_logo.svg";
+    "//resources/cr_components/searchbox/icons/drive_logo.svg";
 constexpr char kDrivePdfIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/drive_pdf.svg";
+    "//resources/cr_components/searchbox/icons/drive_pdf.svg";
 constexpr char kDriveSheetsIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/drive_sheets.svg";
+    "//resources/cr_components/searchbox/icons/drive_sheets.svg";
 constexpr char kDriveSlidesIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/drive_slides.svg";
+    "//resources/cr_components/searchbox/icons/drive_slides.svg";
 constexpr char kDriveVideoIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/drive_video.svg";
+    "//resources/cr_components/searchbox/icons/drive_video.svg";
 constexpr char kExtensionAppIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/extension_app.svg";
+    "//resources/cr_components/searchbox/icons/extension_app.svg";
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 constexpr char kGoogleCalendarIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/calendar.svg";
+    "//resources/cr_components/searchbox/icons/calendar.svg";
 const char* kGoogleGIconResourceName =
-    "//resources/cr_components/omnibox/icons/google_g.svg";
+    "//resources/cr_components/searchbox/icons/google_g.svg";
 constexpr char kGoogleKeepNoteIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/note.svg";
+    "//resources/cr_components/searchbox/icons/note.svg";
 constexpr char kGoogleSitesIconResourceName[] =
-    "//resources/cr_components/omnibox/icons/sites.svg";
+    "//resources/cr_components/searchbox/icons/sites.svg";
 #endif
 const char* kHistoryIconResourceName = "//resources/images/icon_history.svg";
 const char* kIncognitoIconResourceName =
-    "//resources/cr_components/omnibox/icons/incognito.svg";
+    "//resources/cr_components/searchbox/icons/incognito.svg";
 const char* kJourneysIconResourceName =
-    "//resources/cr_components/omnibox/icons/journeys.svg";
+    "//resources/cr_components/searchbox/icons/journeys.svg";
 const char* kPageIconResourceName =
-    "//resources/cr_components/omnibox/icons/page.svg";
+    "//resources/cr_components/searchbox/icons/page.svg";
 const char* kPedalsIconResourceName = "//theme/current-channel-logo";
 const char* kSearchIconResourceName = "//resources/images/icon_search.svg";
 const char* kTabIconResourceName =
-    "//resources/cr_components/omnibox/icons/tab.svg";
+    "//resources/cr_components/searchbox/icons/tab.svg";
 const char* kTrendingUpIconResourceName =
-    "//resources/cr_components/omnibox/icons/trending_up.svg";
+    "//resources/cr_components/searchbox/icons/trending_up.svg";
 
 #if BUILDFLAG(IS_MAC)
 const char* kMacShareIconResourceName =
-    "//resources/cr_components/omnibox/icons/mac_share.svg";
+    "//resources/cr_components/searchbox/icons/mac_share.svg";
 #elif BUILDFLAG(IS_WIN)
 const char* kWinShareIconResourceName =
-    "//resources/cr_components/omnibox/icons/win_share.svg";
+    "//resources/cr_components/searchbox/icons/win_share.svg";
 #elif BUILDFLAG(IS_LINUX)
 const char* kLinuxShareIconResourceName =
-    "//resources/cr_components/omnibox/icons/share.svg";
+    "//resources/cr_components/searchbox/icons/share.svg";
 #else
 const char* kShareIconResourceName =
-    "//resources/cr_components/omnibox/icons/share.svg";
+    "//resources/cr_components/searchbox/icons/share.svg";
 #endif
 
 static void DefineChromeRefreshRealboxIcons() {
   kAnswerCurrencyIconResourceName =
-      "//resources/cr_components/omnibox/icons/currency_cr23.svg";
+      "//resources/cr_components/searchbox/icons/currency_cr23.svg";
   kAnswerDictionaryIconResourceName =
-      "//resources/cr_components/omnibox/icons/definition_cr23.svg";
+      "//resources/cr_components/searchbox/icons/definition_cr23.svg";
   kAnswerFinanceIconResourceName =
-      "//resources/cr_components/omnibox/icons/finance_cr23.svg";
+      "//resources/cr_components/searchbox/icons/finance_cr23.svg";
   kAnswerSunriseIconResourceName =
-      "//resources/cr_components/omnibox/icons/sunrise_cr23.svg";
+      "//resources/cr_components/searchbox/icons/sunrise_cr23.svg";
   kAnswerTranslationIconResourceName =
-      "//resources/cr_components/omnibox/icons/translation_cr23.svg";
+      "//resources/cr_components/searchbox/icons/translation_cr23.svg";
   kAnswerWhenIsIconResourceName =
-      "//resources/cr_components/omnibox/icons/when_is_cr23.svg";
+      "//resources/cr_components/searchbox/icons/when_is_cr23.svg";
   kBookmarkIconResourceName =
-      "//resources/cr_components/omnibox/icons/bookmark_cr23.svg";
+      "//resources/cr_components/searchbox/icons/bookmark_cr23.svg";
   kCalculatorIconResourceName =
-      "//resources/cr_components/omnibox/icons/calculator_cr23.svg";
+      "//resources/cr_components/searchbox/icons/calculator_cr23.svg";
   kChromeProductIconResourceName =
-      "//resources/cr_components/omnibox/icons/chrome_product_cr23.svg";
+      "//resources/cr_components/searchbox/icons/chrome_product_cr23.svg";
   kClockIconResourceName =
-      "//resources/cr_components/omnibox/icons/clock_cr23.svg";
+      "//resources/cr_components/searchbox/icons/clock_cr23.svg";
   kDinoIconResourceName =
-      "//resources/cr_components/omnibox/icons/dino_cr23.svg";
+      "//resources/cr_components/searchbox/icons/dino_cr23.svg";
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   kGoogleGIconResourceName =
-      "//resources/cr_components/omnibox/icons/google_g_cr23.svg";
+      "//resources/cr_components/searchbox/icons/google_g_cr23.svg";
 #endif
 
   kHistoryIconResourceName =
-      "//resources/cr_components/omnibox/icons/history_cr23.svg";
+      "//resources/cr_components/searchbox/icons/history_cr23.svg";
   kIncognitoIconResourceName =
-      "//resources/cr_components/omnibox/icons/incognito_cr23.svg";
+      "//resources/cr_components/searchbox/icons/incognito_cr23.svg";
   kJourneysIconResourceName =
-      "//resources/cr_components/omnibox/icons/journeys_cr23.svg";
+      "//resources/cr_components/searchbox/icons/journeys_cr23.svg";
   kPageIconResourceName =
-      "//resources/cr_components/omnibox/icons/page_cr23.svg";
+      "//resources/cr_components/searchbox/icons/page_cr23.svg";
   kPedalsIconResourceName =
-      "//resources/cr_components/omnibox/icons/chrome_product_cr23.svg";
+      "//resources/cr_components/searchbox/icons/chrome_product_cr23.svg";
   kSearchIconResourceName =
-      "//resources/cr_components/omnibox/icons/search_cr23.svg";
-  kTabIconResourceName = "//resources/cr_components/omnibox/icons/tab_cr23.svg";
+      "//resources/cr_components/searchbox/icons/search_cr23.svg";
+  kTabIconResourceName = "//resources/cr_components/searchbox/icons/tab_cr23.svg";
   kTrendingUpIconResourceName =
-      "//resources/cr_components/omnibox/icons/trending_up_cr23.svg";
+      "//resources/cr_components/searchbox/icons/trending_up_cr23.svg";
 
 #if BUILDFLAG(IS_MAC)
   kMacShareIconResourceName =
-      "//resources/cr_components/omnibox/icons/mac_share_cr23.svg";
+      "//resources/cr_components/searchbox/icons/mac_share_cr23.svg";
 #elif BUILDFLAG(IS_WIN)
   kWinShareIconResourceName =
-      "//resources/cr_components/omnibox/icons/win_share_cr23.svg";
+      "//resources/cr_components/searchbox/icons/win_share_cr23.svg";
 #elif BUILDFLAG(IS_LINUX)
   kLinuxShareIconResourceName =
-      "//resources/cr_components/omnibox/icons/share_cr23.svg";
+      "//resources/cr_components/searchbox/icons/share_cr23.svg";
 #else
   kShareIconResourceName =
-      "//resources/cr_components/omnibox/icons/share_cr23.svg";
+      "//resources/cr_components/searchbox/icons/share_cr23.svg";
 #endif
 }
 
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 df960ac..6c17f28 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
@@ -545,7 +545,6 @@
       content::RenderFrameHost* render_frame_host;
       if (is_pdf) {
         contents->ForEachRenderFrameHost([&](content::RenderFrameHost* rfh) {
-          // TODO: Use RenderFrameHostImpl::IsPdf.
           if (rfh->GetProcess()->IsPdf()) {
             render_frame_host = rfh;
           }
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
index b50ecd8..9cf3f94 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -325,6 +325,27 @@
   return false;
 }
 
+void BeginFirstWebContentsProfiling(Browser* browser,
+                                    base::TimeTicks pick_time) {
+  content::WebContents* visible_contents =
+      metrics::FirstWebContentsProfilerBase::GetVisibleContents(browser);
+  if (!visible_contents) {
+    RecordProfilingFinishReason(metrics::StartupProfilingFinishReason::
+                                    kAbandonNoInitiallyVisibleContent);
+    return;
+  }
+
+  if (visible_contents->CompletedFirstVisuallyNonEmptyPaint()) {
+    RecordProfilingFinishReason(
+        metrics::StartupProfilingFinishReason::kAbandonAlreadyPaintedContent);
+    return;
+  }
+
+  // FirstWebContentsProfilerForProfilePicker owns itself and is also bound to
+  // |visible_contents|'s lifetime by observing WebContentsDestroyed().
+  new FirstWebContentsProfilerForProfilePicker(visible_contents, pick_time);
+}
+
 }  // namespace
 
 ProfilePickerHandler::ProfilePickerHandler()
@@ -401,9 +422,10 @@
       base::BindRepeating(&ProfilePickerHandler::HandleGetAvailableIcons,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "continueWithoutAccount",
-      base::BindRepeating(&ProfilePickerHandler::HandleContinueWithoutAccount,
-                          base::Unretained(this)));
+      "createProfileAndOpenCustomizationDialog",
+      base::BindRepeating(
+          &ProfilePickerHandler::HandleCreateProfileAndOpenCustomizationDialog,
+          base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       "getSwitchProfile",
       base::BindRepeating(&ProfilePickerHandler::HandleGetSwitchProfile,
@@ -704,7 +726,7 @@
                             profiles::GetCustomProfileAvatarIconsAndLabels());
 }
 
-void ProfilePickerHandler::HandleContinueWithoutAccount(
+void ProfilePickerHandler::HandleCreateProfileAndOpenCustomizationDialog(
     const base::Value::List& args) {
   CHECK_EQ(1U, args.size());
 
@@ -713,17 +735,17 @@
   if (args[0].is_int())
     profile_color = args[0].GetInt();
 
-  RecordProfilePickerAction(ProfilePickerAction::kLaunchNewProfile);
+  size_t icon_index = profiles::GetPlaceholderAvatarIndex();
+
   ProfileMetrics::LogProfileAddNewUser(
       ProfileMetrics::ADD_NEW_PROFILE_PICKER_LOCAL);
-  ProfilePicker::SwitchToSignedOutPostIdentityFlow(
-      profile_color, profile_picked_time_on_startup_,
-      base::BindOnce(
-          &ProfilePickerHandler::OnProfileCreationFinished,
-          // `OnProfileCreationFinished` is called when we want to close the
-          // profile picker. `ProfilePickerHandler` will always be initialized
-          // when we get to that call because the picker will still be open.
-          base::Unretained(this)));
+  ProfileManager::CreateMultiProfileAsync(
+      g_browser_process->profile_manager()
+          ->GetProfileAttributesStorage()
+          .ChooseNameForNewProfile(icon_index),
+      icon_index, /*is_hidden=*/true,
+      base::BindOnce(&ProfilePickerHandler::OnLocalProfileInitialized,
+                     weak_factory_.GetWeakPtr(), profile_color));
 }
 
 void ProfilePickerHandler::HandleGetSwitchProfile(
@@ -767,8 +789,47 @@
   ProfilePicker::CancelSignedInFlow();
 }
 
-void ProfilePickerHandler::OnProfileCreationFinished(
-    bool finished_successfully) {
+void ProfilePickerHandler::OnLocalProfileInitialized(
+    std::optional<SkColor> profile_color,
+    Profile* profile) {
+  if (!profile) {
+    NOTREACHED() << "Local fail in creating new profile";
+    FireWebUIListener("create-profile-finished", base::Value());
+    return;
+  }
+  DCHECK(!signin_util::IsForceSigninEnabled());
+
+  // Apply a new color to the profile or use the default theme.
+  auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
+  if (profile_color.has_value()) {
+    if (features::IsChromeWebuiRefresh2023()) {
+      theme_service->SetUserColorAndBrowserColorVariant(
+          *profile_color, ui::mojom::BrowserColorVariant::kTonalSpot);
+    } else {
+      theme_service->BuildAutogeneratedThemeFromColor(*profile_color);
+    }
+  } else {
+    theme_service->UseDefaultTheme();
+  }
+
+  // TODO(https://crbug.com/1282157): Add shortcut creation.
+
+  // Skip the FRE for this profile as sign-in was offered as part of the flow.
+  profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, true);
+
+  // Launch profile and close the picker.
+  profiles::OpenBrowserWindowForProfile(
+      base::BindOnce(
+          &ProfilePickerHandler::OnSwitchToProfileCompleteOpenCustomization,
+          weak_factory_.GetWeakPtr()),
+      /*always_create=*/false,  // Don't create a window if one already exists.
+      /*is_new_profile=*/true,  // Create a first run window.
+      /*unblock_extensions=*/false,  // There is no need to unblock all
+                                     // extensions because we only open browser
+                                     // window if the Profile is not locked.
+                                     // Hence there is no extension blocked.
+      profile);
+
   FireWebUIListener("create-profile-finished", base::Value());
 }
 
@@ -1002,6 +1063,36 @@
   ProfilePicker::Hide();
 }
 
+void ProfilePickerHandler::OnSwitchToProfileCompleteOpenCustomization(
+    Browser* browser) {
+  if (!browser) {
+    // TODO(crbug.com/1374315): Make sure we do something or log an error if
+    // opening a browser window was not possible.
+    return;
+  }
+
+  DCHECK(browser->window());
+  Profile* profile = browser->profile();
+
+  TRACE_EVENT1(
+      "browser",
+      "ProfilePickerHandler::OnSwitchToProfileCompleteOpenCustomization",
+      "profile_path", profile->GetPath().AsUTF8Unsafe());
+
+  // Measure startup time to display first web contents if the profile picker
+  // was displayed on startup and if the initiating action is instrumented. For
+  // example we don't record pick time for profile creations.
+  if (!profile_picked_time_on_startup_.is_null()) {
+    BeginFirstWebContentsProfiling(browser, profile_picked_time_on_startup_);
+  }
+
+  browser->signin_view_controller()->ShowModalProfileCustomizationDialog(
+      /*is_local_profile_creation=*/true);
+
+  RecordProfilePickerAction(ProfilePickerAction::kLaunchNewProfile);
+  ProfilePicker::Hide();
+}
+
 void ProfilePickerHandler::PushProfilesList() {
   DCHECK(IsJavascriptAllowed());
   FireWebUIListener("profiles-list-changed", GetProfilesList());
@@ -1168,29 +1259,6 @@
     Observe(nullptr);
 }
 
-// static
-void ProfilePickerHandler::BeginFirstWebContentsProfiling(
-    Browser* browser,
-    base::TimeTicks pick_time) {
-  content::WebContents* visible_contents =
-      metrics::FirstWebContentsProfilerBase::GetVisibleContents(browser);
-  if (!visible_contents) {
-    RecordProfilingFinishReason(metrics::StartupProfilingFinishReason::
-                                    kAbandonNoInitiallyVisibleContent);
-    return;
-  }
-
-  if (visible_contents->CompletedFirstVisuallyNonEmptyPaint()) {
-    RecordProfilingFinishReason(
-        metrics::StartupProfilingFinishReason::kAbandonAlreadyPaintedContent);
-    return;
-  }
-
-  // FirstWebContentsProfilerForProfilePicker owns itself and is also bound to
-  // |visible_contents|'s lifetime by observing WebContentsDestroyed().
-  new FirstWebContentsProfilerForProfilePicker(visible_contents, pick_time);
-}
-
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 
 void ProfilePickerHandler::HandleOpenAshAccountSettingsPage(
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.h b/chrome/browser/ui/webui/signin/profile_picker_handler.h
index cc94d12..7cfe3f9 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.h
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.h
@@ -65,12 +65,6 @@
   void OnJavascriptAllowed() override;
   void OnJavascriptDisallowed() override;
 
-  // Measure startup time to display first web contents if the profile picker
-  // was displayed on startup and if the initiating action is instrumented. For
-  // example we don't record pick time for profile creations.
-  static void BeginFirstWebContentsProfiling(Browser* browser,
-                                             base::TimeTicks pick_time);
-
  private:
   friend class ProfilePickerHandlerTest;
   friend class ProfilePickerHandlerInUserProfileTest;
@@ -110,7 +104,10 @@
   void HandleGetNewProfileSuggestedThemeInfo(const base::Value::List& args);
   void HandleGetProfileThemeInfo(const base::Value::List& args);
   void HandleGetAvailableIcons(const base::Value::List& args);
-  void HandleContinueWithoutAccount(const base::Value::List& args);
+  // This function creates a new local profile and opens the profile
+  // customization in a modal dialog.
+  void HandleCreateProfileAndOpenCustomizationDialog(
+      const base::Value::List& args);
 
   // Profile switch screen:
   void HandleGetSwitchProfile(const base::Value::List& args);
@@ -127,8 +124,9 @@
   void OnSwitchToProfileComplete(bool new_profile,
                                  bool open_settings,
                                  Browser* browser);
-
-  void OnProfileCreationFinished(bool finished_successfully);
+  void OnSwitchToProfileCompleteOpenCustomization(Browser* browser);
+  void OnLocalProfileInitialized(std::optional<SkColor> profile_color,
+                                 Profile* profile);
   void PushProfilesList();
   base::Value::List GetProfilesList();
   // Adds a profile with `profile_path` to `profiles_order_`.
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
index 71f997d..67b39b76 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
@@ -343,12 +343,8 @@
 void TabSearchPageHandler::GetTabOrganizationSession(
     GetTabOrganizationSessionCallback callback) {
   Browser* browser = chrome::FindLastActive();
-  if (!browser) {
-    std::move(callback).Run(CreateFailedMojoSession());
-    return;
-  }
-
-  if (!organization_service_) {
+  if (!browser || !browser->tab_strip_model()->SupportsTabGroups() ||
+      !organization_service_) {
     std::move(callback).Run(CreateFailedMojoSession());
     return;
   }
diff --git a/chrome/browser/ui/webui/webui_util.cc b/chrome/browser/ui/webui/webui_util.cc
index 508414f..3b53f1c 100644
--- a/chrome/browser/ui/webui/webui_util.cc
+++ b/chrome/browser/ui/webui/webui_util.cc
@@ -65,7 +65,7 @@
         "chrome://favicon2 chrome://app-icon chrome://extension-icon "
         "chrome://fileicon "
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-        "chrome://chromeos-asset "
+        "chrome://chromeos-asset chrome://userimage "
 #endif
         "blob: data: 'self';");
   }
diff --git a/chrome/browser/user_education/user_education_service.cc b/chrome/browser/user_education/user_education_service.cc
index 3efdad4..c792a53 100644
--- a/chrome/browser/user_education/user_education_service.cc
+++ b/chrome/browser/user_education/user_education_service.cc
@@ -6,7 +6,12 @@
 
 #include <memory>
 
+#include "base/check.h"
 #include "base/feature_list.h"
+#include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/user_education/user_education_service_factory.h"
+#include "components/feature_engagement/public/tracker.h"
 #include "components/user_education/common/feature_promo_registry.h"
 #include "components/user_education/common/feature_promo_session_policy.h"
 #include "components/user_education/common/feature_promo_storage_service.h"
@@ -41,4 +46,55 @@
   }
 }
 
+// static
+bool UserEducationService::MaybeShowNewBadge(content::BrowserContext* context,
+                                             const base::Feature& feature) {
+  auto* const service =
+      UserEducationServiceFactory::GetForBrowserContext(context);
+  if (!service || !service->new_badge_controller()) {
+    return false;
+  }
+
+  // For some tests, browser initialization is never done so there are no
+  // registered "New" Badges.
+  if (!service->new_badge_registry()->IsFeatureRegistered(
+          user_education::features::kNewBadgeTestFeature)) {
+    // Verify that this is actually a testing situation, and then fail.
+    CHECK(Profile::FromBrowserContext(context)->AsTestingProfile());
+    return false;
+  }
+
+  return service->new_badge_controller()->MaybeShowNewBadge(feature);
+}
+
+// static
+void UserEducationService::MaybeNotifyPromoFeatureUsed(
+    content::BrowserContext* context,
+    const base::Feature& feature) {
+  // Do not register events for disabled features.
+  if (!base::FeatureList::IsEnabled(feature)) {
+    return;
+  }
+
+  // Do not register events for profiles incompatible with user education.
+  auto* const service =
+      UserEducationServiceFactory::GetForBrowserContext(context);
+  if (!service || !service->new_badge_controller()) {
+    return;
+  }
+
+  // Notify the "New" Badge controller.
+  service->new_badge_controller()->NotifyFeatureUsedIfValid(feature);
+
+  // Notify the Feature Engagement Tracker if there is a corresponding IPH.
+  // This mirrors logic in FeaturePromoController without having to actually
+  // retrieve a controller from a browser window.
+  if (service->feature_promo_registry().IsFeatureRegistered(feature)) {
+    if (auto* const tracker =
+            feature_engagement::TrackerFactory::GetForBrowserContext(context)) {
+      tracker->NotifyUsedEvent(feature);
+    }
+  }
+}
+
 UserEducationService::~UserEducationService() = default;
diff --git a/chrome/browser/user_education/user_education_service.h b/chrome/browser/user_education/user_education_service.h
index 6f4d1474..d1635137 100644
--- a/chrome/browser/user_education/user_education_service.h
+++ b/chrome/browser/user_education/user_education_service.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/feature_list.h"
 #include "chrome/browser/user_education/browser_tutorial_service.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/user_education/common/feature_promo_registry.h"
@@ -18,6 +19,7 @@
 #include "components/user_education/common/product_messaging_controller.h"
 #include "components/user_education/common/tutorial.h"
 #include "components/user_education/common/tutorial_registry.h"
+#include "content/public/browser/browser_context.h"
 
 extern const char kTabGroupTutorialId[];
 extern const char kSavedTabGroupTutorialId[];
@@ -64,6 +66,19 @@
     return new_badge_controller_.get();
   }
 
+  // Utility methods for when a browser [window] isn't available; for example,
+  // when only a WebContents is available:
+
+  // Checks if a "New" Badge should be shown for the given `context` (or
+  // profile), for `feature`.
+  static bool MaybeShowNewBadge(content::BrowserContext* context,
+                                const base::Feature& feature);
+
+  // Notifies that a feature associated with an IPH or "New" Badge was used in
+  // `context` (or profile), but only if the context supports user education.
+  static void MaybeNotifyPromoFeatureUsed(content::BrowserContext* context,
+                                          const base::Feature& feature);
+
  private:
   user_education::TutorialRegistry tutorial_registry_;
   user_education::HelpBubbleFactoryRegistry help_bubble_factory_registry_;
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
index 368613e..e84000e 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -1731,7 +1731,7 @@
 
   std::vector<std::string> policy_ids;
 
-  if (std::optional<base::StringPiece> preinstalled_web_app_policy_id =
+  if (std::optional<std::string_view> preinstalled_web_app_policy_id =
           apps_util::GetPolicyIdForPreinstalledWebApp(app_id)) {
     policy_ids.emplace_back(*preinstalled_web_app_policy_id);
   }
@@ -1742,7 +1742,7 @@
     const auto& swa_data = web_app.client_data().system_web_app_data;
     DCHECK(swa_data);
     const ash::SystemWebAppType swa_type = swa_data->system_app_type;
-    const std::optional<base::StringPiece> swa_policy_id =
+    const std::optional<std::string_view> swa_policy_id =
         apps_util::GetPolicyIdForSystemWebAppType(swa_type);
     if (swa_policy_id) {
       policy_ids.emplace_back(*swa_policy_id);
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsertest.cc
index 4af465c1..663b484 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsertest.cc
@@ -272,7 +272,10 @@
   builder.AddExchange(
       "/manifest.webmanifest",
       {{":status", "200"}, {"content-type", "application/manifest+json"}},
-      ManifestBuilder().SetName("fallback manifest").ToJson());
+      ManifestBuilder()
+          .AddIcon("/icon.png", gfx::Size(256, 256), "image/png")
+          .SetName("fallback manifest")
+          .ToJson());
   builder.AddExchange("/", {{":status", "200"}, {"content-type", "text/html"}},
                       "Test html");
   builder.AddExchange("/icon.png",
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsing_data_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsing_data_browsertest.cc
index 2ae32bc..c333e9d2 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsing_data_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsing_data_browsertest.cc
@@ -660,8 +660,14 @@
   EXPECT_THAT(GetIwaUsage(url_info2), 0);
 }
 
+// TODO(crbug.com/1504250): Re-enable this test
+#if BUILDFLAG(IS_LINUX)
+#define MAYBE_ClearBrowserDataTimeRanged DISABLED_ClearBrowserDataTimeRanged
+#else
+#define MAYBE_ClearBrowserDataTimeRanged ClearBrowserDataTimeRanged
+#endif
 IN_PROC_BROWSER_TEST_F(IsolatedWebAppBrowsingDataClearingTest,
-                       ClearBrowserDataTimeRanged) {
+                       MAYBE_ClearBrowserDataTimeRanged) {
   auto cache_test_server = std::make_unique<net::EmbeddedTestServer>();
   cache_test_server->AddDefaultHandlers(
       base::FilePath(FILE_PATH_LITERAL("content/test/data")));
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_browsertest.cc
index 0d712b1..0874bd11 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_browsertest.cc
@@ -173,12 +173,10 @@
         });
       )");
     base::FilePath bundle_304_path = temp_dir_.Append(kBundle304FileName);
-    std::vector<uint8_t> bundle_304_contents =
-        builder.BuildInMemoryBundle(key_pair);
-    CHECK(base::WriteFile(bundle_304_path, bundle_304_contents));
+    bundle_304_ = builder.BuildBundle(bundle_304_path, key_pair);
 
     base::FilePath bundle_706_path = temp_dir_.Append(kBundle706FileName);
-    std::vector<uint8_t> bundle_706_contents =
+    bundle_706_ =
         IsolatedWebAppBuilder(
             ManifestBuilder().SetName("app-7.0.6").SetVersion("7.0.6"))
             .AddHtml("/", R"(
@@ -189,8 +187,7 @@
                   <h1>Hello from version 7.0.6</h1>
                 </body>
             )")
-            .BuildInMemoryBundle(key_pair);
-    CHECK(base::WriteFile(bundle_706_path, bundle_706_contents));
+            .BuildBundle(bundle_706_path, key_pair);
 
     EXPECT_TRUE(base::WriteFile(
         temp_dir_.Append(kUpdateManifestFileName),
@@ -213,6 +210,8 @@
   std::optional<IsolatedWebAppUrlInfo> url_info_;
   base::FilePath temp_dir_;
   net::EmbeddedTestServer iwa_server_;
+  std::unique_ptr<BundledIsolatedWebApp> bundle_304_;
+  std::unique_ptr<BundledIsolatedWebApp> bundle_706_;
 };
 
 IN_PROC_BROWSER_TEST_F(IsolatedWebAppUpdateManagerBrowserTest, Succeeds) {
diff --git a/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.cc b/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.cc
index ac3c4ac..593c6278 100644
--- a/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.cc
@@ -17,6 +17,8 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/to_string.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/test_future.h"
 #include "base/threading/thread_restrictions.h"
@@ -41,6 +43,9 @@
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
+#include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h"
+#include "third_party/blink/public/common/permissions_policy/permissions_policy_declaration.h"
+#include "third_party/blink/public/common/permissions_policy/policy_helper_public.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkStream.h"
@@ -77,9 +82,12 @@
   for (const blink::Manifest::ImageResource& icon : blink_manifest->icons) {
     FakeWebContentsManager::FakeIconState& icon_state =
         fake_web_contents_manager.GetOrCreateIconState(icon.src);
-    // For now we use a placeholder square icon rather than reading the icons
+    // For now we use a placeholder icon rather than reading the icons
     // from the app.
-    icon_state.bitmaps = {CreateSquareIcon(256, SK_ColorWHITE)};
+    icon_state.bitmaps = {SkBitmap()};
+    icon_state.bitmaps[0].allocN32Pixels(icon.sizes[0].width(),
+                                         icon.sizes[0].height());
+    icon_state.bitmaps[0].eraseColor(SK_ColorWHITE);
   }
 
   GURL install_url = base_url.Resolve(kInstallPagePath);
@@ -216,16 +224,6 @@
 
 ScopedProxyIsolatedWebApp::~ScopedProxyIsolatedWebApp() = default;
 
-void ScopedProxyIsolatedWebApp::FakeInstallPageState(
-    Profile* profile,
-    const web_package::SignedWebBundleId& web_bundle_id) {
-  CHECK(manifest_builder_.has_value());
-  auto url_info =
-      IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(web_bundle_id);
-  ::web_app::FakeInstallPageState(
-      profile, url_info, manifest_builder_->ToBlinkManifest(url_info.origin()));
-}
-
 IsolatedWebAppUrlInfo ScopedProxyIsolatedWebApp::InstallChecked(
     Profile* profile) {
   auto result = Install(profile);
@@ -248,12 +246,25 @@
                                 IwaSourceProxy(proxy_server_->GetOrigin())));
 }
 
+ManifestBuilder::PermissionsPolicy::PermissionsPolicy(
+    bool wildcard,
+    bool self,
+    std::vector<url::Origin> origins)
+    : wildcard(wildcard), self(self), origins(origins) {
+  CHECK(!(wildcard && self));
+  CHECK(!(wildcard && !origins.empty()));
+}
+
+ManifestBuilder::PermissionsPolicy::PermissionsPolicy(
+    const ManifestBuilder::PermissionsPolicy&) = default;
+ManifestBuilder::PermissionsPolicy::~PermissionsPolicy() = default;
+
 ManifestBuilder::ManifestBuilder()
-    : name_("Test App"),
-      version_("0.0.1"),
-      start_url_("/"),
-      permissions_policy_{{"cross-origin-isolated", {"self"}}},
-      icon_paths_{"/icon.png"} {}
+    : name_("Test App"), version_("0.0.1"), start_url_("/") {
+  AddPermissionsPolicy(
+      blink::mojom::PermissionsPolicyFeature::kCrossOriginIsolated,
+      /*self=*/true, /*origins=*/{});
+}
 
 ManifestBuilder::ManifestBuilder(const ManifestBuilder&) = default;
 ManifestBuilder::~ManifestBuilder() = default;
@@ -273,15 +284,30 @@
   return *this;
 }
 
-ManifestBuilder& ManifestBuilder::AddPermissionsPolicy(
-    std::string_view name,
-    std::vector<std::string> value) {
-  permissions_policy_[std::string(name)] = value;
+ManifestBuilder& ManifestBuilder::AddIcon(std::string_view resource_path,
+                                          gfx::Size size,
+                                          std::string_view content_type) {
+  icons_.emplace_back(std::string(resource_path), size,
+                      std::string(content_type));
   return *this;
 }
 
-ManifestBuilder& ManifestBuilder::AddIcon(std::string_view resource_path) {
-  icon_paths_.emplace_back(resource_path);
+ManifestBuilder& ManifestBuilder::AddPermissionsPolicyWildcard(
+    blink::mojom::PermissionsPolicyFeature feature) {
+  permissions_policy_.insert_or_assign(
+      feature,
+      ManifestBuilder::PermissionsPolicy(/*wildcard=*/true, /*self=*/false,
+                                         /*origins=*/{}));
+  return *this;
+}
+
+ManifestBuilder& ManifestBuilder::AddPermissionsPolicy(
+    blink::mojom::PermissionsPolicyFeature feature,
+    bool self,
+    std::vector<url::Origin> origins) {
+  permissions_policy_.insert_or_assign(
+      feature,
+      ManifestBuilder::PermissionsPolicy(/*wildcard=*/false, self, origins));
   return *this;
 }
 
@@ -291,12 +317,20 @@
   return *this;
 }
 
+ManifestBuilder& ManifestBuilder::AddFileHandler(
+    std::string_view action,
+    const FileHandlerAccept& accept) {
+  file_handlers_[std::string(action)] = accept;
+  return *this;
+}
+
 const std::string& ManifestBuilder::start_url() const {
   return start_url_;
 }
 
-const std::vector<std::string>& ManifestBuilder::icon_paths() const {
-  return icon_paths_;
+const std::vector<ManifestBuilder::IconMetadata>& ManifestBuilder::icons()
+    const {
+  return icons_;
 }
 
 std::string ManifestBuilder::ToJson() const {
@@ -311,20 +345,29 @@
   base::Value::Dict policies;
   for (const auto& policy : permissions_policy_) {
     base::Value::List values;
-    for (const auto& value : policy.second) {
-      values.Append(value);
+    if (policy.second.wildcard) {
+      values.Append("*");
     }
-    policies.Set(policy.first, std::move(values));
+    if (policy.second.self) {
+      values.Append("self");
+    }
+    for (const auto& origin : policy.second.origins) {
+      values.Append(origin.Serialize());
+    }
+    std::string_view feature_name =
+        blink::GetPermissionsPolicyFeatureToNameMap().at(policy.first);
+    policies.Set(feature_name, std::move(values));
   }
   json.Set("permissions_policy", std::move(policies));
 
   base::Value::List icons;
-  for (const auto& icon_path : icon_paths_) {
-    // For now we just hardcode the icon size to 256x256.
-    icons.Append(base::Value::Dict()
-                     .Set("src", icon_path)
-                     .Set("sizes", "256x256")
-                     .Set("type", "image/png"));
+  for (const auto& icon : icons_) {
+    icons.Append(
+        base::Value::Dict()
+            .Set("src", icon.resource_path)
+            .Set("sizes", base::StringPrintf("%dx%d", icon.size.width(),
+                                             icon.size.height()))
+            .Set("type", icon.content_type));
   }
   json.Set("icons", std::move(icons));
 
@@ -336,6 +379,24 @@
   }
   json.Set("protocol_handlers", std::move(protocol_handlers));
 
+  if (!file_handlers_.empty()) {
+    base::Value::List file_handlers;
+    for (const auto& handler_entry : file_handlers_) {
+      base::Value::Dict accept;
+      for (const auto& accept_entry : handler_entry.second) {
+        base::Value::List extensions;
+        for (const auto& extension : accept_entry.second) {
+          extensions.Append(extension);
+        }
+        accept.Set(accept_entry.first, std::move(extensions));
+      }
+      file_handlers.Append(base::Value::Dict()
+                               .Set("action", handler_entry.first)
+                               .Set("accept", std::move(accept)));
+    }
+    json.Set("file_handlers", std::move(file_handlers));
+  }
+
   return base::WriteJsonWithOptions(json, base::OPTIONS_PRETTY_PRINT).value();
 }
 
@@ -350,13 +411,13 @@
   manifest->start_url = base_url.Resolve(start_url_);
   manifest->display = blink::mojom::DisplayMode::kStandalone;
 
-  for (const auto& icon_path : icon_paths_) {
-    blink::Manifest::ImageResource icon;
-    icon.purpose = {blink::mojom::ManifestImageResource_Purpose::ANY};
-    icon.src = base_url.Resolve(icon_path);
-    icon.type = u"image/png";
-    icon.sizes.push_back(gfx::Size(256, 256));
-    manifest->icons.push_back(icon);
+  for (const auto& icon : icons_) {
+    blink::Manifest::ImageResource blink_icon;
+    blink_icon.purpose = {blink::mojom::ManifestImageResource_Purpose::ANY};
+    blink_icon.src = base_url.Resolve(icon.resource_path);
+    blink_icon.type = base::UTF8ToUTF16(icon.content_type);
+    blink_icon.sizes.push_back(icon.size);
+    manifest->icons.push_back(blink_icon);
   }
 
   for (const auto& protocol_handler_pair : protocol_handlers_) {
@@ -367,23 +428,52 @@
     manifest->protocol_handlers.push_back(std::move(protocol_handler));
   }
 
-  // Permissions policy isn't included here as it's not needed by anything
-  // yet and is tricky to parse.
+  for (const auto& policy : permissions_policy_) {
+    blink::ParsedPermissionsPolicyDeclaration decl;
+    decl.feature = policy.first;
+    if (policy.second.wildcard) {
+      decl.matches_all_origins = true;
+    }
+    if (policy.second.self) {
+      decl.self_if_matches = url::Origin::Create(base_url);
+    }
+    for (const auto& origin : policy.second.origins) {
+      decl.allowed_origins.push_back(
+          blink::OriginWithPossibleWildcards::FromOrigin(origin).value());
+    }
+    manifest->permissions_policy.push_back(decl);
+  }
+
+  for (const auto& file_handler : file_handlers_) {
+    base::flat_map<std::u16string, std::vector<std::u16string>> accept;
+    for (const auto& accept_entry : file_handler.second) {
+      std::vector<std::u16string>& extensions =
+          accept[base::UTF8ToUTF16(accept_entry.first)];
+      for (const auto& extension : accept_entry.second) {
+        extensions.push_back(base::UTF8ToUTF16(extension));
+      }
+    }
+    auto handler = blink::mojom::ManifestFileHandler::New();
+    handler->action = GURL(file_handler.first);
+    handler->accept = accept;
+    manifest->file_handlers.push_back(std::move(handler));
+  }
 
   return manifest;
 }
 
 IsolatedWebAppBuilder::Resource::Resource(
+    net::HttpStatusCode status,
     const IsolatedWebAppBuilder::Headers& headers,
     const IsolatedWebAppBuilder::ResourceBody& body)
-    : headers_(headers), body_(body) {}
+    : status_(status), headers_(headers), body_(body) {}
 
 IsolatedWebAppBuilder::Resource::Resource(
     const IsolatedWebAppBuilder::Resource&) = default;
 IsolatedWebAppBuilder::Resource::~Resource() = default;
 
 scoped_refptr<net::HttpResponseHeaders>
-IsolatedWebAppBuilder::Resource::headers() const {
+IsolatedWebAppBuilder::Resource::headers(std::string_view resource_path) const {
   scoped_refptr<net::HttpResponseHeaders> http_headers;
 
   if (const base::FilePath* path = absl::get_if<base::FilePath>(&body_)) {
@@ -410,10 +500,23 @@
     http_headers->AddHeader(header.name, header.value);
   }
 
-  if (!has_content_type && absl::holds_alternative<base::FilePath>(body_)) {
-    http_headers->AddHeader(
-        net::HttpRequestHeaders::kContentType,
-        net::test_server::GetContentType(absl::get<base::FilePath>(body_)));
+  if (!has_content_type) {
+    base::FilePath file_path =
+        absl::visit(base::Overloaded{
+                        [&](const std::string&) {
+                          return base::FilePath::FromUTF8Unsafe(resource_path);
+                        },
+                        [&](const base::FilePath& path) { return path; },
+                    },
+                    body_);
+    std::string content_type = net::test_server::GetContentType(file_path);
+    if (content_type.empty()) {
+      LOG(WARNING) << "Could not infer the Content-Type of " << file_path
+                   << ". Falling back to application/octet-stream.";
+      content_type = "application/octet-stream";
+    }
+    http_headers->AddHeader(net::HttpRequestHeaders::kContentType,
+                            content_type);
   }
   return http_headers;
 }
@@ -434,7 +537,7 @@
     const ManifestBuilder& manifest_builder)
     : manifest_builder_(manifest_builder) {
   AddHtml("/", "Test Isolated Web App");
-  AddImageAsPng("/icon.png", CreateSquareIcon(256, SK_ColorBLUE));
+  AddIconAsPng("/icon.png", CreateSquareIcon(256, SK_ColorBLUE));
 }
 
 IsolatedWebAppBuilder::IsolatedWebAppBuilder(const IsolatedWebAppBuilder&) =
@@ -457,11 +560,12 @@
 IsolatedWebAppBuilder& IsolatedWebAppBuilder::AddResource(
     std::string_view resource_path,
     std::string_view content,
-    const Headers& headers) {
+    const Headers& headers,
+    net::HttpStatusCode status) {
   CHECK(resource_path != kManifestPath)
       << "The manifest must be specified through the ManifestBuilder";
   resources_.insert_or_assign(std::string(resource_path),
-                              Resource(headers, std::string(content)));
+                              Resource(status, headers, std::string(content)));
   return *this;
 }
 
@@ -477,6 +581,14 @@
   return AddResource(resource_path, content, "text/javascript");
 }
 
+IsolatedWebAppBuilder& IsolatedWebAppBuilder::AddIconAsPng(
+    std::string_view resource_path,
+    const SkBitmap& image) {
+  manifest_builder_.AddIcon(
+      resource_path, gfx::Size(image.width(), image.height()), "image/png");
+  return AddImageAsPng(resource_path, image);
+}
+
 IsolatedWebAppBuilder& IsolatedWebAppBuilder::AddImageAsPng(
     std::string_view resource_path,
     const SkBitmap& image) {
@@ -492,15 +604,17 @@
     std::string_view resource_path,
     const base::FilePath& file_path,
     const Headers& headers) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
   CHECK(base::PathExists(file_path)) << file_path << " does not exist";
   resources_.insert_or_assign(std::string(resource_path),
-                              Resource(headers, file_path));
+                              Resource(net::HTTP_OK, headers, file_path));
   return *this;
 }
 
 IsolatedWebAppBuilder& IsolatedWebAppBuilder::AddFolderFromDisk(
     std::string_view resource_path,
     const base::FilePath& folder_path) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
   base::FileEnumerator files(folder_path, /*recursive=*/true,
                              base::FileEnumerator::FILES);
   for (base::FilePath path = files.Next(); !path.empty(); path = files.Next()) {
@@ -569,13 +683,15 @@
 
 std::vector<uint8_t> IsolatedWebAppBuilder::BuildInMemoryBundle(
     const web_package::WebBundleSigner::KeyPair& key_pair) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
   Validate();
   web_package::WebBundleBuilder builder;
   for (const auto& resource : resources_) {
-    scoped_refptr<net::HttpResponseHeaders> headers = resource.second.headers();
+    scoped_refptr<net::HttpResponseHeaders> headers =
+        resource.second.headers(resource.first);
 
     web_package::WebBundleBuilder::Headers bundle_headers = {
-        {":status", "200"}};
+        {":status", base::ToString(resource.second.status())}};
     size_t iterator = 0;
     std::string name;
     std::string value;
@@ -598,13 +714,15 @@
 }
 
 void IsolatedWebAppBuilder::Validate() {
-  CHECK(resources_.find(manifest_builder_.start_url()) != resources_.end())
-      << "Resource at 'start_url' (" << manifest_builder_.start_url()
-      << ") does not exist";
+  if (resources_.find(manifest_builder_.start_url()) == resources_.end()) {
+    LOG(WARNING) << "Resource at 'start_url' (" << manifest_builder_.start_url()
+                 << ") does not exist";
+  }
 
-  for (const auto& icon_path : manifest_builder_.icon_paths()) {
-    CHECK(resources_.find(icon_path) != resources_.end())
-        << "Icon at '" << icon_path << "' does not exist";
+  for (const auto& icon : manifest_builder_.icons()) {
+    if (resources_.find(icon.resource_path) == resources_.end()) {
+      LOG(WARNING) << "Icon at '" << icon.resource_path << "' does not exist";
+    }
   }
 }
 
@@ -614,6 +732,7 @@
     const ManifestBuilder& manifest_builder,
     const std::map<std::string, Resource>& resources,
     const net::test_server::HttpRequest& request) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
   auto response = std::make_unique<net::test_server::BasicHttpResponse>();
   std::string path = request.GetURL().path();
   if (path == kManifestPath) {
@@ -622,11 +741,11 @@
     response->set_content(manifest_builder.ToJson());
   } else if (const auto resource = resources.find(path);
              resource != resources.end()) {
-    response->set_code(net::HTTP_OK);
+    response->set_code(resource->second.status());
     response->set_content(resource->second.body());
 
     scoped_refptr<net::HttpResponseHeaders> headers =
-        resource->second.headers();
+        resource->second.headers(resource->first);
 
     size_t iterator = 0;
     std::string name;
diff --git a/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h b/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h
index e86216b..616095f 100644
--- a/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h
+++ b/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h
@@ -19,8 +19,12 @@
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
 #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
 #include "components/web_package/test_support/signed_web_bundles/web_bundle_signer.h"
+#include "net/http/http_status_code.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h"
+#include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-forward.h"
+#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-forward.h"
+#include "ui/gfx/geometry/size.h"
 
 class Profile;
 class SkBitmap;
@@ -45,6 +49,27 @@
 // A builder for a subset of the Web Manifest spec.
 class ManifestBuilder {
  public:
+  struct IconMetadata {
+    std::string resource_path;
+    gfx::Size size;
+    std::string content_type;
+  };
+
+  struct PermissionsPolicy {
+    PermissionsPolicy(bool wildcard,
+                      bool self,
+                      std::vector<url::Origin> origins);
+    PermissionsPolicy(const PermissionsPolicy&);
+    ~PermissionsPolicy();
+
+    bool wildcard;
+    bool self;
+    std::vector<url::Origin> origins;
+  };
+
+  // Mime type to vector of file extensions.
+  using FileHandlerAccept = std::map<std::string, std::vector<std::string>>;
+
   // Creates the following default manifest:
   // {
   //   name: "Test App",
@@ -55,13 +80,6 @@
   //   permissions_policy: {
   //     cross-origin-isolated: ["self"]
   //   },
-  //   icons: [
-  //     {
-  //       src: "/icon.png",
-  //       sizes: "256x256",
-  //       type: "image/png"
-  //     }
-  //   ]
   // }
   ManifestBuilder();
   ManifestBuilder(const ManifestBuilder&);
@@ -71,17 +89,26 @@
   ManifestBuilder& SetName(std::string_view name);
   ManifestBuilder& SetVersion(std::string_view version);
   ManifestBuilder& SetStartUrl(std::string_view start_url);
-  ManifestBuilder& AddPermissionsPolicy(std::string_view name,
-                                        std::vector<std::string> value);
-  ManifestBuilder& AddIcon(std::string_view resource_path);
+  ManifestBuilder& AddIcon(std::string_view resource_path,
+                           gfx::Size size,
+                           std::string_view content_type);
+
+  ManifestBuilder& AddPermissionsPolicyWildcard(
+      blink::mojom::PermissionsPolicyFeature feature);
+  ManifestBuilder& AddPermissionsPolicy(
+      blink::mojom::PermissionsPolicyFeature feature,
+      bool self,
+      std::vector<url::Origin> origins);
+
   ManifestBuilder& AddProtocolHandler(std::string_view protocol,
                                       std::string_view url);
+  ManifestBuilder& AddFileHandler(std::string_view action,
+                                  const FileHandlerAccept& accept);
 
-  // TODO: Other manifest fields like file_handlers,
-  // share_target as needed by tests.
+  // TODO: Other manifest fields like share_target as needed by tests.
 
   const std::string& start_url() const;
-  const std::vector<std::string>& icon_paths() const;
+  const std::vector<IconMetadata>& icons() const;
 
   std::string ToJson() const;
   blink::mojom::ManifestPtr ToBlinkManifest(
@@ -91,9 +118,11 @@
   std::string name_;
   std::string version_;
   std::string start_url_;
-  std::map<std::string, std::vector<std::string>> permissions_policy_;
-  std::vector<std::string> icon_paths_;
+  std::vector<IconMetadata> icons_;
+  std::map<blink::mojom::PermissionsPolicyFeature, PermissionsPolicy>
+      permissions_policy_;
   std::vector<std::pair<std::string, std::string>> protocol_handlers_;
+  std::map<std::string, FileHandlerAccept> file_handlers_;
 };
 
 class BundledIsolatedWebApp {
@@ -157,10 +186,6 @@
 
   net::EmbeddedTestServer& proxy_server() { return *proxy_server_; }
 
-  void FakeInstallPageState(
-      Profile* profile,
-      const web_package::SignedWebBundleId& web_bundle_id);
-
   IsolatedWebAppUrlInfo InstallChecked(Profile* profile);
 
   base::expected<IsolatedWebAppUrlInfo, std::string> Install(Profile* profile);
@@ -212,7 +237,8 @@
   // be included in the list of headers.
   IsolatedWebAppBuilder& AddResource(std::string_view resource_path,
                                      std::string_view content,
-                                     const Headers& headers);
+                                     const Headers& headers,
+                                     net::HttpStatusCode status = net::HTTP_OK);
 
   // Adds a text/html type resource to the app.
   IsolatedWebAppBuilder& AddHtml(std::string_view resource_path,
@@ -222,6 +248,11 @@
   IsolatedWebAppBuilder& AddJs(std::string_view resource_path,
                                std::string_view content);
 
+  // Adds a image/png type resource to the app, and adds it as an icon in the
+  // manifest.
+  IsolatedWebAppBuilder& AddIconAsPng(std::string_view resource_path,
+                                      const SkBitmap& image);
+
   // Adds a image/png type resource to the app.
   IsolatedWebAppBuilder& AddImageAsPng(std::string_view resource_path,
                                        const SkBitmap& image);
@@ -277,25 +308,24 @@
       const base::FilePath& bundle_path,
       const web_package::WebBundleSigner::KeyPair& key_pair);
 
-  // Creates and signs a .swbn file and returns its serialized contents.
-  //
-  // Prefer the BuildBundle overloads that return a ScopedBundledIsolatedWebApp.
-  std::vector<uint8_t> BuildInMemoryBundle(
-      const web_package::WebBundleSigner::KeyPair& key_pair);
-
  private:
   using ResourceBody = absl::variant<base::FilePath, std::string>;
 
   class Resource {
    public:
-    Resource(const Headers& headers, const ResourceBody& body);
+    Resource(net::HttpStatusCode status,
+             const Headers& headers,
+             const ResourceBody& body);
     Resource(const Resource&);
     ~Resource();
 
-    scoped_refptr<net::HttpResponseHeaders> headers() const;
+    net::HttpStatusCode status() const { return status_; }
+    scoped_refptr<net::HttpResponseHeaders> headers(
+        std::string_view resource_path) const;
     std::string body() const;
 
    private:
+    net::HttpStatusCode status_;
     Headers headers_;
     ResourceBody body_;
   };
@@ -305,6 +335,9 @@
       const std::map<std::string, Resource>& resources,
       const net::test_server::HttpRequest& request);
 
+  std::vector<uint8_t> BuildInMemoryBundle(
+      const web_package::WebBundleSigner::KeyPair& key_pair);
+
   void Validate();
 
   ManifestBuilder manifest_builder_;
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.cc b/chrome/browser/web_applications/test/web_app_test_utils.cc
index 8ec3f5e..98bc26a 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_test_utils.cc
@@ -57,6 +57,7 @@
 #include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "components/base32/base32.h"
 #include "components/prefs/pref_service.h"
@@ -85,7 +86,7 @@
 
 namespace {
 
-std::vector<std::string> features = {
+std::vector<std::string> test_features = {
     "default_on_feature", "default_self_feature", "default_disabled_feature"};
 
 }  // namespace
@@ -206,9 +207,9 @@
 blink::ParsedPermissionsPolicy CreateRandomPermissionsPolicy(
     RandomHelper& random) {
   const int num_permissions_policy_declarations =
-      random.next_uint(features.size());
+      random.next_uint(test_features.size());
 
-  std::vector<std::string> available_features = features;
+  std::vector<std::string> available_features = test_features;
 
   const auto suffix = random.next_uint();
   std::default_random_engine rng;
@@ -996,16 +997,20 @@
                                 false /*accept*/, std::move(web_app_info)));
 }
 
+// TODO(b/329703817): Make this smarter by waiting for a specific dialog, and
+// then triggering accept on the dialog.
 webapps::AppId InstallPwaForCurrentUrl(Browser* browser) {
   // Depending on the installability criteria, different dialogs can be used.
   SetAutoAcceptWebAppDialogForTesting(true, true);
   SetAutoAcceptPWAInstallConfirmationForTesting(true);
+  SetAutoAcceptDiyAppsInstallDialogForTesting(true);
   WebAppTestInstallWithOsHooksObserver observer(browser->profile());
   observer.BeginListening();
   CHECK(chrome::ExecuteCommand(browser, IDC_INSTALL_PWA));
   webapps::AppId app_id = observer.Wait();
   SetAutoAcceptPWAInstallConfirmationForTesting(false);
   SetAutoAcceptWebAppDialogForTesting(false, false);
+  SetAutoAcceptDiyAppsInstallDialogForTesting(false);
   return app_id;
 }
 
diff --git a/chrome/browser/web_applications/user_display_mode.cc b/chrome/browser/web_applications/user_display_mode.cc
index 95957af..92b39f1 100644
--- a/chrome/browser/web_applications/user_display_mode.cc
+++ b/chrome/browser/web_applications/user_display_mode.cc
@@ -11,8 +11,7 @@
 
 namespace web_app {
 
-sync_pb::WebAppSpecifics::UserDisplayMode
-ConvertUserDisplayModeToWebAppSpecificsUserDisplayMode(
+sync_pb::WebAppSpecifics::UserDisplayMode ToWebAppSpecificsUserDisplayMode(
     mojom::UserDisplayMode user_display_mode) {
   switch (user_display_mode) {
     case mojom::UserDisplayMode::kBrowser:
@@ -24,7 +23,7 @@
   }
 }
 
-mojom::UserDisplayMode CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
+mojom::UserDisplayMode ToMojomUserDisplayMode(
     sync_pb::WebAppSpecifics::UserDisplayMode display_mode) {
   switch (display_mode) {
     case sync_pb::WebAppSpecifics::BROWSER:
@@ -41,8 +40,7 @@
 mojom::UserDisplayMode ResolvePlatformSpecificUserDisplayMode(
     const sync_pb::WebAppSpecifics& sync_proto) {
   if (!base::FeatureList::IsEnabled(kSeparateUserDisplayModeForCrOS)) {
-    return CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
-        sync_proto.user_display_mode_default());
+    return ToMojomUserDisplayMode(sync_proto.user_display_mode_default());
   }
 
   sync_pb::WebAppSpecifics_UserDisplayMode user_display_mode;
@@ -54,8 +52,7 @@
   // Defaults to UNSPECIFIED, which will be converted to kStandalone.
   user_display_mode = sync_proto.user_display_mode_default();
 #endif  // BUILDFLAG(IS_CHROMEOS)
-  return CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
-      user_display_mode);
+  return ToMojomUserDisplayMode(user_display_mode);
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/user_display_mode.h b/chrome/browser/web_applications/user_display_mode.h
index 0f42f69..fdea46b 100644
--- a/chrome/browser/web_applications/user_display_mode.h
+++ b/chrome/browser/web_applications/user_display_mode.h
@@ -10,11 +10,10 @@
 
 namespace web_app {
 
-sync_pb::WebAppSpecifics::UserDisplayMode
-ConvertUserDisplayModeToWebAppSpecificsUserDisplayMode(
+sync_pb::WebAppSpecifics::UserDisplayMode ToWebAppSpecificsUserDisplayMode(
     mojom::UserDisplayMode user_display_mode);
 
-mojom::UserDisplayMode CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
+mojom::UserDisplayMode ToMojomUserDisplayMode(
     sync_pb::WebAppSpecifics::UserDisplayMode display_mode);
 
 // Get the platform-specific UserDisplayMode field in the `sync_proto` based on
diff --git a/chrome/browser/web_applications/web_app_database.cc b/chrome/browser/web_applications/web_app_database.cc
index 6b162e1..e25f54b 100644
--- a/chrome/browser/web_applications/web_app_database.cc
+++ b/chrome/browser/web_applications/web_app_database.cc
@@ -1001,21 +1001,18 @@
   if (base::FeatureList::IsEnabled(kSeparateUserDisplayModeForCrOS)) {
     if (sync_data.has_user_display_mode_cros()) {
       web_app->SetUserDisplayModeCrOS(
-          CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
-              sync_data.user_display_mode_cros()));
+          ToMojomUserDisplayMode(sync_data.user_display_mode_cros()));
     }
     if (sync_data.has_user_display_mode_default()) {
       web_app->SetUserDisplayModeDefault(
-          CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
-              sync_data.user_display_mode_default()));
+          ToMojomUserDisplayMode(sync_data.user_display_mode_default()));
     }
     // Note: migration runs after database opened to ensure the current platform
     // always has a UserDisplayMode set (see
     // `EnsureAppsHaveUserDisplayModeForCurrentPlatform`).
   } else {
     web_app->SetUserDisplayModeDefault(
-        CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
-            sync_data.user_display_mode_default()));
+        ToMojomUserDisplayMode(sync_data.user_display_mode_default()));
   }
 
   // Ordinals used for chrome://apps page.
diff --git a/chrome/browser/web_applications/web_app_proto_utils.cc b/chrome/browser/web_applications/web_app_proto_utils.cc
index 21d1d56..d8384ae 100644
--- a/chrome/browser/web_applications/web_app_proto_utils.cc
+++ b/chrome/browser/web_applications/web_app_proto_utils.cc
@@ -248,19 +248,16 @@
   if (base::FeatureList::IsEnabled(kSeparateUserDisplayModeForCrOS) ||
       base::FeatureList::IsEnabled(kSyncOnlySeparateUserDisplayModeForCrOS)) {
     if (app.user_display_mode_cros()) {
-      sync_proto.set_user_display_mode_cros(
-          ConvertUserDisplayModeToWebAppSpecificsUserDisplayMode(
-              app.user_display_mode_cros().value()));
+      sync_proto.set_user_display_mode_cros(ToWebAppSpecificsUserDisplayMode(
+          app.user_display_mode_cros().value()));
     }
     if (app.user_display_mode_default()) {
-      sync_proto.set_user_display_mode_default(
-          ConvertUserDisplayModeToWebAppSpecificsUserDisplayMode(
-              app.user_display_mode_default().value()));
+      sync_proto.set_user_display_mode_default(ToWebAppSpecificsUserDisplayMode(
+          app.user_display_mode_default().value()));
     }
   } else {
-    sync_proto.set_user_display_mode_default(
-        ConvertUserDisplayModeToWebAppSpecificsUserDisplayMode(
-            app.user_display_mode_default().value()));
+    sync_proto.set_user_display_mode_default(ToWebAppSpecificsUserDisplayMode(
+        app.user_display_mode_default().value()));
   }
 
   sync_proto.set_name(app.sync_fallback_data().name);
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.cc b/chrome/browser/web_applications/web_app_sync_bridge.cc
index d0f5797..4454ec7 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge.cc
+++ b/chrome/browser/web_applications/web_app_sync_bridge.cc
@@ -159,13 +159,11 @@
       base::FeatureList::IsEnabled(kSyncOnlySeparateUserDisplayModeForCrOS)) {
     if (sync_data.has_user_display_mode_cros()) {
       app->SetUserDisplayModeCrOS(
-          CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
-              sync_data.user_display_mode_cros()));
+          ToMojomUserDisplayMode(sync_data.user_display_mode_cros()));
     }
     if (sync_data.has_user_display_mode_default()) {
       app->SetUserDisplayModeDefault(
-          CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
-              sync_data.user_display_mode_default()));
+          ToMojomUserDisplayMode(sync_data.user_display_mode_default()));
     }
   }
 
@@ -181,8 +179,7 @@
   } else {
     // Always overwrite the original UserDisplayMode with sync data.
     app->SetUserDisplayMode(
-        CreateUserDisplayModeFromWebAppSpecificsUserDisplayMode(
-            sync_data.user_display_mode_default()));
+        ToMojomUserDisplayMode(sync_data.user_display_mode_default()));
   }
 
   app->SetUserPageOrdinal(syncer::StringOrdinal(sync_data.user_page_ordinal()));
diff --git a/chrome/browser/webauthn/enclave_manager.cc b/chrome/browser/webauthn/enclave_manager.cc
index 5e693a4..a8a7f5ec 100644
--- a/chrome/browser/webauthn/enclave_manager.cc
+++ b/chrome/browser/webauthn/enclave_manager.cc
@@ -81,7 +81,7 @@
 
 struct EnclaveManager::PendingAction {
   EnclaveManager::Callback callback;
-  bool want_registration;
+  bool want_registration = false;
   std::unique_ptr<StoreKeysArgs> store_keys_args;
   bool setup_account = false;
   std::string pin;
@@ -1149,8 +1149,13 @@
             [](std::optional<std::vector<uint8_t>> key_id,
                std::unique_ptr<crypto::UserVerifyingSigningKey> uv_key)
                 -> Event {
+#if BUILDFLAG(IS_WIN)
+              auto provider = crypto::GetUnexportableKeyProvider(
+                  crypto::UnexportableKeyProvider::Config());
+#else
               auto provider =
                   crypto::GetSoftwareUnsecureUnexportableKeyProvider();
+#endif
               if (!provider) {
                 return Failure();
               }
@@ -1908,7 +1913,7 @@
         }
         DCHECK_CALLED_ON_VALID_SEQUENCE(enclave_manager->sequence_checker_);
         if (!key) {
-          // TODO(enclave): The key is gone. Clear registration state.
+          enclave_manager->ClearRegistration();
           std::move(callback).Run(nullptr);
           return;
         }
@@ -1927,8 +1932,16 @@
       base::BindOnce(
           [](std::string wrapped_hardware_private_key)
               -> std::unique_ptr<crypto::UnexportableSigningKey> {
+#if BUILDFLAG(IS_WIN)
+            auto provider = crypto::GetUnexportableKeyProvider(
+                crypto::UnexportableKeyProvider::Config());
+            if (!provider) {
+              return nullptr;
+            }
+#else
             auto provider =
                 crypto::GetSoftwareUnsecureUnexportableKeyProvider();
+#endif
             return provider->FromWrappedSigningKeySlowly(
                 ToVector(wrapped_hardware_private_key));
           },
@@ -2015,7 +2028,7 @@
       crypto::GetUserVerifyingKeyProvider(MakeUserVerifyingKeyConfig());
   if (!user_verifying_key_provider) {
     // This indicates the platform key provider was available, but now is not.
-    // TODO(enclave): Clear registration state.
+    ClearRegistration();
     std::move(callback).Run(nullptr);
     return;
   }
@@ -2038,7 +2051,7 @@
           return;
         }
         if (!key) {
-          // TODO(enclave): The key is gone. Clear registration state.
+          enclave_manager->ClearRegistration();
           std::move(callback).Run(nullptr);
           return;
         }
@@ -2272,6 +2285,11 @@
   return *local_state_;
 }
 
+void EnclaveManager::ClearCachedKeysForTesting() {
+  user_verifying_key_ = nullptr;
+  hardware_key_ = nullptr;
+}
+
 // static
 std::string_view EnclaveManager::recovery_key_store_url_for_testing() {
   return kRecoveryKeyStoreURL;
@@ -2572,3 +2590,23 @@
     std::move(write_finished_callback_).Run();
   }
 }
+
+void EnclaveManager::ClearRegistration() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!user_) {
+    return;
+  }
+
+  user_verifying_key_.reset();
+  hardware_key_.reset();
+
+  // TODO(enclave): Attempt to delete UV keys from system, since these can
+  // sometimes be stored.
+  user_ = nullptr;  // Prevent dangling raw_ptr error on next line.
+  CHECK(local_state_->mutable_users()->erase(primary_account_info_->gaia));
+  user_ = CreateStateForUser(local_state_.get(), *primary_account_info_);
+  WriteState(local_state_.get());
+
+  CancelAllActions();
+  Stopped();
+}
diff --git a/chrome/browser/webauthn/enclave_manager.h b/chrome/browser/webauthn/enclave_manager.h
index 576c0f6..fe9ed4f7 100644
--- a/chrome/browser/webauthn/enclave_manager.h
+++ b/chrome/browser/webauthn/enclave_manager.h
@@ -186,6 +186,9 @@
 
   webauthn_pb::EnclaveLocalState& local_state_for_testing() const;
 
+  // Release the cached HW and UV key references.
+  void ClearCachedKeysForTesting();
+
   // These methods get internal URLs so that tests can reply when they're
   // fetched.
   static std::string_view recovery_key_store_url_for_testing();
@@ -238,6 +241,11 @@
       base::OnceCallback<void(
           scoped_refptr<crypto::RefCountedUserVerifyingSigningKey>)> callback);
 
+  // If signing keys are lost or disabled, this can put the enclave registration
+  // in an unrecoverable state. In this case the registration state needs to be
+  // reset, and can be initiated from scratch.
+  void ClearRegistration();
+
   const base::FilePath file_path_;
   const raw_ptr<signin::IdentityManager> identity_manager_;
   const raw_ptr<network::mojom::NetworkContext> network_context_;
diff --git a/chrome/browser/webauthn/enclave_manager_unittest.cc b/chrome/browser/webauthn/enclave_manager_unittest.cc
index ffa52beb..8d4f0c6 100644
--- a/chrome/browser/webauthn/enclave_manager_unittest.cc
+++ b/chrome/browser/webauthn/enclave_manager_unittest.cc
@@ -26,6 +26,8 @@
 #include "components/trusted_vault/proto/recovery_key_store.pb.h"
 #include "components/trusted_vault/proto/vault.pb.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
+#include "crypto/scoped_fake_user_verifying_key_provider.h"
+#include "crypto/scoped_mock_unexportable_key_provider.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/enclave/constants.h"
 #include "device/fido/enclave/enclave_authenticator.h"
@@ -43,8 +45,6 @@
 
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
-
-#include "crypto/scoped_fake_user_verifying_key_provider.h"
 #endif
 
 // These tests are also disabled under MSAN. The enclave subprocess is written
@@ -307,6 +307,8 @@
                                             response->first);
           }
         }));
+    mock_hw_provider_ =
+        std::make_unique<crypto::ScopedMockUnexportableKeyProvider>();
   }
 
   ~EnclaveManagerTest() override {
@@ -507,6 +509,7 @@
   signin::IdentityTestEnvironment identity_test_env_;
   std::string gaia_id_;
   std::unique_ptr<FakeSecurityDomainService> security_domain_service_;
+  std::unique_ptr<crypto::ScopedMockUnexportableKeyProvider> mock_hw_provider_;
   EnclaveManager manager_;
 };
 
@@ -886,11 +889,14 @@
   EXPECT_FALSE(std::get<0>(add_callback.result().value()));
 }
 
-// TODO(enclave): Make ScopedFakeUserVerifyingKeyProvider build on other
-// platforms and enable these tests.
+// UV keys are only supported on Windows at this time.
 #if BUILDFLAG(IS_WIN)
-TEST_F(EnclaveManagerTest, UserVerifyingKeyAvailable) {
-  crypto::ScopedFakeUserVerifyingKeyProvider fake_provider;
+#define MAYBE_UserVerifyingKeyAvailable UserVerifyingKeyAvailable
+#else
+#define MAYBE_UserVerifyingKeyAvailable DISABLED_UserVerifyingKeyAvailable
+#endif
+TEST_F(EnclaveManagerTest, MAYBE_UserVerifyingKeyAvailable) {
+  crypto::ScopedFakeUserVerifyingKeyProvider fake_uv_provider;
   security_domain_service_->pretend_there_are_members();
   NoArgCallback loaded_callback;
   manager_.Load(loaded_callback.callback());
@@ -914,11 +920,17 @@
   ASSERT_FALSE(manager_.is_idle());
   add_callback.WaitForCallback();
 
-  ASSERT_EQ(manager_.uv_key_state(), EnclaveManager::UvKeyState::kUsesSystemUI);
+  EXPECT_EQ(manager_.uv_key_state(), EnclaveManager::UvKeyState::kUsesSystemUI);
 }
 
-TEST_F(EnclaveManagerTest, UserVerifyingKeyUnavailable) {
-  crypto::ScopedNullUserVerifyingKeyProvider null_provider;
+// UV keys are only supported on Windows at this time.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_UserVerifyingKeyUnavailable UserVerifyingKeyUnavailable
+#else
+#define MAYBE_UserVerifyingKeyUnavailable DISABLED_UserVerifyingKeyUnavailable
+#endif
+TEST_F(EnclaveManagerTest, MAYBE_UserVerifyingKeyUnavailable) {
+  crypto::ScopedNullUserVerifyingKeyProvider null_uv_provider;
   security_domain_service_->pretend_there_are_members();
   NoArgCallback loaded_callback;
   manager_.Load(loaded_callback.callback());
@@ -942,9 +954,110 @@
   ASSERT_FALSE(manager_.is_idle());
   add_callback.WaitForCallback();
   ASSERT_TRUE(manager_.is_registered());
-  ASSERT_EQ(manager_.uv_key_state(), EnclaveManager::UvKeyState::kNone);
+  EXPECT_EQ(manager_.uv_key_state(), EnclaveManager::UvKeyState::kNone);
 }
-#endif  // BUILDFLAG(IS_WIN)
+
+// UV keys are only supported on Windows at this time.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_UserVerifyingKeyLost UserVerifyingKeyLost
+#else
+#define MAYBE_UserVerifyingKeyLost DISABLED_UserVerifyingKeyLost
+#endif
+TEST_F(EnclaveManagerTest, MAYBE_UserVerifyingKeyLost) {
+  {
+    crypto::ScopedFakeUserVerifyingKeyProvider fake_uv_provider;
+    security_domain_service_->pretend_there_are_members();
+    NoArgCallback loaded_callback;
+    manager_.Load(loaded_callback.callback());
+    loaded_callback.WaitForCallback();
+
+    BoolCallback register_callback;
+    manager_.RegisterIfNeeded(register_callback.callback());
+    ASSERT_FALSE(manager_.is_idle());
+    register_callback.WaitForCallback();
+
+    std::vector<uint8_t> key(kTestKey.begin(), kTestKey.end());
+    ASSERT_FALSE(manager_.has_pending_keys());
+    manager_.StoreKeys(gaia_id_, {std::move(key)},
+                       /*last_key_version=*/kSecretVersion);
+    ASSERT_TRUE(manager_.is_idle());
+    ASSERT_TRUE(manager_.has_pending_keys());
+
+    BoolCallback add_callback;
+    ASSERT_TRUE(manager_.AddDeviceToAccount(
+        /*serialized_wrapped_pin=*/std::nullopt, add_callback.callback()));
+    ASSERT_FALSE(manager_.is_idle());
+    add_callback.WaitForCallback();
+
+    ASSERT_EQ(manager_.uv_key_state(),
+              EnclaveManager::UvKeyState::kUsesSystemUI);
+  }
+  manager_.ClearCachedKeysForTesting();
+  {
+    crypto::ScopedNullUserVerifyingKeyProvider null_uv_provider;
+    auto signing_callback = manager_.UserVerifyingKeySigningCallback();
+    auto quit_closure = task_env_.QuitClosure();
+    std::move(signing_callback)
+        .Run({1, 2, 3, 4},
+             base::BindLambdaForTesting(
+                 [&quit_closure](
+                     std::optional<enclave::ClientSignature> signature) {
+                   EXPECT_EQ(signature, std::nullopt);
+                   quit_closure.Run();
+                 }));
+    task_env_.RunUntilQuit();
+    EXPECT_FALSE(manager_.is_registered());
+  }
+}
+
+// Tests that rely on `ScopedMockUnexportableKeyProvider` only work on
+// platforms where EnclaveManager uses `GetUnexportableKeyProvider`, as opposed
+// to `GetSoftwareUnsecureUnexportableKeyProvider`.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_HardwareKeyLost HardwareKeyLost
+#else
+#define MAYBE_HardwareKeyLost DISABLED_HardwareKeyLost
+#endif
+TEST_F(EnclaveManagerTest, MAYBE_HardwareKeyLost) {
+  security_domain_service_->pretend_there_are_members();
+  NoArgCallback loaded_callback;
+  manager_.Load(loaded_callback.callback());
+  loaded_callback.WaitForCallback();
+
+  BoolCallback register_callback;
+  manager_.RegisterIfNeeded(register_callback.callback());
+  ASSERT_FALSE(manager_.is_idle());
+  register_callback.WaitForCallback();
+
+  std::vector<uint8_t> key(kTestKey.begin(), kTestKey.end());
+  ASSERT_FALSE(manager_.has_pending_keys());
+  manager_.StoreKeys(gaia_id_, {std::move(key)},
+                     /*last_key_version=*/kSecretVersion);
+  ASSERT_TRUE(manager_.is_idle());
+  ASSERT_TRUE(manager_.has_pending_keys());
+
+  BoolCallback add_callback;
+  ASSERT_TRUE(manager_.AddDeviceToAccount(
+      /*serialized_wrapped_pin=*/std::nullopt, add_callback.callback()));
+  ASSERT_FALSE(manager_.is_idle());
+  add_callback.WaitForCallback();
+  mock_hw_provider_.reset();
+  manager_.ClearCachedKeysForTesting();
+
+  crypto::ScopedNullUnexportableKeyProvider null_hw_provider;
+  auto signing_callback = manager_.HardwareKeySigningCallback();
+  auto quit_closure = task_env_.QuitClosure();
+  std::move(signing_callback)
+      .Run({1, 2, 3, 4},
+           base::BindLambdaForTesting(
+               [&quit_closure](
+                   std::optional<enclave::ClientSignature> signature) {
+                 EXPECT_EQ(signature, std::nullopt);
+                 quit_closure.Run();
+               }));
+  task_env_.RunUntilQuit();
+  EXPECT_FALSE(manager_.is_registered());
+}
 
 }  // namespace
 
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 514ee44..38270efe 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1710762904-22105809f60633f863938d830684e479d5c949cb-078b54b8063451151653e69250c1273230c084b6.profdata
+chrome-android32-main-1710806366-b20d0655f30866ec31b8f40045ba282809d2fe65-96d9486b16e8e26e4aa76ffa3ec4cf812e72373e.profdata
diff --git a/chrome/build/lacros64.pgo.txt b/chrome/build/lacros64.pgo.txt
index 312c8e89..4e6bbbf 100644
--- a/chrome/build/lacros64.pgo.txt
+++ b/chrome/build/lacros64.pgo.txt
@@ -1 +1 @@
-chrome-chromeos-amd64-generic-main-1710762904-b43a3d468bc301f02b597c249ee97e08568b7055-078b54b8063451151653e69250c1273230c084b6.profdata
+chrome-chromeos-amd64-generic-main-1710806366-162cdb3da665571a29f91213f264dd337b50228f-96d9486b16e8e26e4aa76ffa3ec4cf812e72373e.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index fb900419..f2581ea 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1710762904-560250b1e360f5dcc0cf9091376c9106ff876ca3-078b54b8063451151653e69250c1273230c084b6.profdata
+chrome-linux-main-1710806366-5935de052512950decd23c9a87f08ebed71bfdcf-96d9486b16e8e26e4aa76ffa3ec4cf812e72373e.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 9b78f346..7c3fce6 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1710777545-98c028c1f5b9e5706164e00af7f7fdf5e1449295-033921b894618860e0dde15caa4d3bd9ceb009d7.profdata
+chrome-mac-arm-main-1710806366-b04fe7f501cf73db3d1e06f5d2b17a99d7e9d104-96d9486b16e8e26e4aa76ffa3ec4cf812e72373e.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 4a6f0ee0..4160d299 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1710762904-f26e4ecf4dacea562c518119ca4947d1c7c3b90b-078b54b8063451151653e69250c1273230c084b6.profdata
+chrome-mac-main-1710806366-73eee0ed8ad3d94b3c7f27539a6d8fef901aa0f7-96d9486b16e8e26e4aa76ffa3ec4cf812e72373e.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 6fe994b..ccddbb9 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1710762904-a426e54fc700ad5f63bf644ec622900bd9d1e771-078b54b8063451151653e69250c1273230c084b6.profdata
+chrome-win-arm64-main-1710806366-c2d9eb708b17f707a8602c1d29984c8170c7e4f6-96d9486b16e8e26e4aa76ffa3ec4cf812e72373e.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index aa6add61..1d4f842 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1710762904-7029e2916ac9fd2d70ec4e2103da3ad660bf391a-078b54b8063451151653e69250c1273230c084b6.profdata
+chrome-win32-main-1710795422-ee98202bc7b8339f43991292592695a22ca37f1b-b4eb5d485be4bd5232a94f4144532c9d503450df.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 28806a4..1ed161f7 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1710762904-2e0092bdaae152637f22f385f0e5992225106e3c-078b54b8063451151653e69250c1273230c084b6.profdata
+chrome-win64-main-1710795422-90c8d7ab4d7c70f9639dd5b3f069e22e9203d21d-b4eb5d485be4bd5232a94f4144532c9d503450df.profdata
diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc
index 7c80a2d2..2cae61f 100644
--- a/chrome/common/logging_chrome.cc
+++ b/chrome/common/logging_chrome.cc
@@ -70,8 +70,11 @@
 #include "base/logging_win.h"
 #include "base/process/process_info.h"
 #include "base/syslog_logging.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/win_util.h"
 #include "chrome/common/win/eventlog_messages.h"
 #include "chrome/install_static/install_details.h"
+#include "sandbox/policy/switches.h"
 #endif
 
 namespace logging {
@@ -138,10 +141,35 @@
   dialogs_are_suppressed_ = true;
 }
 
-}  // anonymous namespace
-
-LoggingDestination DetermineLoggingDestination(
+#if BUILDFLAG(IS_WIN)
+base::win::ScopedHandle GetLogInheritedHandle(
     const base::CommandLine& command_line) {
+  auto handle_str = command_line.GetSwitchValueNative(switches::kLogFile);
+  uint32_t handle_value = 0;
+  if (!base::StringToUint(handle_str, &handle_value)) {
+    return base::win::ScopedHandle();
+  }
+  // Duplicate the handle from the command line so that different things can
+  // init logging. This means the handle from the parent is never closed, but
+  // there will only be one of these in the process.
+  HANDLE log_handle = nullptr;
+  if (!::DuplicateHandle(GetCurrentProcess(),
+                         base::win::Uint32ToHandle(handle_value),
+                         GetCurrentProcess(), &log_handle, 0,
+                         /*bInheritHandle=*/FALSE, DUPLICATE_SAME_ACCESS)) {
+    return base::win::ScopedHandle();
+  }
+  // Transfer ownership to the caller.
+  return base::win::ScopedHandle(log_handle);
+}
+#endif
+
+// `filename_is_handle`, will be set to `true` if the log-file switch contains
+// an inherited handle value rather than a filepath, and `false` otherwise.
+LoggingDestination LoggingDestFromCommandLine(
+    const base::CommandLine& command_line,
+    bool& filename_is_handle) {
+  filename_is_handle = false;
 #if BUILDFLAG(IS_FUCHSIA)
   // Fuchsia provides a system log that can be filtered for logs from specific
   // components (e.g. Chrome), and which is easier to access than logs in a
@@ -176,20 +204,41 @@
         command_line.GetSwitchValueASCII(switches::kEnableLogging);
     if (logging_destination == "stderr") {
       return LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR;
-    } else if (logging_destination != "") {
-      PLOG(ERROR) << "Invalid logging destination: " << logging_destination;
-#if BUILDFLAG(IS_WIN)
-    } else if (base::IsCurrentProcessInAppContainer() &&
-               !command_line.HasSwitch(switches::kLogFile)) {
-      // Sandboxed appcontainer processes are unable to resolve the default log
-      // file path without asserting.
-      return kDefaultLoggingMode & ~LOG_TO_FILE;
-#endif
     }
+#if BUILDFLAG(IS_WIN)
+    if (logging_destination == "handle" &&
+        command_line.HasSwitch(switches::kProcessType) &&
+        command_line.HasSwitch(switches::kLogFile)) {
+      // Child processes can log to a handle duplicated from the parent, and
+      // provided in the log-file switch value.
+      filename_is_handle = true;
+      return kDefaultLoggingMode | LOG_TO_FILE;
+    }
+#endif  // BUILDFLAG(IS_WIN)
+    if (logging_destination != "") {
+      // The browser process should not be called with --enable-logging=handle.
+      LOG(ERROR) << "Invalid logging destination: " << logging_destination;
+      return kDefaultLoggingMode;
+    }
+#if BUILDFLAG(IS_WIN)
+    if (command_line.HasSwitch(switches::kProcessType) &&
+        !command_line.HasSwitch(sandbox::policy::switches::kNoSandbox)) {
+      // Sandboxed processes cannot open log files so skip if provided.
+      return kDefaultLoggingMode & ~LOG_TO_FILE;
+    }
+#endif
   }
   return kDefaultLoggingMode;
 }
 
+}  // anonymous namespace
+
+LoggingDestination DetermineLoggingDestination(
+    const base::CommandLine& command_line) {
+  bool unused = false;
+  return LoggingDestFromCommandLine(command_line, unused);
+}
+
 #if BUILDFLAG(IS_CHROMEOS)
 bool RotateLogFile(const base::FilePath& target_path) {
   DCHECK(!target_path.empty());
@@ -367,43 +416,69 @@
                        OldFileDeletionState delete_old_log_file) {
   DCHECK(!chrome_logging_initialized_)
       << "Attempted to initialize logging when it was already initialized.";
-  LoggingDestination logging_dest = DetermineLoggingDestination(command_line);
+  bool filename_is_handle = false;
+  LoggingDestination logging_dest =
+      LoggingDestFromCommandLine(command_line, filename_is_handle);
   LogLockingState log_locking_state = LOCK_LOG_FILE;
   base::FilePath log_path;
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   base::FilePath target_path;
 #endif
+#if BUILDFLAG(IS_WIN)
+  base::win::ScopedHandle log_handle;
+#endif
 
-  // Don't resolve the log path unless we need to. Otherwise we leave an open
-  // ALPC handle after sandbox lockdown on Windows.
-  if ((logging_dest & LOG_TO_FILE) != 0) {
-    log_path = GetLogFileName(command_line);
+  if (logging_dest & LOG_TO_FILE) {
+    if (filename_is_handle) {
+#if BUILDFLAG(IS_WIN)
+      // Child processes on Windows are provided a file handle if logging is
+      // enabled as sandboxed processes cannot open files.
+      log_handle = GetLogInheritedHandle(command_line);
+      if (!log_handle.is_valid()) {
+        DLOG(ERROR) << "Unable to initialize logging from handle.";
+        chrome_logging_failed_ = true;
+        return;
+      }
+#endif
+    } else {
+      log_path = GetLogFileName(command_line);
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-    // For BWSI (Incognito) logins, we want to put the logs in the user
-    // profile directory that is created for the temporary session instead
-    // of in the system log directory, for privacy reasons.
-    if (command_line.HasSwitch(ash::switches::kGuestSession))
-      log_path = GetSessionLogFile(command_line);
+      // For BWSI (Incognito) logins, we want to put the logs in the user
+      // profile directory that is created for the temporary session instead
+      // of in the system log directory, for privacy reasons.
+      if (command_line.HasSwitch(ash::switches::kGuestSession)) {
+        log_path = GetSessionLogFile(command_line);
+      }
 
-    // Prepares a log file.  We rotate the previous log file and prepare a new
-    // log file if we've been asked to delete the old log, since that
-    // indicates the start of a new session.
-    target_path =
-        SetUpLogFile(log_path, delete_old_log_file == DELETE_OLD_LOG_FILE);
+      // Prepares a log file.  We rotate the previous log file and prepare a new
+      // log file if we've been asked to delete the old log, since that
+      // indicates the start of a new session.
+      target_path =
+          SetUpLogFile(log_path, delete_old_log_file == DELETE_OLD_LOG_FILE);
 
-    // Because ChromeOS manages the move to a new session by redirecting
-    // the link, it shouldn't remove the old file in the logging code,
-    // since that will remove the newly created link instead.
-    delete_old_log_file = APPEND_TO_OLD_LOG_FILE;
+      // Because ChromeOS manages the move to a new session by redirecting
+      // the link, it shouldn't remove the old file in the logging code,
+      // since that will remove the newly created link instead.
+      delete_old_log_file = APPEND_TO_OLD_LOG_FILE;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+    }
   } else {
     log_locking_state = DONT_LOCK_LOG_FILE;
   }
 
   LoggingSettings settings;
   settings.logging_dest = logging_dest;
-  settings.log_file_path = log_path.value().c_str();
+  if (!log_path.empty()) {
+    settings.log_file_path = log_path.value().c_str();
+  }
+#if BUILDFLAG(IS_WIN)
+  // Avoid initializing with INVALID_HANDLE_VALUE.
+  // This handle is owned by the logging framework and is closed when the
+  // process exits.
+  // TODO(crbug.com/328285906) Use a ScopedHandle in logging settings.
+  settings.log_file = log_handle.is_valid() ? log_handle.release() : nullptr;
+#endif
   settings.lock_log = log_locking_state;
   settings.delete_old = delete_old_log_file;
   bool success = InitLogging(settings);
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index ce8a3df..65a67e1 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -414,6 +414,14 @@
     "https://support.google.com/chrome?p=settings_privacy";
 #endif
 
+// "Chrome Settings" URL for Ad Topics page
+inline constexpr char kPrivacySandboxAdTopicsURL[] =
+    "chrome://settings/adPrivacy/interests";
+
+// "Chrome Settings" URL for Managing Topics page
+inline constexpr char kPrivacySandboxManageTopicsURL[] =
+    "chrome://settings/adPrivacy/interests/manage";
+
 // The URL for the Learn More link of the non-CWS bubble.
 inline constexpr char kRemoveNonCWSExtensionURL[] =
     "https://support.google.com/chrome?p=ui_remove_non_cws_extensions";
diff --git a/chrome/installer/util/taskbar_util.cc b/chrome/installer/util/taskbar_util.cc
index 0329447..8909aad 100644
--- a/chrome/installer/util/taskbar_util.cc
+++ b/chrome/installer/util/taskbar_util.cc
@@ -116,33 +116,13 @@
 }
 
 bool PinShortcutToTaskbar(const base::FilePath& shortcut) {
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  DCHECK(CanPinShortcutToTaskbar());
-  if (base::win::GetVersion() >= base::win::Version::WIN10_RS5)
-    return PinShortcutWithIPinnedList3(shortcut);
-
-  intptr_t result = reinterpret_cast<intptr_t>(ShellExecute(
-      nullptr, L"taskbarpin", shortcut.value().c_str(), nullptr, nullptr, 0));
-  return result > 32;
+  return PinShortcutWithIPinnedList3(shortcut);
 }
 
 bool UnpinShortcutFromTaskbar(const base::FilePath& shortcut) {
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  // Calling ShellExecute can be crashy because of shell hooks/malware, so try
-  // using IPinnedList3. Fallback to ShellExecute if it fails.
-  if (base::win::GetVersion() >= base::win::Version::WIN10_RS5 &&
-      UnpinShortcutWithIPinnedList3(shortcut)) {
-    return true;
-  }
-
-  intptr_t result = reinterpret_cast<intptr_t>(ShellExecute(
-      nullptr, L"taskbarunpin", shortcut.value().c_str(), nullptr, nullptr, 0));
-  return result > 32;
+  return UnpinShortcutWithIPinnedList3(shortcut);
 }
 
-// static
 std::optional<bool> IsShortcutPinnedToTaskbar(const base::FilePath& shortcut) {
   Microsoft::WRL::ComPtr<IPinnedList3> pinned_list = GetTaskbarPinnedList();
   if (!pinned_list.Get())
diff --git a/chrome/installer/util/taskbar_util.h b/chrome/installer/util/taskbar_util.h
index 76e8d996..650295c 100644
--- a/chrome/installer/util/taskbar_util.h
+++ b/chrome/installer/util/taskbar_util.h
@@ -14,13 +14,12 @@
 class FilePath;
 }  // namespace base
 
-// Pin to taskbar is supported on Windows 7, 8 and Win10RS5+. Returns
-// true on those platforms.
+// Pin to taskbar is supported on Win10RS5+. Returns true on those platforms.
 bool CanPinShortcutToTaskbar();
 
-// Pins a shortcut to the taskbar on Windows 7, 8 and Win10RS5+ . `shortcut`
-// file must already exist and be a shortcut that points to an executable.The
-// app id of the shortcut is used to group windows and must be set correctly.
+// Pins a shortcut to the taskbar on supported platforms. The `shortcut` file
+// must already exist and be a shortcut that points to an executable. The app id
+// of the shortcut is used to group windows and must be set correctly.
 bool PinShortcutToTaskbar(const base::FilePath& shortcut);
 
 // Unpins a shortcut from the Windows 7+ taskbar. `shortcut` must exist
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index be8a04096..000fd07 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -97,6 +97,7 @@
   "+google_apis",
   "+media/base",
   "+media/mojo",
+  "+pdf/pdf_features.h",
   "+ppapi/shared_impl",
   "+services/network/public/cpp",
   "+services/network/public/mojom",
diff --git a/chrome/renderer/accessibility/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything_app_model.cc
index 6598c8b..6aa6499 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything_app_model.cc
@@ -826,7 +826,6 @@
       case ui::AXEventGenerator::Event::CHECKED_STATE_CHANGED:
       case ui::AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
       case ui::AXEventGenerator::Event::CHILDREN_CHANGED:
-      case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED:
       case ui::AXEventGenerator::Event::COLLAPSED:
       case ui::AXEventGenerator::Event::CONTROLS_CHANGED:
       case ui::AXEventGenerator::Event::DETAILS_CHANGED:
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index 3fc7491..9c78cca 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -4756,13 +4756,13 @@
 }
 
 // Tests that user modifying the text field value does not notify the browser if
-// the field has no name or id.
-TEST_F(PasswordAutofillAgentTest, ModifyNamelessNonPasswordField) {
+// the field has name shorter than kMinInputNameLengthForSingleUsername symbols.
+TEST_F(PasswordAutofillAgentTest, ModifyNonPasswordFieldShortName) {
   LoadHTML(kSingleUsernameFormHTML);
   UpdateOnlyUsernameElement();
-  username_element_.SetAttribute("name", "");
-  username_element_.SetAttribute("id", "");
-  ASSERT_TRUE(username_element_.NameForAutofill().IsEmpty());
+  username_element_.SetAttribute("name", "i");
+  username_element_.SetAttribute("id", "i");
+  ASSERT_TRUE(username_element_.NameForAutofill().length() == 1);
 
 #if BUILDFLAG(IS_ANDROID)
   // TODO(crbug.com/1293802): User typing doesn't send focus events properly.
diff --git a/chrome/renderer/chrome_content_settings_agent_delegate.cc b/chrome/renderer/chrome_content_settings_agent_delegate.cc
index 30704069..9b8e3e4 100644
--- a/chrome/renderer/chrome_content_settings_agent_delegate.cc
+++ b/chrome/renderer/chrome_content_settings_agent_delegate.cc
@@ -5,6 +5,7 @@
 #include "chrome/renderer/chrome_content_settings_agent_delegate.h"
 
 #include "build/chromeos_buildflags.h"
+#include "pdf/buildflags.h"
 
 // TODO(b/197163596): Remove File Manager constants
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -13,6 +14,7 @@
 #include "base/containers/contains.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/renderer/render_frame.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -25,6 +27,14 @@
 #include "extensions/renderer/renderer_extension_registry.h"
 #endif
 
+#if BUILDFLAG(ENABLE_PDF)
+#include "base/feature_list.h"
+#include "chrome/common/pdf_util.h"
+#include "pdf/pdf_features.h"
+#include "third_party/blink/public/web/web_frame.h"
+#include "url/origin.h"
+#endif
+
 ChromeContentSettingsAgentDelegate::ChromeContentSettingsAgentDelegate(
     content::RenderFrame* render_frame)
     : RenderFrameObserver(render_frame),
@@ -66,6 +76,25 @@
   temporarily_allowed_plugins_.insert(identifier);
 }
 
+bool ChromeContentSettingsAgentDelegate::IsFrameAllowlistedForStorageAccess(
+    blink::WebFrame* frame) const {
+#if BUILDFLAG(ENABLE_PDF)
+  // Allow the Chrome PDF Viewer's extension frame to access storage. This is
+  // needed when a data: URL navigates to or embeds a PDF. Normally, data: URLs
+  // are opaque and shouldn't be able to access storage. However, the Chrome PDF
+  // viewer is an internal use case and does not need to adhere to the web spec.
+
+  // OOPIF PDF viewer only. The origin should match the PDF extension's origin.
+  // A PDF extension frame should always have a parent (the PDF embedder frame).
+  if (base::FeatureList::IsEnabled(chrome_pdf::features::kPdfOopif) &&
+      IsPdfExtensionOrigin(url::Origin(frame->GetSecurityOrigin())) &&
+      frame->Parent()) {
+    return true;
+  }
+#endif
+  return false;
+}
+
 bool ChromeContentSettingsAgentDelegate::IsSchemeAllowlisted(
     const std::string& scheme) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/renderer/chrome_content_settings_agent_delegate.h b/chrome/renderer/chrome_content_settings_agent_delegate.h
index 59bc99a..b874b40 100644
--- a/chrome/renderer/chrome_content_settings_agent_delegate.h
+++ b/chrome/renderer/chrome_content_settings_agent_delegate.h
@@ -37,6 +37,8 @@
   void AllowPluginTemporarily(const std::string& identifier);
 
   // content_settings::ContentSettingsAgentImpl::Delegate:
+  bool IsFrameAllowlistedForStorageAccess(
+      blink::WebFrame* frame) const override;
   bool IsSchemeAllowlisted(const std::string& scheme) override;
   bool AllowReadFromClipboard() override;
   bool AllowWriteToClipboard() override;
diff --git a/chrome/renderer/chrome_content_settings_agent_delegate_browsertest.cc b/chrome/renderer/chrome_content_settings_agent_delegate_browsertest.cc
index 400fb78..77609c4 100644
--- a/chrome/renderer/chrome_content_settings_agent_delegate_browsertest.cc
+++ b/chrome/renderer/chrome_content_settings_agent_delegate_browsertest.cc
@@ -6,9 +6,16 @@
 
 #include "chrome/test/base/chrome_render_view_test.h"
 #include "content/public/renderer/render_frame.h"
+#include "pdf/buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 
+#if BUILDFLAG(ENABLE_PDF)
+#include "base/test/scoped_feature_list.h"
+#include "pdf/pdf_features.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#endif  // BUILDFLAG(ENABLE_PDF)
+
 class ChromeContentSettingsAgentDelegateBrowserTest
     : public ChromeRenderViewTest {
  protected:
@@ -58,3 +65,44 @@
   EXPECT_TRUE(delegate->IsPluginTemporarilyAllowed(foo_plugin));
   EXPECT_TRUE(delegate->IsPluginTemporarilyAllowed(bar_plugin));
 }
+
+#if BUILDFLAG(ENABLE_PDF)
+// Test that the PDF extension frame is allowlisted for storage access.
+TEST_F(ChromeContentSettingsAgentDelegateBrowserTest,
+       FrameAllowlistedForStorageAccessPdfExtensionOrigin) {
+  // Load HTML with an iframe navigating to the PDF extension URL. Normally,
+  // an iframe navigating to the PDF extension URL fails. For testing purposes,
+  // it is needed to create a child with the PDF extension origin.
+  LoadHTML(
+      "<html><iframe "
+      "src='chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html'></"
+      "iframe></html>");
+
+  auto* delegate =
+      ChromeContentSettingsAgentDelegate::Get(GetMainRenderFrame());
+
+  blink::WebFrame* child_frame = GetMainFrame()->FirstChild();
+  ASSERT_TRUE(child_frame);
+
+  base::test::ScopedFeatureList feature_list(chrome_pdf::features::kPdfOopif);
+
+  // The PDF extension frame should be allowlisted for storage access.
+  EXPECT_TRUE(delegate->IsFrameAllowlistedForStorageAccess(child_frame));
+}
+#endif  // BUILDFLAG(ENABLE_PDF)
+
+// Test that a child frame with an origin not allowlisted for storage access
+// cannot access it.
+TEST_F(ChromeContentSettingsAgentDelegateBrowserTest,
+       FrameAllowlistedForStorageAccessFail) {
+  // Load HTML with an iframe navigating to a URL without an allowlisted origin.
+  LoadHTML("<html><iframe src='https://www.example.com'></iframe></html>");
+
+  auto* delegate =
+      ChromeContentSettingsAgentDelegate::Get(GetMainRenderFrame());
+
+  blink::WebFrame* child_frame = GetMainFrame()->FirstChild();
+  ASSERT_TRUE(child_frame);
+
+  EXPECT_FALSE(delegate->IsFrameAllowlistedForStorageAccess(child_frame));
+}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index a50b8f69..3f4d0c9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1594,6 +1594,7 @@
       "//content/public/android:content_java",
       "//content/public/test/android:android_test_message_pump_support_java",
       "//content/shell/android:content_shell_browsertests_java",
+      "//testing/android/native_test:native_browser_test_java",
       "//testing/android/native_test:native_test_java",
       "//ui/android:ui_full_java",
     ]
@@ -2223,6 +2224,7 @@
       "../../apps/load_and_launch_browsertest.cc",
       "../browser/accessibility/accessibility_labels_service_browsertest.cc",
       "../browser/accessibility/browser_accessibility_state_browsertest.cc",
+      "../browser/accessibility/embedded_a11y_extension_loader_browsertest.cc",
       "../browser/accessibility/image_annotation_browsertest.cc",
       "../browser/accessibility/interstitial_accessibility_browsertest.cc",
       "../browser/accessibility/page_colors_browsertest.cc",
@@ -2377,7 +2379,6 @@
       "../browser/media/test_license_server.h",
       "../browser/media/test_license_server_config.h",
       "../browser/media/unified_autoplay_browsertest.cc",
-      "../browser/media/webrtc/capture_policy_utils_browsertest.cc",
       "../browser/media/webrtc/media_stream_devices_controller_browsertest.cc",
       "../browser/media/webrtc/same_origin_observer_browsertest.cc",
       "../browser/media/webrtc/sub_capture_browsertest.cc",
@@ -2561,7 +2562,6 @@
       "../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_notice_confirmation_browsertest.cc",
       "../browser/privacy_sandbox/privacy_sandbox_settings_browsertest.cc",
       "../browser/privacy_sandbox/tracking_protection_notice_browsertest.cc",
       "../browser/privacy_sandbox/tracking_protection_settings_browsertest.cc",
@@ -3106,6 +3106,7 @@
         "../browser/certificate_manager_model_ash_browsertest.cc",
         "../browser/chromeos/enterprise/cloud_storage/one_drive_pref_observer_browsertest.cc",
         "../browser/chromeos/extensions/odfs_config_private/odfs_config_private_api_browsertest.cc",
+        "../browser/media/webrtc/capture_policy_utils_browsertest.cc",
         "../browser/media/webrtc/get_all_screens_media_browsertest.cc",
         "../browser/sessions/tab_restore_service_browsertest.cc",
         "../browser/support_tool/signin_data_collector_browsertest.cc",
@@ -4181,7 +4182,6 @@
         "../browser/ui/views/location_bar/location_icon_view_browsertest.cc",
         "../browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc",
         "../browser/ui/views/media_router/cast_dialog_view_browsertest.cc",
-        "../browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc",
         "../browser/ui/views/media_router/media_router_ui_browsertest.cc",
         "../browser/ui/views/media_router/presentation_receiver_window_view_browsertest.cc",
         "../browser/ui/views/omnibox/omnibox_popup_view_views_browsertest.cc",
@@ -5569,7 +5569,6 @@
     ]
 
     sources = [
-      "../browser/accessibility/embedded_a11y_extension_loader_browsertest.cc",
       "../browser/accessibility/live_caption/live_caption_surface_browsertest.cc",
       "../browser/chromeos/enterprise/cloud_storage/one_drive_pref_observer_browsertest.cc",
       "../browser/chromeos/extensions/contact_center_insights/contact_center_insights_extension_manager_lacros_browsertest.cc",
@@ -5665,6 +5664,7 @@
       "../browser/ui/startup/first_run_service_browsertest.cc",
       "../browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc",
       "../browser/ui/views/frame/immersive_mode_browser_view_test.cc",
+      "../browser/ui/views/frame/immersive_mode_controller_chromeos_browsertest.cc",
       "../browser/ui/views/intent_picker_bubble_view_browsertest.cc",
       "../browser/ui/views/location_bar/intent_chip_button_browsertest.cc",
       "../browser/ui/views/profiles/profile_picker_view_browsertest.cc",
@@ -7511,7 +7511,6 @@
       "../browser/ui/android/autofill/autofill_save_iban_bottom_sheet_bridge_unittest.cc",
       "../browser/ui/android/autofill/autofill_save_iban_delegate_unittest.cc",
       "../browser/ui/android/autofill/autofill_vcn_enroll_bottom_sheet_bridge_unittest.cc",
-      "../browser/ui/android/autofill/facilitated_payment_bottom_sheet_bridge_unittest.cc",
       "../browser/ui/android/device_dialog/usb_chooser_dialog_android_unittest.cc",
       "../browser/ui/android/fast_checkout/ui_view_android_utils_unittest.cc",
       "../browser/ui/android/hats/survey_client_android_unittest.cc",
@@ -7543,6 +7542,7 @@
       "//chrome/browser/android/webapk:webapk_sources",
       "//chrome/browser/android/webapk/test:webapk_test_support",
       "//chrome/browser/commerce/merchant_viewer:merchant_viewer_data_manager",
+      "//chrome/browser/facilitated_payments/ui/android:unit_tests",
       "//chrome/browser/flags:flags_android",
       "//chrome/browser/keyboard_accessory/android",
       "//chrome/browser/keyboard_accessory/android:unit_test",
@@ -7664,6 +7664,7 @@
       "../browser/download/bubble/download_bubble_prefs_unittest.cc",
       "../browser/download/bubble/download_bubble_ui_controller_unittest.cc",
       "../browser/download/bubble/download_bubble_update_service_unittest.cc",
+      "../browser/download/bubble/download_bubble_utils_unittest.cc",
       "../browser/download/bubble/download_display_controller_unittest.cc",
       "../browser/download/download_commands_unittest.cc",
       "../browser/download/download_dir_policy_handler_unittest.cc",
@@ -9763,6 +9764,7 @@
       "//extensions/browser",
       "//net",
       "//pdf:content_restriction",
+      "//pdf:features",
       "//services/network/public/mojom:url_loader_base",
       "//url",
     ]
@@ -10803,6 +10805,7 @@
       "../browser/password_manager/password_manager_interactive_test_base.h",
       "../browser/password_manager/password_manager_interactive_uitest.cc",
       "../browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc",
+      "../browser/privacy_sandbox/browsing_topics_settings_interactive_uitest.cc",
       "../browser/renderer_context_menu/link_to_text_menu_observer_interactive_uitest.cc",
       "../browser/renderer_context_menu/render_view_context_menu_browsertest_util.cc",
       "../browser/renderer_context_menu/render_view_context_menu_browsertest_util.h",
@@ -10856,6 +10859,7 @@
       "../browser/ui/views/extensions/extensions_menu_site_permissions_page_view_interactive_uitest.cc",
       "../browser/ui/views/extensions/extensions_menu_view_interactive_uitest.cc",
       "../browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_pixel_test.cc",
+      "../browser/ui/views/media_router/media_router_dialog_controller_views_interactive_uitest.cc",
       "../browser/ui/views/page_info/page_info_bubble_view_interactive_uitest.cc",
       "../browser/ui/views/permissions/midi_permissions_flow_interactive_uitest.cc",
       "../browser/ui/views/permissions/permissions_flow_interactive_uitest.cc",
@@ -10989,6 +10993,7 @@
       "//components/soda:constants",
       "//components/sync",
       "//components/sync:test_support",
+      "//components/sync_bookmarks",
       "//components/sync_device_info:test_support",
       "//components/tab_groups",
       "//components/translate/content/browser:test_support",
diff --git a/chrome/test/chromeos/standalone_browser_test_controller.cc b/chrome/test/chromeos/standalone_browser_test_controller.cc
index 59dfce6..164be3354 100644
--- a/chrome/test/chromeos/standalone_browser_test_controller.cc
+++ b/chrome/test/chromeos/standalone_browser_test_controller.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_keeplist_chromeos.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -219,6 +220,18 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+void StandaloneBrowserTestController::InstallComponentExtension(
+    const std::string& path,
+    const std::string& extension_id,
+    InstallComponentExtensionCallback callback) {
+  Profile* profile = ProfileManager::GetPrimaryUserProfile();
+  extensions::ExtensionService* service =
+      extensions::ExtensionSystem::Get(profile)->extension_service();
+  service->component_loader()->AddComponentFromDirWithManifestFilename(
+      base::FilePath(path), extension_id, extensions::kManifestFilename,
+      extensions::kManifestFilename, std::move(callback));
+}
+
 void StandaloneBrowserTestController::RemoveComponentExtension(
     const std::string& extension_id,
     RemoveComponentExtensionCallback callback) {
diff --git a/chrome/test/chromeos/standalone_browser_test_controller.h b/chrome/test/chromeos/standalone_browser_test_controller.h
index a820a41b..6144e7c2 100644
--- a/chrome/test/chromeos/standalone_browser_test_controller.h
+++ b/chrome/test/chromeos/standalone_browser_test_controller.h
@@ -61,6 +61,11 @@
       const std::string& path,
       InstallUnpackedExtensionCallback callback) override;
 
+  void InstallComponentExtension(
+      const std::string& path,
+      const std::string& extension_id,
+      InstallComponentExtensionCallback callback) override;
+
   void RemoveComponentExtension(
       const std::string& extension_id,
       RemoveComponentExtensionCallback callback) override;
diff --git a/chrome/test/data/chromeos/file_manager/quickoffice/manifest.json b/chrome/test/data/chromeos/file_manager/quickoffice/manifest.json
new file mode 100644
index 0000000..1f5a33d8
--- /dev/null
+++ b/chrome/test/data/chromeos/file_manager/quickoffice/manifest.json
@@ -0,0 +1,155 @@
+{
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7ctCVtjJUxR/gv6iYPkgLvhSG5j9IcbJEjqR1rbi1OvJOYQkAniDpYo46x9Ah68Fg4e0yTspvjABnB0N8YW+e7iOHhPWz7MfhRsM9AyXn8KvFwZ2BHvhXZVXBmk8wo/VkGZuw4RJWl/itpOCVwin7emaEboRQO6ETBXCVjmdYyQIDAQAB",
+  "name": "Basic Editor for Office files",
+  "version": "1",
+  "manifest_version": 3,
+  "background": {
+    "service_worker": "worker.js"
+  },
+  "file_handlers": [
+    {
+      "action": "/views/app.html",
+      "name": "qo_documents",
+      "accept": {
+        "application/msword": [
+          ".doc",
+          ".dot"
+        ],
+        "application/vnd.ms-word": [
+          ".doc"
+        ],
+        "application/vnd.msword": [
+          ".doc"
+        ],
+        "application/vnd.openxmlformats-officedocument.wordprocessingml.document": [
+          ".docx"
+        ],
+        "application/vnd.wordprocessing-openxml": [
+          ".docx"
+        ],
+        "application/vnd.ms-word.document.macroenabled.12": [
+          ".docm"
+        ],
+        "application/vnd.ms-word.document.macroEnabled.12": [
+          ".docm"
+        ],
+        "application/vnd.ms-word.document.12": [
+          ".docm"
+        ],
+        "application/msword-template": [
+          ".dot"
+        ],
+        "application/vnd.openxmlformats-officedocument.wordprocessingml.template": [
+          ".dotx"
+        ],
+        "application/vnd.ms-word.template.macroenabled.12": [
+          ".dotm"
+        ],
+        "application/vnd.ces-quickword": [
+          ".doc"
+        ],
+        "application/mspowerpoint": [
+          ".ppt",
+          ".pot"
+        ],
+        "application/mspowerpoint-template": [
+          ".pot"
+        ],
+        "application/vnd.ms-powerpoint": [
+          ".ppt"
+        ],
+        "application/vnd.presentation-openxml": [
+          ".pptx"
+        ],
+        "application/vnd.presentation-openxmlm": [
+          ".pptx"
+        ],
+        "application/vnd.ms-powerpoint.presentation.macroenabled.12": [
+          ".pptm"
+        ],
+        "application/vnd.ms-powerpoint.presentation.macroEnabled.12": [
+          ".pptm"
+        ],
+        "application/vnd.openxmlformats-officedocument.presentationml.template": [
+          ".potx"
+        ],
+        "application/vnd.ms-powerpoint.template.macroenabled.12": [
+          ".potm"
+        ],
+        "application/vnd.openxmlformats-officedocument.presentationml.presentation": [
+          ".pptx"
+        ],
+        "application/vnd.ces-quickpoint": [
+          ".ppt"
+        ],
+        "application/msexcel": [
+          ".xls",
+          ".xlt"
+        ],
+        "application/msexcel-template": [
+          ".xlt"
+        ],
+        "application/vnd.ms-excel": [
+          ".xls"
+        ],
+        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [
+          ".xlsx"
+        ],
+        "application/vnd.spreadsheet-openxml": [
+          ".xlsx"
+        ],
+        "application/vnd.ms-excel.sheet.macroenabled.12": [
+          ".xlsm"
+        ],
+        "application/vnd.ms-excel.sheet.macroEnabled.12": [
+          ".xlsm"
+        ],
+        "application/vnd.openxmlformats-officedocument.spreadsheetml.template": [
+          ".xltx"
+        ],
+        "application/vnd.ms-excel.template.macroenabled.12": [
+          ".xltm"
+        ],
+        "application/vnd.ces-quicksheet": [
+          ".xls"
+        ],
+        "text/csv": [
+          ".csv"
+        ]
+      },
+      "launch_type": "multiple-clients"
+    }
+  ],
+  "mime_types": [
+    "application/msword",
+    "application/vnd.ms-word",
+    "application/vnd.msword",
+    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+    "application/vnd.wordprocessing-openxml",
+    "application/vnd.ces-quickword",
+    "application/vnd.ms-word.document.macroEnabled.12",
+    "application/vnd.ms-word.document.macroenabled.12",
+    "application/vnd.ms-word.document.12",
+    "application/msword-template",
+    "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+    "application/mspowerpoint",
+    "application/vnd.ms-powerpoint",
+    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+    "application/vnd.ces-quickpoint",
+    "application/vnd.presentation-openxml",
+    "application/vnd.presentation-openxmlm",
+    "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
+    "application/vnd.ms-powerpoint.presentation.macroenabled.12",
+    "application/vnd.openxmlformats-officedocument.presentationml.template",
+    "application/msexcel",
+    "application/vnd.ms-excel",
+    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+    "application/vnd.ces-quicksheet",
+    "application/vnd.spreadsheet-openxml",
+    "application/vnd.ms-excel.sheet.macroEnabled.12",
+    "application/vnd.ms-excel.sheet.macroenabled.12",
+    "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
+    "text/csv"
+  ],
+  "mime_types_handler": "views/app.html"
+}
diff --git a/chrome/test/data/extensions/api_test/active_tab/background.js b/chrome/test/data/extensions/api_test/active_tab/background.js
index ef1b23be..a2458a10 100644
--- a/chrome/test/data/extensions/api_test/active_tab/background.js
+++ b/chrome/test/data/extensions/api_test/active_tab/background.js
@@ -36,7 +36,7 @@
   }
 });
 
-var iframeUrl = chrome.extension.getURL('iframe.html');
+var iframeUrl = chrome.runtime.getURL('iframe.html');
 var injectIframe =
     'var iframe = document.createElement("iframe");\n' +
     'iframe.src = "' + iframeUrl + '";\n' +
diff --git a/chrome/test/data/extensions/api_test/activity_log_private/friend/reply.js b/chrome/test/data/extensions/api_test/activity_log_private/friend/reply.js
index e2dca6a..ccf1b22c 100644
--- a/chrome/test/data/extensions/api_test/activity_log_private/friend/reply.js
+++ b/chrome/test/data/extensions/api_test/activity_log_private/friend/reply.js
@@ -55,7 +55,7 @@
 // Makes an API call that has a custom binding.
 function makeSpecialApiCalls() {
   resetStatus();
-  var url = chrome.extension.getURL('image/cat.jpg');
+  var url = chrome.runtime.getURL('image/cat.jpg');
   var noparam = chrome.extension.getViews();
   appendCompleted('makeSpecialApiCalls');
 }
@@ -377,7 +377,7 @@
 // Opens the extensions options page and then runs the executeDOMFullscreen
 // test.
 function launchDOMFullscreenTest() {
-  openTab(chrome.extension.getURL('/options.html#dom_fullscreen'));
+  openTab(chrome.runtime.getURL('/options.html#dom_fullscreen'));
 }
 
 // ADD TESTS CASES TO THE MAP HERE.
diff --git a/chrome/test/data/extensions/api_test/activity_log_private/test/test.js b/chrome/test/data/extensions/api_test/activity_log_private/test/test.js
index 8da091f..32c96f13 100644
--- a/chrome/test/data/extensions/api_test/activity_log_private/test/test.js
+++ b/chrome/test/data/extensions/api_test/activity_log_private/test/test.js
@@ -22,7 +22,7 @@
                                'special_call', function response() { });
   },
   expected_activity: [
-    'extension.getURL',
+    'runtime.getURL',
     'extension.getViews'
   ]
 });
@@ -209,7 +209,7 @@
                                function response() { });
   },
   expected_activity: [
-    'extension.getURL',
+    'runtime.getURL',
     'test.getConfig',
     'Element.webkitRequestFullscreen'
   ]
diff --git a/chrome/test/data/extensions/api_test/content_scripts/extension_iframe/script.js b/chrome/test/data/extensions/api_test/content_scripts/extension_iframe/script.js
index 2b2613b0..32ded44 100644
--- a/chrome/test/data/extensions/api_test/content_scripts/extension_iframe/script.js
+++ b/chrome/test/data/extensions/api_test/content_scripts/extension_iframe/script.js
@@ -1,3 +1,3 @@
 var ifr = document.createElement("iframe");
-ifr.src = chrome.extension.getURL("iframe.html");
+ifr.src = chrome.runtime.getURL("iframe.html");
 document.body.appendChild(ifr);
diff --git a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/injector/test.js b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/injector/test.js
index 048a0605..f25c80b 100644
--- a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/injector/test.js
+++ b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/injector/test.js
@@ -20,7 +20,7 @@
       chrome.test.log("Creating tab...");
       var test_url = ("http://a.com:PORT/extensions/api_test" +
           "/content_scripts/other_extensions/iframe_content.html#" +
-          escape(chrome.extension.getURL("test.html")))
+          escape(chrome.runtime.getURL("test.html")))
           .replace(/PORT/, config.testServer.port);
       console.log('Opening frame: ' + test_url);
       document.getElementById('content_frame').src = test_url;
diff --git a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/victim/test.js b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/victim/test.js
index 39a175e..8c20b6c0 100644
--- a/chrome/test/data/extensions/api_test/content_scripts/other_extensions/victim/test.js
+++ b/chrome/test/data/extensions/api_test/content_scripts/other_extensions/victim/test.js
@@ -20,7 +20,7 @@
       chrome.test.log("Creating tab...");
       var test_url = ("http://a.com:PORT/extensions/api_test" +
           "/content_scripts/other_extensions/iframe_content.html#" +
-          escape(chrome.extension.getURL("test.html")))
+          escape(chrome.runtime.getURL("test.html")))
           .replace(/PORT/, config.testServer.port);
       console.log('Opening frame: ' + test_url);
       document.getElementById('content_frame').src = test_url;
diff --git a/chrome/test/data/extensions/api_test/cookies/api/background.js b/chrome/test/data/extensions/api_test/cookies/api/background.js
index a636d8d..f632c7da 100644
--- a/chrome/test/data/extensions/api_test/cookies/api/background.js
+++ b/chrome/test/data/extensions/api_test/cookies/api/background.js
@@ -184,7 +184,7 @@
 chrome.test.runTests([
   function invalidScheme() {
     // Invalid schemes don't work with the cookie API.
-    var ourUrl = chrome.extension.getURL('background.js');
+    var ourUrl = chrome.runtime.getURL('background.js');
     chrome.cookies.get(
         {url: ourUrl, name: 'a'},
         chrome.test.callbackFail(
diff --git a/chrome/test/data/extensions/api_test/debugger/background.js b/chrome/test/data/extensions/api_test/debugger/background.js
index fd9cc04..84bc996c 100644
--- a/chrome/test/data/extensions/api_test/debugger/background.js
+++ b/chrome/test/data/extensions/api_test/debugger/background.js
@@ -178,7 +178,7 @@
   },
 
   function attachToOwnBackgroundPageWithNoSilentFlag() {
-    var ownExtensionId = chrome.extension.getURL('').split('/')[2];
+    var ownExtensionId = chrome.runtime.getURL('').split('/')[2];
     debuggee = {extensionId: ownExtensionId};
     chrome.debugger.attach(debuggee, protocolVersion, pass());
   },
diff --git a/chrome/test/data/extensions/api_test/debugger_extension/background.js b/chrome/test/data/extensions/api_test/debugger_extension/background.js
index aedf2db..8440e39 100644
--- a/chrome/test/data/extensions/api_test/debugger_extension/background.js
+++ b/chrome/test/data/extensions/api_test/debugger_extension/background.js
@@ -20,7 +20,7 @@
   },
 
   function attach() {
-    var extensionId = chrome.extension.getURL('').split('/')[2];
+    var extensionId = chrome.runtime.getURL('').split('/')[2];
     debuggee = {extensionId: extensionId};
     chrome.debugger.attach(debuggee, protocolVersion, pass());
   },
diff --git a/chrome/test/data/extensions/api_test/executescript/file_after_close/test.js b/chrome/test/data/extensions/api_test/executescript/file_after_close/test.js
index 6ec4c8f1..e2ff358 100644
--- a/chrome/test/data/extensions/api_test/executescript/file_after_close/test.js
+++ b/chrome/test/data/extensions/api_test/executescript/file_after_close/test.js
@@ -4,7 +4,7 @@
 
 var server = 'http://b.com:PORT';
 var relativePath = '/extensions/api_test/executescript/file_after_close/';
-var extensionPage = chrome.extension.getURL('extension_page.html');
+var extensionPage = chrome.runtime.getURL('extension_page.html');
 var webPage1 = server + relativePath + 'web_page1.html';
 var webPage2 = server + relativePath + 'web_page2.html';
 var extensionPageOpened = false;
diff --git a/chrome/test/data/extensions/api_test/get_views/test.js b/chrome/test/data/extensions/api_test/get_views/test.js
index 838f64c..6dfcf416 100644
--- a/chrome/test/data/extensions/api_test/get_views/test.js
+++ b/chrome/test/data/extensions/api_test/get_views/test.js
@@ -19,7 +19,7 @@
   // We have now added a popup so the total count goes up one.
   assertEq(2, chrome.extension.getViews().length);
   assertEq(1, chrome.extension.getViews({windowId: popupWindowId}).length);
-  chrome.tabs.create({url: chrome.extension.getURL("options.html")},
+  chrome.tabs.create({url: chrome.runtime.getURL("options.html")},
                      function(tab) {
     optionsTabId = tab.id;
   });
@@ -69,7 +69,7 @@
 
       // TODO (catmullings): Fix potential race condition when/if
       // popupCallback() is called before popupWindowId is set below
-      chrome.windows.create({url: chrome.extension.getURL("popup.html"),
+      chrome.windows.create({url: chrome.runtime.getURL("popup.html"),
                              type: "popup"}, function(window) {
         assertTrue(window.id > 0);
         popupWindowId = window.id;
diff --git a/chrome/test/data/extensions/api_test/messaging/connect_panel/test.js b/chrome/test/data/extensions/api_test/messaging/connect_panel/test.js
index 124e0d9..13f44f6 100644
--- a/chrome/test/data/extensions/api_test/messaging/connect_panel/test.js
+++ b/chrome/test/data/extensions/api_test/messaging/connect_panel/test.js
@@ -11,7 +11,7 @@
       chrome.test.assertTrue(port.sender.tab.id > 0);
     });
     chrome.windows.create(
-        { 'url': chrome.extension.getURL('panel.html'), 'type': 'panel' },
+        { 'url': chrome.runtime.getURL('panel.html'), 'type': 'panel' },
         chrome.test.callbackPass(function(win) {
           chrome.test.assertEq('panel', win.type);
           panelWindowId = win.id;
diff --git a/chrome/test/data/extensions/api_test/processes/api/test.js b/chrome/test/data/extensions/api_test/processes/api/test.js
index 3cd385b..7316ab0 100644
--- a/chrome/test/data/extensions/api_test/processes/api/test.js
+++ b/chrome/test/data/extensions/api_test/processes/api/test.js
@@ -24,7 +24,7 @@
 var getProcessId = chrome.processes.getProcessIdForTab;
 
 function pageUrl(letter) {
-  return chrome.extension.getURL(letter + ".html");
+  return chrome.runtime.getURL(letter + ".html");
 }
 
 function dumpProcess(process) {
diff --git a/chrome/test/data/extensions/api_test/service_worker/mime_handler_view/background.js b/chrome/test/data/extensions/api_test/service_worker/mime_handler_view/background.js
index ed748104d..18ae569 100644
--- a/chrome/test/data/extensions/api_test/service_worker/mime_handler_view/background.js
+++ b/chrome/test/data/extensions/api_test/service_worker/mime_handler_view/background.js
@@ -21,7 +21,7 @@
   return navigator.serviceWorker.ready;
 }).then(function() {
   chrome.tabs.create({
-    url: chrome.extension.getURL('page_with_embed.html'),
+    url: chrome.runtime.getURL('page_with_embed.html'),
   });
 }).catch(function(e) {
   chrome.test.fail('Unexpected error: ' + e);
diff --git a/chrome/test/data/extensions/api_test/service_worker/mime_handler_view/mime_handler.js b/chrome/test/data/extensions/api_test/service_worker/mime_handler_view/mime_handler.js
index 8d601e9..665dd30 100644
--- a/chrome/test/data/extensions/api_test/service_worker/mime_handler_view/mime_handler.js
+++ b/chrome/test/data/extensions/api_test/service_worker/mime_handler_view/mime_handler.js
@@ -4,7 +4,7 @@
 
 chrome.mimeHandlerPrivate.getStreamInfo(function(streamInfo) {
   chrome.test.assertEq(
-      chrome.extension.getURL('well-known-mime.ics'), streamInfo.originalUrl);
+      chrome.runtime.getURL('well-known-mime.ics'), streamInfo.originalUrl);
   var x = new XMLHttpRequest();
   x.open('GET', streamInfo.streamUrl);
   x.onloadend = function() {
diff --git a/chrome/test/data/extensions/api_test/service_worker/worker_based_background/filtered_events/service_worker_background.js b/chrome/test/data/extensions/api_test/service_worker/worker_based_background/filtered_events/service_worker_background.js
index 19afa23..32b25933 100644
--- a/chrome/test/data/extensions/api_test/service_worker/worker_based_background/filtered_events/service_worker_background.js
+++ b/chrome/test/data/extensions/api_test/service_worker/worker_based_background/filtered_events/service_worker_background.js
@@ -4,7 +4,7 @@
 
 chrome.test.runTests([
   function testWebNavigationOnCommitted() {
-    var getURL = chrome.extension.getURL;
+    var getURL = chrome.runtime.getURL;
     chrome.tabs.create({url: 'about:blank'}, function(tab) {
       var tabId = tab.id;
       var aVisited = false;
diff --git a/chrome/test/data/extensions/api_test/sessions/sessions.js b/chrome/test/data/extensions/api_test/sessions/sessions.js
index 8755aa2a..bd883bb9 100644
--- a/chrome/test/data/extensions/api_test/sessions/sessions.js
+++ b/chrome/test/data/extensions/api_test/sessions/sessions.js
@@ -16,7 +16,7 @@
 var assertTrue = chrome.test.assertTrue;
 
 function pageUrl(letter) {
-  return chrome.extension.getURL(letter + ".html");
+  return chrome.runtime.getURL(letter + ".html");
 }
 
 // Creates one window with tabs set to the urls in the array |tabUrls|.
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/get_views_common.js b/chrome/test/data/extensions/api_test/tabs/basics/get_views_common.js
index c9f58a9..904e697d 100644
--- a/chrome/test/data/extensions/api_test/tabs/basics/get_views_common.js
+++ b/chrome/test/data/extensions/api_test/tabs/basics/get_views_common.js
@@ -18,7 +18,7 @@
 
       // Make the expected URLs non-relative, and sort them.
       var expectedUrls = expectedViewURLs.map(function(url) {
-        return chrome.extension.getURL(url);
+        return chrome.runtime.getURL(url);
       }).sort();
 
       // Comparing JSON makes errors easy to read.
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/query/query.js b/chrome/test/data/extensions/api_test/tabs/basics/query/query.js
index b24cb5a..7177528 100644
--- a/chrome/test/data/extensions/api_test/tabs/basics/query/query.js
+++ b/chrome/test/data/extensions/api_test/tabs/basics/query/query.js
@@ -193,7 +193,7 @@
   },
 
   function queryTitle() {
-    let title_url = chrome.extension.getURL("query.html");
+    let title_url = chrome.runtime.getURL("query.html");
     chrome.tabs.create({url: title_url}, pass(function() {
       waitForAllTabs(pass(function() {
         chrome.tabs.query({title: "*query.html"}, pass(function(tabs) {
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/tabs_util.js b/chrome/test/data/extensions/api_test/tabs/basics/tabs_util.js
index 8f1b9f2..45dd855b 100644
--- a/chrome/test/data/extensions/api_test/tabs/basics/tabs_util.js
+++ b/chrome/test/data/extensions/api_test/tabs/basics/tabs_util.js
@@ -9,7 +9,7 @@
 var assertTrue = chrome.test.assertTrue;
 
 function pageUrl(name) {
-  return chrome.extension.getURL(
+  return chrome.runtime.getURL(
       '_test_resources/api_test/tabs/basics/' + name + '.html');
 }
 
diff --git a/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_jpeg/test_jpeg.js b/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_jpeg/test_jpeg.js
index db1a639..8c4ae1821 100644
--- a/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_jpeg/test_jpeg.js
+++ b/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_jpeg/test_jpeg.js
@@ -81,7 +81,7 @@
     },
 
     function captureVisibleTabChromeExtensionScheme() {
-      var url = chrome.extension.getURL("/white.html");
+      var url = chrome.runtime.getURL("/white.html");
       createWindow([url], kWindowRect, pass(function(winId, tabIds) {
         waitForAllTabs(pass(function() {
           chrome.tabs.getSelected(winId, pass(function(tab) {
diff --git a/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_png/test_png.js b/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_png/test_png.js
index 7d4f818..f920be4 100644
--- a/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_png/test_png.js
+++ b/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_png/test_png.js
@@ -96,7 +96,7 @@
     },
 
     function captureVisibleTabChromeExtensionScheme() {
-      var url = chrome.extension.getURL("/white.html");
+      var url = chrome.runtime.getURL("/white.html");
       createWindow([url], kWindowRect, pass(function(winId, tabIds) {
         waitForAllTabs(pass(function() {
           chrome.tabs.getSelected(winId, pass(function(tab) {
diff --git a/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_race/test_race.js b/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_race/test_race.js
index b80cb80..af207123 100644
--- a/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_race/test_race.js
+++ b/chrome/test/data/extensions/api_test/tabs/capture_visible_tab/test_race/test_race.js
@@ -46,7 +46,7 @@
         colorName = 'black';
         expectedColor = '0,0,0,255';
       }
-      var url = chrome.extension.getURL('/common/' + colorName + '.html');
+      var url = chrome.runtime.getURL('/common/' + colorName + '.html');
       createWindow(
           [url],
           kWindowRect,
diff --git a/chrome/test/data/extensions/api_test/tabs/get_current/test.js b/chrome/test/data/extensions/api_test/tabs/get_current/test.js
index a87910f..6d966782 100644
--- a/chrome/test/data/extensions/api_test/tabs/get_current/test.js
+++ b/chrome/test/data/extensions/api_test/tabs/get_current/test.js
@@ -5,7 +5,7 @@
 var assertEq = chrome.test.assertEq;
 
 function pageUrl(letter) {
-  return chrome.extension.getURL(letter + ".html");
+  return chrome.runtime.getURL(letter + ".html");
 }
 
 chrome.runtime.onMessage.addListener(function listener(tab,
diff --git a/chrome/test/data/extensions/api_test/tabs/on_removed/test.js b/chrome/test/data/extensions/api_test/tabs/on_removed/test.js
index 9ef46c9..06587fc 100644
--- a/chrome/test/data/extensions/api_test/tabs/on_removed/test.js
+++ b/chrome/test/data/extensions/api_test/tabs/on_removed/test.js
@@ -61,7 +61,7 @@
 }
 
 function pageUrl(letter) {
-  return chrome.extension.getURL(letter + ".html");
+  return chrome.runtime.getURL(letter + ".html");
 }
 
 chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/tabs/on_updated/test.js b/chrome/test/data/extensions/api_test/tabs/on_updated/test.js
index 1fd9f00..8d568360 100644
--- a/chrome/test/data/extensions/api_test/tabs/on_updated/test.js
+++ b/chrome/test/data/extensions/api_test/tabs/on_updated/test.js
@@ -21,7 +21,7 @@
   chrome.test.succeed();
 }
 
-var getURL = chrome.extension.getURL;
+var getURL = chrome.runtime.getURL;
 
 chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
   console.log('---onUpdated: ' + info.status + ', ' + info.url + '. ' +
diff --git a/chrome/test/data/extensions/api_test/tabs/reload/test.js b/chrome/test/data/extensions/api_test/tabs/reload/test.js
index 4e94120..23607dbb 100644
--- a/chrome/test/data/extensions/api_test/tabs/reload/test.js
+++ b/chrome/test/data/extensions/api_test/tabs/reload/test.js
@@ -5,7 +5,7 @@
 var myTabId;
 
 function pageUrl(letter) {
-  return chrome.extension.getURL(letter + ".html");
+  return chrome.runtime.getURL(letter + ".html");
 }
 
 function withTabOnReload(fn) {
diff --git a/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/test_clientRedirect.js b/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/test_clientRedirect.js
index 10ffbf55..5be33604 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/test_clientRedirect.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/test_clientRedirect.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   chrome.test.runTests([
     // Navigates to a.html that redirects to b.html (using javascript).
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcess/test_crossProcess.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcess/test_crossProcess.js
index f1a8387d2..64bdc49 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcess/test_crossProcess.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcess/test_crossProcess.js
@@ -9,7 +9,7 @@
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
 
   var URL_REGULAR =
       "http://127.0.0.1:" + port + "/extensions/api_test/webnavigation/" +
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/test_crossProcessAbort.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/test_crossProcessAbort.js
index 7a7cfa5..3a1ae47 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/test_crossProcessAbort.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/test_crossProcessAbort.js
@@ -6,7 +6,7 @@
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
 
   let INITIAL_URL = getURL("initial.html");
   let SAME_SITE_URL = getURL("empty.html");
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/test_crossProcessFragment.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/test_crossProcessFragment.js
index 9ef9eeb..2c8dfc88 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/test_crossProcessFragment.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/test_crossProcessFragment.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 onload = async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.js
index ce6cc217..3f76a929 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  var getURL = chrome.extension.getURL;
+  var getURL = chrome.runtime.getURL;
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
   var URL_TEST = "http://127.0.0.1:" + port + "/test";
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/test_crossProcessIframe.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/test_crossProcessIframe.js
index 4767328..6312e3d6 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/test_crossProcessIframe.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/test_crossProcessIframe.js
@@ -7,7 +7,7 @@
 
 loadScript.then(async function() {
   debug = true;
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
diff --git a/chrome/test/data/extensions/api_test/webnavigation/download/test_download.js b/chrome/test/data/extensions/api_test/webnavigation/download/test_download.js
index a3ab7d8..dacac99 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/download/test_download.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/download/test_download.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
diff --git a/chrome/test/data/extensions/api_test/webnavigation/failures/test_failures.js b/chrome/test/data/extensions/api_test/webnavigation/failures/test_failures.js
index 5282b49f..5304b88 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/failures/test_failures.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/failures/test_failures.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  var getURL = chrome.extension.getURL;
+  var getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/webnavigation/filtered/test_filtered.js b/chrome/test/data/extensions/api_test/webnavigation/filtered/test_filtered.js
index 2e9edd96..cfaf3ef29 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/filtered/test_filtered.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/filtered/test_filtered.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   chrome.test.runTests([
     function dontGetEventToWrongUrl() {
diff --git a/chrome/test/data/extensions/api_test/webnavigation/formSubmission/test_formSubmission.js b/chrome/test/data/extensions/api_test/webnavigation/formSubmission/test_formSubmission.js
index bce5263..1d484af0 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/formSubmission/test_formSubmission.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/formSubmission/test_formSubmission.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   chrome.test.runTests([
     // Navigates to a.html that redirects to b.html (using javascript).
diff --git a/chrome/test/data/extensions/api_test/webnavigation/forwardBack/test_forwardBack.js b/chrome/test/data/extensions/api_test/webnavigation/forwardBack/test_forwardBack.js
index 4008674c..65cad03 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/forwardBack/test_forwardBack.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/forwardBack/test_forwardBack.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js b/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
index a09947c..022bdb0 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
@@ -20,8 +20,8 @@
 }
 
 ready.then(async function() {
-  var URL = chrome.extension.getURL("a.html");
-  var URL_FRAMES = chrome.extension.getURL("b.html");
+  var URL = chrome.runtime.getURL("a.html");
+  var URL_FRAMES = chrome.runtime.getURL("b.html");
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
   var processId = -1;
diff --git a/chrome/test/data/extensions/api_test/webnavigation/history/test_history.js b/chrome/test/data/extensions/api_test/webnavigation/history/test_history.js
index 352a693..d6cc9e8 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/history/test_history.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/history/test_history.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  var getURL = chrome.extension.getURL;
+  var getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.js b/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.js
index 63c4321..f96abda 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  var getURL = chrome.extension.getURL;
+  var getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.js b/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.js
index 1f54470..3d53cc1 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  var getURL = chrome.extension.getURL;
+  var getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/webnavigation/prerender/test_prerender.js b/chrome/test/data/extensions/api_test/webnavigation/prerender/test_prerender.js
index 39485568..d7f7bb4 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/prerender/test_prerender.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/prerender/test_prerender.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 onload = async function() {
-  var getURL = chrome.extension.getURL;
+  var getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
diff --git a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.js b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.js
index 808993e3..307954e 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  var getURL = chrome.extension.getURL;
+  var getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/test_requestOpenTab.js b/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/test_requestOpenTab.js
index 1a552693..801b38b 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/test_requestOpenTab.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/test_requestOpenTab.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/test_simpleLoad.js b/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/test_simpleLoad.js
index 10f94a0..c1a764a 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/test_simpleLoad.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/test_simpleLoad.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/webnavigation/srcdoc/test_srcdoc.js b/chrome/test/data/extensions/api_test/webnavigation/srcdoc/test_srcdoc.js
index 8651c15..27068c5e 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/srcdoc/test_srcdoc.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/srcdoc/test_srcdoc.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/webnavigation/userAction/test_userAction.js b/chrome/test/data/extensions/api_test/webnavigation/userAction/test_userAction.js
index 13bf70a..5e9dc00 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/userAction/test_userAction.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/userAction/test_userAction.js
@@ -6,7 +6,7 @@
 let loadScript = chrome.test.loadScript(scriptUrl);
 
 loadScript.then(async function() {
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
diff --git a/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.js b/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.js
index 84ad67a5..46ca99c 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.js
@@ -7,7 +7,7 @@
 
 loadScript.then(async function() {
   debug = true;
-  let getURL = chrome.extension.getURL;
+  let getURL = chrome.runtime.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
   let config = await promise(chrome.test.getConfig);
   let port = config.testServer.port;
diff --git a/chrome/test/data/extensions/api_test/webrequest/framework.js b/chrome/test/data/extensions/api_test/webrequest/framework.js
index f2569eb8..fed4f71 100644
--- a/chrome/test/data/extensions/api_test/webrequest/framework.js
+++ b/chrome/test/data/extensions/api_test/webrequest/framework.js
@@ -9,7 +9,7 @@
   self.selfFrameId = 0;
 }
 
-var getURL = chrome.extension.getURL;
+var getURL = chrome.runtime.getURL;
 var deepEq = chrome.test.checkDeepEq;
 var expectedEventData;
 var capturedEventData;
diff --git a/chrome/test/data/extensions/api_test/window_open/opener/test.js b/chrome/test/data/extensions/api_test/window_open/opener/test.js
index 4c22dd9..4755fb1 100644
--- a/chrome/test/data/extensions/api_test/window_open/opener/test.js
+++ b/chrome/test/data/extensions/api_test/window_open/opener/test.js
@@ -30,7 +30,7 @@
           chrome.test.callbackPass(function(tabs) {
             chrome.test.assertEq(1, tabs.length);
             chrome.test.assertEq(
-                chrome.extension.getURL('check-opener.html'), tabs[0].url);
+                chrome.runtime.getURL('check-opener.html'), tabs[0].url);
             testCompleted();
           }));
     };
diff --git a/chrome/test/data/extensions/api_test/window_open/window_size/test.js b/chrome/test/data/extensions/api_test/window_open/window_size/test.js
index 2830408..d7d98e6 100644
--- a/chrome/test/data/extensions/api_test/window_open/window_size/test.js
+++ b/chrome/test/data/extensions/api_test/window_open/window_size/test.js
@@ -7,7 +7,7 @@
     chrome.windows.create(
       // Note: width and height must be larger than the minimum window size
       // and smaller than the max (screen) size.
-      { 'url': chrome.extension.getURL('popup.html'), 'type': 'popup',
+      { 'url': chrome.runtime.getURL('popup.html'), 'type': 'popup',
         'width': 200, 'height': 200 },
       chrome.test.callbackPass(function(win) {
         chrome.test.assertEq(200, win.width);
diff --git a/chrome/test/data/extensions/subscribe_page_action/src/background.js b/chrome/test/data/extensions/subscribe_page_action/src/background.js
index abbb0d9..c3c1208 100644
--- a/chrome/test/data/extensions/subscribe_page_action/src/background.js
+++ b/chrome/test/data/extensions/subscribe_page_action/src/background.js
@@ -47,7 +47,7 @@
                  "history.go(-1); else window.close();"
         });
     var url = "subscribe.html?" + encodeURIComponent(request.href);
-    url = chrome.extension.getURL(url);
+    url = chrome.runtime.getURL(url);
     chrome.tabs.create({ url: url, index: sender.tab.index });
   }
 });
diff --git a/chrome/test/data/extensions/subscribe_page_action/src/subscribe.js b/chrome/test/data/extensions/subscribe_page_action/src/subscribe.js
index e06c282..de2fa6e 100644
--- a/chrome/test/data/extensions/subscribe_page_action/src/subscribe.js
+++ b/chrome/test/data/extensions/subscribe_page_action/src/subscribe.js
@@ -89,11 +89,11 @@
   token = [].join.call(tokenArray);
 
   styleSheet = "<link rel='stylesheet' type='text/css' href='" +
-                   chrome.extension.getURL("style.css") + "'>";
+                   chrome.runtime.getURL("style.css") + "'>";
   frameScript = window.domAutomationController !== undefined ? "<script src='" +
-                    chrome.extension.getURL("test_support.js") +
+                    chrome.runtime.getURL("test_support.js") +
                     "'></" + "script>" : "";
-  frameScript += "<script src='" + chrome.extension.getURL("iframe.js") +
+  frameScript += "<script src='" + chrome.runtime.getURL("iframe.js") +
                      "'></" + "script>";
 
   // Now fetch the feed data.
@@ -131,7 +131,7 @@
   var html = "<body><span id=\"error\" class=\"item_desc\">" + error +
                "</span></body>";
   if (window.domAutomationController) {
-    html += "<script src='" + chrome.extension.getURL("test_send_error.js") +
+    html += "<script src='" + chrome.runtime.getURL("test_send_error.js") +
                      "'></" + "script>";
   }
 
diff --git a/chrome/test/data/extensions/web_request_site_process_registration/test.js b/chrome/test/data/extensions/web_request_site_process_registration/test.js
index 11283dc..2e88942 100644
--- a/chrome/test/data/extensions/web_request_site_process_registration/test.js
+++ b/chrome/test/data/extensions/web_request_site_process_registration/test.js
@@ -4,7 +4,7 @@
 
 chrome.webRequest.onBeforeRequest.addListener(
   function(details) {
-    if (details.url == chrome.extension.getURL("/blocked.html")) {
+    if (details.url == chrome.runtime.getURL("/blocked.html")) {
       return {cancel: true};
     }
   },
diff --git a/chrome/test/data/pdf/accessibility/inaccessible-text-in-three-page.pdf b/chrome/test/data/pdf/accessibility/inaccessible-text-in-three-page.pdf
new file mode 100644
index 0000000..af9384b0
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/inaccessible-text-in-three-page.pdf
Binary files differ
diff --git a/chrome/test/data/pdf/accessibility/three-page.in b/chrome/test/data/pdf/accessibility/three-page.in
new file mode 100644
index 0000000..1f2f0d45
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/three-page.in
@@ -0,0 +1,89 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 250]
+  /Count 3
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Kids [3 0 R 6 0 R 8 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 100 Td
+/F1 16 Tf
+(Hello, world!) Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 200 Td
+/F1 16 Tf
+(Paragraph 1 on Page 2) Tj
+ET
+BT
+20 100 Td
+/F1 16 Tf
+(Paragraph 2 on Page 2) Tj
+ET
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 9 0 R
+>>
+endobj
+{{object 9 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 200 Td
+/F1 16 Tf
+(Paragraph 1 on Page 3) Tj
+ET
+BT
+20 100 Td
+/F1 16 Tf
+(Paragraph 2 on Page 3) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index b4a9bab..edb23a51 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -98,10 +98,12 @@
       "chromeos/ash_common/cr_elements/ash_common_cr_elements_browsertest.cc",
       "chromeos/ash_common/sea_pen/sea_pen_browsertest.cc",
       "chromeos/ash_common/webui_resource_browsertest.cc",
+      "chromeos/edu_coexistence/edu_coexistence_browsertest.cc",
       "chromeos/enterprise_reporting/enterprise_reporting_browsertest.cc",
       "chromeos/firmware_update/firmware_update_browsertest.cc",
       "chromeos/inline_login/inline_login_browsertest.cc",
       "chromeos/oobe/security_token_pin_browsertest.cc",
+      "chromeos/parent_access/parent_access_browsertest.cc",
       "chromeos/personalization_app/personalization_app_browsertest.cc",
       "chromeos/personalization_app/personalization_app_sea_pen_browsertest.cc",
       "chromeos/print_preview_cros/print_preview_cros_browsertest.cc",
diff --git a/chrome/test/data/webui/access_code_cast/passcode_input_test.ts b/chrome/test/data/webui/access_code_cast/passcode_input_test.ts
index 82275b11..2150edd 100644
--- a/chrome/test/data/webui/access_code_cast/passcode_input_test.ts
+++ b/chrome/test/data/webui/access_code_cast/passcode_input_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://access-code-cast/passcode_input/passcode_input.js';
 
-import type {PasscodeInputElement} from 'chrome://access-code-cast/passcode_input/passcode_input';
+import type {PasscodeInputElement} from 'chrome://access-code-cast/passcode_input/passcode_input.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/chromeos/BUILD.gn b/chrome/test/data/webui/chromeos/BUILD.gn
index 2932c6b..a40262fe 100644
--- a/chrome/test/data/webui/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/BUILD.gn
@@ -50,8 +50,6 @@
       "cloud_upload/connect_onedrive_browsertest.js",
       "cloud_upload/file_handler_page_browsertest.js",
       "cloud_upload/move_confirmation_page_browsertest.js",
-      "edu_coexistence/edu_coexistence_browsertest.js",
-      "edu_coexistence/edu_coexistence_with_arc_restrictions_browsertest.js",
       "emoji_picker/emoji_picker_browsertest.js",
       "gaia_action_buttons/gaia_action_buttons_browsertest.js",
       "internet_config_dialog_browsertest.js",
@@ -101,7 +99,6 @@
       "../nearby_share/shared/nearby_shared_v3_browsertest.js",
       "diagnostics/diagnostics_browsertest.js",
       "manage_mirrorsync/manage_mirrorsync_browsertest.js",
-      "parent_access/parent_access_browsertest.js",
       "print_management/print_management_browsertest.js",
       "shortcut_customization/shortcut_customization_browsertest.js",
     ]
diff --git a/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.cc b/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.cc
new file mode 100644
index 0000000..5b49949
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.cc
@@ -0,0 +1,63 @@
+// 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/strings/strcat.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/chromeos/ash_browser_test_starter.h"
+#include "chrome/test/base/chromeos/lacros_only_mocha_browser_test.h"
+#include "chrome/test/base/web_ui_mocha_browser_test.h"
+#include "chromeos/ash/components/standalone_browser/standalone_browser_features.h"
+#include "content/public/test/browser_test.h"
+
+class EduCoexistenceMochaTest : public WebUIMochaBrowserTest {
+ protected:
+  EduCoexistenceMochaTest() {
+    set_test_loader_host(chrome::kChromeUIChromeSigninHost);
+  }
+
+  void RunEduCoexistenceTest(const std::string& test_path) {
+    RunTest(base::StrCat({"chromeos/edu_coexistence/", test_path}),
+            "mocha.run()");
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(EduCoexistenceMochaTest, App) {
+  RunEduCoexistenceTest("edu_coexistence_app_test.js");
+}
+
+IN_PROC_BROWSER_TEST_F(EduCoexistenceMochaTest, Controller) {
+  RunEduCoexistenceTest("edu_coexistence_controller_test.js");
+}
+
+IN_PROC_BROWSER_TEST_F(EduCoexistenceMochaTest, Ui) {
+  RunEduCoexistenceTest("edu_coexistence_ui_test.js");
+}
+
+class EduCoexistenceWithArcRestrictionsMochaTest
+    : public LacrosOnlyMochaBrowserTest {
+ protected:
+  EduCoexistenceWithArcRestrictionsMochaTest() {
+    set_test_loader_host(chrome::kChromeUIChromeSigninHost);
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled=*/
+        {
+            ash::standalone_browser::features::kLacrosOnly,
+            ash::standalone_browser::features::kLacrosProfileMigrationForceOff,
+        },
+        /*disabled=*/{});
+  }
+
+  void RunEduCoexistenceTest(const std::string& test_path) {
+    RunTest(base::StrCat({"chromeos/edu_coexistence/", test_path}),
+            "mocha.run()");
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(EduCoexistenceWithArcRestrictionsMochaTest, App) {
+  RunEduCoexistenceTest("edu_coexistence_app_with_arc_picker_test.js");
+}
diff --git a/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.js b/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.js
deleted file mode 100644
index 0561628..0000000
--- a/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.js
+++ /dev/null
@@ -1,38 +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.
-
-/** @fileoverview Runs the EDU Coexistence flow tests. */
-
-GEN_INCLUDE(['//chrome/test/data/webui/chromeos/polymer_browser_test_base.js']);
-
-GEN('#include "content/public/test/browser_test.h"');
-GEN('#include "ash/constants/ash_features.h"');
-
-const EduCoexistenceTest = class extends PolymerTest {};
-
-const tests = [
-  ['App', 'edu_coexistence_app_test.js'],
-  ['Controller', 'edu_coexistence_controller_test.js'],
-  ['Ui', 'edu_coexistence_ui_test.js'],
-];
-
-tests.forEach(test => registerTest(...test));
-
-/*
- * Add a `caseName` to a specific test to disable it i.e. 'DISABLED_All'
- * @param {string} testName
- * @param {string} module
- * @param {string} caseName
- */
-function registerTest(testName, module, caseName) {
-  const className = `EduCoexistence${testName}`;
-  this[className] = class extends EduCoexistenceTest {
-    /** @override */
-    get browsePreload() {
-      return `chrome://chrome-signin/test_loader.html` +
-          `?module=chromeos/edu_coexistence/${module}`;
-    }
-  };
-  TEST_F(className, caseName || 'All', () => mocha.run());
-}
diff --git a/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_with_arc_restrictions_browsertest.js b/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_with_arc_restrictions_browsertest.js
deleted file mode 100644
index a337c0af..0000000
--- a/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_with_arc_restrictions_browsertest.js
+++ /dev/null
@@ -1,46 +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.
-
-GEN_INCLUDE(['//chrome/test/data/webui/chromeos/polymer_browser_test_base.js']);
-
-GEN('#include "ash/constants/ash_features.h"');
-GEN('#include "chromeos/ash/components/standalone_browser/standalone_browser_features.h"');
-GEN('#include "content/public/test/browser_test.h"');
-
-// TODO(crbug.com/1347746): Merge this test suite with the EduCoexistenceTest
-// after the feature is launched.
-const EduCoexistenceTestWithArcRestrictions = class extends PolymerTest {
-  get featureList() {
-    return {
-      enabled: [
-        'ash::standalone_browser::features::kLacrosOnly',
-        'ash::standalone_browser::features::kLacrosProfileMigrationForceOff',
-      ],
-    };
-  }
-};
-
-const tests = [
-  ['AppWithArcPicker', 'edu_coexistence_app_with_arc_picker_test.js'],
-];
-
-tests.forEach(test => registerTest(...test));
-
-/*
- * Add a `caseName` to a specific test to disable it i.e. 'DISABLED_All'
- * @param {string} testName
- * @param {string} module
- * @param {string} caseName
- */
-function registerTest(testName, module, caseName) {
-  const className = `EduCoexistence${testName}_WithArcRestrictions`;
-  this[className] = class extends EduCoexistenceTestWithArcRestrictions {
-    /** @override */
-    get browsePreload() {
-      return `chrome://chrome-signin/test_loader.html` +
-          `?module=chromeos/edu_coexistence/${module}`;
-    }
-  };
-  TEST_F(className, caseName || 'All', () => mocha.run());
-}
diff --git a/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.cc b/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.cc
new file mode 100644
index 0000000..643060e
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.cc
@@ -0,0 +1,52 @@
+// 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/strings/strcat.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/web_ui_mocha_browser_test.h"
+#include "content/public/test/browser_test.h"
+
+class ParentAccessMochaTest : public WebUIMochaBrowserTest {
+ protected:
+  ParentAccessMochaTest() {
+    set_test_loader_host(chrome::kChromeUIParentAccessHost);
+  }
+
+  void RunParentAccessTest(const std::string& test_path) {
+    RunTest(base::StrCat({"chromeos/parent_access/", test_path}),
+            "mocha.run()");
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(ParentAccessMochaTest, App) {
+  RunParentAccessTest("parent_access_app_test.js");
+}
+
+IN_PROC_BROWSER_TEST_F(ParentAccessMochaTest, After) {
+  RunParentAccessTest("parent_access_after_test.js");
+}
+
+IN_PROC_BROWSER_TEST_F(ParentAccessMochaTest, Controller) {
+  RunParentAccessTest("parent_access_controller_test.js");
+}
+
+IN_PROC_BROWSER_TEST_F(ParentAccessMochaTest, Disabled) {
+  RunParentAccessTest("parent_access_disabled_test.js");
+}
+
+IN_PROC_BROWSER_TEST_F(ParentAccessMochaTest, UiHandler) {
+  RunParentAccessTest("parent_access_ui_handler_test.js");
+}
+
+IN_PROC_BROWSER_TEST_F(ParentAccessMochaTest, Ui) {
+  RunParentAccessTest("parent_access_ui_test.js");
+}
+
+IN_PROC_BROWSER_TEST_F(ParentAccessMochaTest, ExtensionApprovals) {
+  RunParentAccessTest("extension_approvals_test.js");
+}
+
+IN_PROC_BROWSER_TEST_F(ParentAccessMochaTest, WebviewManager) {
+  RunParentAccessTest("webview_manager_test.js");
+}
diff --git a/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.js b/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.js
deleted file mode 100644
index e2a1ed7..0000000
--- a/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.js
+++ /dev/null
@@ -1,43 +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.
-
-/** @fileoverview Runs the Parent Access flow tests. */
-
-GEN_INCLUDE(['//chrome/test/data/webui/chromeos/polymer_browser_test_base.js']);
-
-GEN('#include "chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h"');
-GEN('#include "content/public/test/browser_test.h"');
-
-this.ParentAccessBrowserTest = class extends PolymerTest {};
-
-const tests = [
-  ['ExtensionApprovals', 'extension_approvals_test.js'],
-  ['ParentAccessAfter', 'parent_access_after_test.js'],
-  ['ParentAccessApp', 'parent_access_app_test.js'],
-  ['ParentAccessController', 'parent_access_controller_test.js'],
-  ['ParentAccessDisabled', 'parent_access_disabled_test.js'],
-  ['ParentAccessUi', 'parent_access_ui_test.js'],
-  ['ParentAccessUiHandler', 'parent_access_ui_handler_test.js'],
-  ['WebviewManager', 'webview_manager_test.js'],
-];
-
-tests.forEach(test => registerTest(...test));
-
-/*
- * Add a `caseName` to a specific test to disable it i.e. 'DISABLED_All'
- * @param {string} testName
- * @param {string} module
- * @param {string} caseName
- */
-function registerTest(testName, module, caseName) {
-  const className = `ParentAccess${testName}`;
-  this[className] = class extends ParentAccessBrowserTest {
-    /** @override */
-    get browsePreload() {
-      return `chrome://parent-access/test_loader.html` +
-          `?module=chromeos/parent_access/${module}`;
-    }
-  };
-  TEST_F(className, caseName || 'All', () => mocha.run());
-}
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_sea_pen_browsertest.cc b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_sea_pen_browsertest.cc
index d0e57c4..74ed52a 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_sea_pen_browsertest.cc
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_sea_pen_browsertest.cc
@@ -2,14 +2,39 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/wallpaper/sea_pen_image.h"
+#include "ash/wallpaper/sea_pen_wallpaper_manager.h"
+#include "ash/webui/common/mojom/sea_pen.mojom.h"
 #include "ash/webui/personalization_app/test/personalization_app_mojom_banned_mocha_test_base.h"
+#include "base/functional/callback_helpers.h"
+#include "base/test/test_future.h"
 #include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_mocha_test_base.h"
+#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_utils.h"
+#include "chrome/browser/ash/wallpaper_handlers/sea_pen_utils.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "components/manta/features.h"
 #include "content/public/test/browser_test.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/image/image_unittest_util.h"
 
 // TODO(b/312208348) move this test to ash common sea_pen browsertest.
 
 namespace ash::personalization_app {
 
+namespace {
+
+std::string CreateJpgBytes() {
+  SkBitmap bitmap = gfx::test::CreateBitmap(1);
+  bitmap.allocN32Pixels(1, 1);
+  std::vector<unsigned char> data;
+  gfx::JPEGCodec::Encode(bitmap, /*quality=*/100, &data);
+  return std::string(data.begin(), data.end());
+}
+
+}  // namespace
+
 // Tests state management and logic in SeaPen.
 using SeaPenControllerTest = PersonalizationAppMojomBannedMochaTestBase;
 
@@ -23,17 +48,54 @@
 class PersonalizationAppSeaPenBrowserTest
     : public PersonalizationAppMochaTestBase {
  public:
-  PersonalizationAppSeaPenBrowserTest() = default;
+  PersonalizationAppSeaPenBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {
+            ::manta::features::kMantaService,
+            ::ash::features::kSeaPen,
+            ::ash::features::kFeatureManagementSeaPen,
+        },
+        {});
+  }
   PersonalizationAppSeaPenBrowserTest(
       const PersonalizationAppSeaPenBrowserTest&) = delete;
   PersonalizationAppSeaPenBrowserTest& operator=(
       const PersonalizationAppSeaPenBrowserTest&) = delete;
 
   ~PersonalizationAppSeaPenBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    PersonalizationAppMochaTestBase::SetUpOnMainThread();
+
+    //  Creates a fake SeaPen image and saves it to disk.
+    auto* sea_pen_wallpaper_manager = SeaPenWallpaperManager::GetInstance();
+    DCHECK(sea_pen_wallpaper_manager);
+    const AccountId account_id = GetAccountId(browser()->profile());
+    const SeaPenImage sea_pen_image = {CreateJpgBytes(), 323};
+    const base::flat_map<mojom::SeaPenTemplateChip, mojom::SeaPenTemplateOption>
+        options({{mojom::SeaPenTemplateChip::kFlowerColor,
+                  mojom::SeaPenTemplateOption::kFlowerColorBlue},
+                 {mojom::SeaPenTemplateChip::kFlowerType,
+                  mojom::SeaPenTemplateOption::kFlowerTypeRose}});
+    const mojom::SeaPenQueryPtr search_query =
+        mojom::SeaPenQuery::NewTemplateQuery(mojom::SeaPenTemplateQuery::New(
+            mojom::SeaPenTemplateId::kFlower, options,
+            mojom::SeaPenUserVisibleQuery::New("test template query",
+                                               "test template title")));
+    ASSERT_TRUE(wallpaper_handlers::IsValidTemplateQuery(
+        search_query->get_template_query()));
+    base::test::TestFuture<const gfx::ImageSkia&> save_image_future;
+    sea_pen_wallpaper_manager->DecodeAndSaveSeaPenImage(
+        account_id, sea_pen_image, std::move(search_query),
+        save_image_future.GetCallback());
+    ASSERT_FALSE(save_image_future.Get().isNull());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// Flaky test disabled b/329149811.
-IN_PROC_BROWSER_TEST_F(PersonalizationAppSeaPenBrowserTest, DISABLED_SeaPen) {
+IN_PROC_BROWSER_TEST_F(PersonalizationAppSeaPenBrowserTest, SeaPen) {
   RunTestWithoutTestLoader(
       "chromeos/personalization_app/personalization_app_test.js",
       "runMochaSuite('sea pen')");
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test.ts
index 0ffaf3e7..054dbba8 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test.ts
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {DynamicColorElement, getThemeProvider, GooglePhotosAlbumsElement, GooglePhotosCollectionElement, GooglePhotosSharedAlbumDialogElement, PersonalizationRouterElement, PersonalizationThemeElement, SeaPenRouterElement, SeaPenTemplateQueryElement, setTransitionsEnabled, WallpaperCollectionsElement, WallpaperGridItemElement, WallpaperImagesElement} from 'chrome://personalization/js/personalization_app.js';
+import {DynamicColorElement, getThemeProvider, GooglePhotosAlbumsElement, GooglePhotosCollectionElement, GooglePhotosSharedAlbumDialogElement, PersonalizationRouterElement, PersonalizationThemeElement, SeaPenImagesElement, SeaPenPaths, SeaPenRecentWallpapersElement, SeaPenRouterElement, SeaPenTemplateQueryElement, setTransitionsEnabled, WallpaperCollectionsElement, WallpaperGridItemElement, WallpaperImagesElement} from 'chrome://personalization/js/personalization_app.js';
 import {CrButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
+import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
+import {CrIconButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
 import {assertInstanceof} from 'chrome://resources/js/assert.js';
 import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
 import {IronSelectorElement} from 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
@@ -520,66 +522,68 @@
     return wallpaperSubpage;
   }
 
-  setup(async () => {
-    // Reset to default state before each test to reduce order dependencies.
-    await window.personalizationTestApi.reset();
+  async function getSeaPenRouter(): Promise<SeaPenRouterElement> {
+    const subpage = getWallpaperSubpage();
+    const seaPenTile = await waitUntil(
+        () => subpage.shadowRoot?.querySelector('wallpaper-collections')
+                  ?.shadowRoot?.querySelector<WallpaperGridItemElement>(
+                      `wallpaper-grid-item[aria-disabled='false']` +
+                      `[data-sea-pen]`),
+        'waiting for sea-pen-tile');
+    seaPenTile.click();
+    const seaPenRouter = await waitUntil(
+        () => getRouter().shadowRoot?.querySelector<SeaPenRouterElement>(
+            'sea-pen-router')!,
+        'waiting for sea-pen-router');
+    return seaPenRouter;
+  }
 
+  async function getSeaPenTemplateQuery(templateIndex: number):
+      Promise<SeaPenTemplateQueryElement> {
+    const seaPenRouter = SeaPenRouterElement.instance();
+    const templates = await waitUntil(
+        () => seaPenRouter.shadowRoot?.querySelector('sea-pen-templates')
+                  ?.shadowRoot?.querySelectorAll<WallpaperGridItemElement>(
+                      `wallpaper-grid-item[data-sea-pen-image]`),
+        'waiting for sea-pen-tile');
+    templates[templateIndex]!.click();
+
+    return await waitUntil(
+        () =>
+            seaPenRouter.shadowRoot?.querySelector<SeaPenTemplateQueryElement>(
+                'sea-pen-template-query')!,
+        'waiting for sea-pen-template-query');
+  }
+
+  setup(async () => {
     // Disables transition animation for tests.
     setTransitionsEnabled(false);
 
+    // Reset to default state before each test to reduce order dependencies.
+    await window.personalizationTestApi.reset();
+
     clickWallpaperPreviewLink();
   });
 
   test('has selected wallpaper on root page', async () => {
-    const subpage = getWallpaperSubpage();
-
-    const seaPenTile = await waitUntil(
-        () => subpage.shadowRoot?.querySelector('wallpaper-collections')
-                  ?.shadowRoot?.querySelector<WallpaperGridItemElement>(
-                      `wallpaper-grid-item[aria-disabled='false']` +
-                      `[data-sea-pen]`),
-        'waiting for sea-pen-tile');
-    seaPenTile.click();
+    await getSeaPenRouter();
 
     const wallpaperSelected = await waitUntil(
         () => getRouter().shadowRoot?.getElementById('wallpaperSelected')!,
-        'waiting for sea-pen-router wallpaper-selected');
+        'waiting for sea-pen-router wallpaper-selected', /*intervalMs=*/ 500,
+        /*timeoutMs=*/ 3001);
     assertTrue(!!wallpaperSelected, 'wallpaper-selected should exist');
   });
 
   test('hides selected wallpaper on non root page', async () => {
-    const subpage = getWallpaperSubpage();
-
-    const seaPenTile = await waitUntil(
-        () => subpage.shadowRoot?.querySelector('wallpaper-collections')
-                  ?.shadowRoot?.querySelector<WallpaperGridItemElement>(
-                      `wallpaper-grid-item[aria-disabled='false']` +
-                      `[data-sea-pen]`),
-        'waiting for sea-pen-tile');
-    seaPenTile.click();
-
-    const seaPenRouter = await waitUntil(
-        () => getRouter().shadowRoot?.querySelector<SeaPenRouterElement>(
-            'sea-pen-router')!,
-        'waiting for sea-pen-router');
+    await getSeaPenRouter();
 
     let wallpaperSelected =
         getRouter().shadowRoot?.getElementById('wallpaperSelected')!;
     assertTrue(!!wallpaperSelected, 'wallpaper-selected should exist');
     assertNotEquals(getComputedStyle(wallpaperSelected).display, 'none');
 
-    const templates = await waitUntil(
-        () => seaPenRouter.shadowRoot?.querySelector('sea-pen-templates')
-                  ?.shadowRoot?.querySelectorAll<WallpaperGridItemElement>(
-                      `wallpaper-grid-item[data-sea-pen-image]`),
-        'waiting for sea-pen-tile');
-    templates[0]!.click();
-
-    const seaPenTemplateQuery = await waitUntil(
-        () =>
-            seaPenRouter.shadowRoot?.querySelector<SeaPenTemplateQueryElement>(
-                'sea-pen-template-query')!,
-        'waiting for sea-pen-template-query');
+    const seaPenTemplateQuery = await getSeaPenTemplateQuery(0);
     assertTrue(!!seaPenTemplateQuery, 'sea-pen-template-query should exist');
 
     wallpaperSelected =
@@ -588,20 +592,7 @@
   });
 
   test('show more option chips', async () => {
-    const subpage = getWallpaperSubpage();
-
-    const seaPenTile = await waitUntil(
-        () => subpage.shadowRoot?.querySelector('wallpaper-collections')
-                  ?.shadowRoot?.querySelector<WallpaperGridItemElement>(
-                      `wallpaper-grid-item[aria-disabled='false']` +
-                      `[data-sea-pen]`),
-        'waiting for sea-pen-tile');
-    seaPenTile.click();
-
-    const seaPenRouter = await waitUntil(
-        () => getRouter().shadowRoot?.querySelector<SeaPenRouterElement>(
-            'sea-pen-router')!,
-        'waiting for sea-pen-router');
+    const seaPenRouter = await getSeaPenRouter();
 
     const acceptTermsButton = await waitUntil(
         () => seaPenRouter.shadowRoot
@@ -610,18 +601,7 @@
         'wait for accept button to load');
     acceptTermsButton.click();
 
-    const templates = await waitUntil(
-        () => seaPenRouter.shadowRoot?.querySelector('sea-pen-templates')
-                  ?.shadowRoot?.querySelectorAll<WallpaperGridItemElement>(
-                      `wallpaper-grid-item[data-sea-pen-image]`),
-        'waiting for sea-pen-tile');
-    templates[6]!.click();
-
-    const seaPenTemplateQuery = await waitUntil(
-        () =>
-            seaPenRouter.shadowRoot?.querySelector<SeaPenTemplateQueryElement>(
-                'sea-pen-template-query')!,
-        'waiting for Characters template');
+    const seaPenTemplateQuery = await getSeaPenTemplateQuery(6);
     assertTrue(!!seaPenTemplateQuery, 'Characters template should show up');
 
     const seaPenChips = await waitUntil(
@@ -659,6 +639,171 @@
         numOptionsInitiallyShown, options.length,
         'more options should show up after clicking expand button');
   });
+
+  [false, true].forEach((useInspire, i) => {
+    test(`creates images with inspire ${useInspire}`, async () => {
+      const seaPenRouter = await getSeaPenRouter();
+      const seaPenTemplateQuery = await getSeaPenTemplateQuery(6);
+      {
+        // Creates images.
+        assertTrue(!!seaPenTemplateQuery, 'Characters template should show up');
+        if (useInspire) {
+          seaPenTemplateQuery.shadowRoot?.getElementById('inspire')!.click();
+        } else {
+          seaPenTemplateQuery.shadowRoot?.getElementById(
+                                            'searchButton')!.click();
+        }
+      }
+
+      {
+        // Selects an image.
+        const seaPenImages = await waitUntil(
+            () => seaPenRouter.shadowRoot?.querySelector<SeaPenImagesElement>(
+                'sea-pen-images'),
+            'waiting for sea-pen-images');
+
+        const thumbnailsToClick = await waitUntil(
+            () => Array.from(seaPenImages!.shadowRoot!.querySelectorAll<
+                             WallpaperGridItemElement>(
+                `wallpaper-grid-item[aria-disabled='false'][data-sea-pen-image]`)),
+            'waiting for thumbnails load');
+        assertTrue(!!thumbnailsToClick, 'thumbnails should show up');
+
+        thumbnailsToClick[i]!.click();
+        assertTrue(
+            thumbnailsToClick[i]?.getAttribute('aria-selected') === 'true',
+            'thumbnail should be selected');
+      }
+
+      const expectedWallpaperTitle =
+          seaPenTemplateQuery.shadowRoot?.getElementById('template')
+              ?.innerText?.replace(/\n/gmi, ' ')
+              .trim();
+
+      // Goes back to sea pen root page.
+      seaPenRouter.goToRoute(SeaPenPaths.ROOT);
+
+      {
+        // Verifies the image is set properly.
+        const wallpaperSelected = await waitUntil(
+            () => getRouter().shadowRoot?.getElementById('wallpaperSelected')!,
+            'waiting for sea-pen-router wallpaper-selected');
+        assertTrue(!!wallpaperSelected, 'wallpaper-selected should exist');
+        const textContainer = await waitUntil(
+            () => wallpaperSelected.shadowRoot?.getElementById('textContainer'),
+            'waiting for wallpaper text container', /*intervalMs=*/ 500,
+            /*timeoutMs=*/ 3001);
+        assertTrue(!!textContainer, 'wallpaper text container exists');
+        assertEquals(
+            expectedWallpaperTitle,
+            textContainer?.querySelector('#imageTitle')?.textContent?.trim(),
+            'image title is correct');
+      }
+
+      {
+        // Verifies the image is visible in recent images.
+        const recentImages = await waitUntil(
+            () => seaPenRouter.shadowRoot
+                      ?.querySelector<SeaPenRecentWallpapersElement>(
+                          'sea-pen-recent-wallpapers'),
+            'waiting for sea-pen-recent-wallpapers');
+        assertTrue(!!recentImages, 'recent images should exist');
+
+        const menuButton = recentImages.shadowRoot?.querySelector<
+            CrIconButtonElement>(
+            `wallpaper-grid-item[aria-selected=true] + .menu-icon-container cr-icon-button`);
+        assertTrue(!!menuButton, 'menu button exists');
+        menuButton!.click();
+
+        const aboutButton = await waitUntil(
+            () => recentImages.shadowRoot?.querySelector<HTMLButtonElement>(
+                `wallpaper-grid-item[aria-selected=true] ~ cr-action-menu .wallpaper-info-option`),
+            'waiting for about wallpaper button');
+        assertTrue(!!aboutButton, 'about wallpaper button exists');
+        aboutButton!.click();
+
+        const dialog = await waitUntil(
+            () => recentImages.shadowRoot?.querySelector<CrDialogElement>(
+                'cr-dialog'),
+            'waiting for about wallpaper dialog');
+        assertTrue(!!dialog, 'about wallpaper dialog exists');
+
+        const promptInfo =
+            recentImages.shadowRoot?.querySelector<HTMLParagraphElement>(
+                'p.about-prompt-info');
+        assertTrue(
+            !!promptInfo?.textContent?.trim().includes(expectedWallpaperTitle!),
+            `prompt info should include ${expectedWallpaperTitle}`);
+      }
+    });
+  });
+
+  test('selects recent image', async () => {
+    const seaPenRouter = await getSeaPenRouter();
+    const recentImages = await waitUntil(
+        () => seaPenRouter.shadowRoot
+                  ?.querySelector<SeaPenRecentWallpapersElement>(
+                      'sea-pen-recent-wallpapers'),
+        'waiting for sea-pen-recent-wallpapers');
+    assertTrue(!!recentImages, 'recent images should exist');
+
+    {
+      // Selects first non-selected image.
+      const image =
+          recentImages.shadowRoot?.querySelector<WallpaperGridItemElement>(
+              `wallpaper-grid-item[aria-selected=false]`);
+      assertTrue(!!image, 'image exists');
+      image!.click();
+      assertTrue(
+          image?.getAttribute('aria-selected') === 'true',
+          'image should be selected');
+    }
+
+    {
+      // Verifies the image is set properly.
+      const menuButton = recentImages.shadowRoot?.querySelector<
+          CrIconButtonElement>(
+          `wallpaper-grid-item[aria-selected=true] + .menu-icon-container cr-icon-button`);
+      menuButton!.click();
+
+      const aboutButton = await waitUntil(
+          () => recentImages.shadowRoot?.querySelector<HTMLButtonElement>(
+              `wallpaper-grid-item[aria-selected=true] ~ cr-action-menu .wallpaper-info-option`),
+          'waiting for about wallpaper button');
+      assertTrue(!!aboutButton, 'about wallpaper button exists');
+      aboutButton!.click();
+
+      const dialog = await waitUntil(
+          () => recentImages.shadowRoot?.querySelector<CrDialogElement>(
+              'cr-dialog'),
+          'waiting for about wallpaper dialog');
+      assertTrue(!!dialog, 'about wallpaper dialog exists');
+
+      const promptInfo =
+          recentImages.shadowRoot?.querySelector<HTMLParagraphElement>(
+              'p.about-prompt-info');
+      const promptText = promptInfo?.textContent?.trim();
+
+      // Verifies the image is set properly.
+      const wallpaperSelected = await waitUntil(
+          () => getRouter().shadowRoot?.getElementById('wallpaperSelected')!,
+          'waiting for sea-pen-router wallpaper-selected');
+      assertTrue(!!wallpaperSelected, 'wallpaper-selected should exist');
+      const textContainer = await waitUntil(
+          () => wallpaperSelected.shadowRoot?.getElementById('textContainer'),
+          'waiting for wallpaper text container', /*intervalMs=*/ 500,
+          /*timeoutMs=*/ 3001);
+      assertTrue(!!textContainer, 'wallpaper text container exists');
+      await waitUntil(
+          () => promptText?.includes(
+              textContainer.querySelector('#imageTitle')?.textContent?.trim()!),
+          () => `failed waiting for expected image title ` +
+              `after selecting wallpaper. ` +
+              `html:\n${textContainer.outerHTML}`,
+          /*intervalMs=*/ 500,
+          /*timeoutMs=*/ 3001);
+    }
+  });
 });
 
 suite('dynamic color', () => {
diff --git a/chrome/test/data/webui/cr_components/BUILD.gn b/chrome/test/data/webui/cr_components/BUILD.gn
index 8a004a8..023fbce 100644
--- a/chrome/test/data/webui/cr_components/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//crypto/features.gni")
 import("../build_webui_tests.gni")
-import("./omnibox/realbox.gni")
+import("./searchbox/realbox.gni")
 
 assert(!is_android && !is_ios)
 
@@ -61,8 +61,8 @@
     "//ui/webui/resources/cr_components/managed_dialog:build_ts",
     "//ui/webui/resources/cr_components/managed_footnote:build_ts",
     "//ui/webui/resources/cr_components/most_visited:build_ts",
-    "//ui/webui/resources/cr_components/omnibox:build_ts",
     "//ui/webui/resources/cr_components/page_image_service:build_ts",
+    "//ui/webui/resources/cr_components/searchbox:build_ts",
     "//ui/webui/resources/cr_components/settings_prefs:build_ts",
     "//ui/webui/resources/cr_components/theme_color_picker:build_ts",
     "//ui/webui/resources/cr_elements:build_ts",
diff --git a/chrome/test/data/webui/cr_components/cr_components_browsertest.cc b/chrome/test/data/webui/cr_components/cr_components_browsertest.cc
index b85c74a..5b5ad3ea 100644
--- a/chrome/test/data/webui/cr_components/cr_components_browsertest.cc
+++ b/chrome/test/data/webui/cr_components/cr_components_browsertest.cc
@@ -94,20 +94,20 @@
   RunTest("cr_components/localized_link_test.js", "mocha.run()");
 }
 
-typedef WebUIMochaBrowserTest CrComponentsOmniboxTest;
-IN_PROC_BROWSER_TEST_F(CrComponentsOmniboxTest, RealboxMatchTest) {
+typedef WebUIMochaBrowserTest CrComponentsSearchboxTest;
+IN_PROC_BROWSER_TEST_F(CrComponentsSearchboxTest, RealboxMatchTest) {
   set_test_loader_host(chrome::kChromeUINewTabPageHost);
-  RunTest("cr_components/omnibox/realbox_match_test.js", "mocha.run()");
+  RunTest("cr_components/searchbox/realbox_match_test.js", "mocha.run()");
 }
 
-IN_PROC_BROWSER_TEST_F(CrComponentsOmniboxTest, RealboxTest) {
+IN_PROC_BROWSER_TEST_F(CrComponentsSearchboxTest, RealboxTest) {
   set_test_loader_host(chrome::kChromeUINewTabPageHost);
-  RunTest("cr_components/omnibox/realbox_test.js", "mocha.run()");
+  RunTest("cr_components/searchbox/realbox_test.js", "mocha.run()");
 }
 
-IN_PROC_BROWSER_TEST_F(CrComponentsOmniboxTest, RealboxLensTest) {
+IN_PROC_BROWSER_TEST_F(CrComponentsSearchboxTest, RealboxLensTest) {
   set_test_loader_host(chrome::kChromeUINewTabPageHost);
-  RunTest("cr_components/omnibox/realbox_lens_test.js", "mocha.run()");
+  RunTest("cr_components/searchbox/realbox_lens_test.js", "mocha.run()");
 }
 
 IN_PROC_BROWSER_TEST_F(CrComponentsTest, SettingsPrefs) {
diff --git a/chrome/test/data/webui/cr_components/history_embeddings/history_embeddings_test.ts b/chrome/test/data/webui/cr_components/history_embeddings/history_embeddings_test.ts
index e5cc9224..ef75daa 100644
--- a/chrome/test/data/webui/cr_components/history_embeddings/history_embeddings_test.ts
+++ b/chrome/test/data/webui/cr_components/history_embeddings/history_embeddings_test.ts
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://history/strings.m.js';
 import 'chrome://resources/cr_components/history_embeddings/history_embeddings.js';
 
 import {HistoryEmbeddingsBrowserProxyImpl} from 'chrome://resources/cr_components/history_embeddings/browser_proxy.js';
 import type {HistoryEmbeddingsElement} from 'chrome://resources/cr_components/history_embeddings/history_embeddings.js';
 import {PageHandlerRemote} from 'chrome://resources/cr_components/history_embeddings/history_embeddings.mojom-webui.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 suite('cr-history-embeddings', () => {
   let element: HistoryEmbeddingsElement;
@@ -32,4 +35,39 @@
     await handler.whenCalled('doSomething');
     assertEquals(1, handler.getCallCount('doSomething'));
   });
+
+  test('DisplaysHeading', () => {
+    loadTimeData.overrideValues(
+        {historyEmbeddingsHeading: 'searched for "$1"'});
+    element.searchQuery = 'my query';
+    assertEquals(
+        'searched for "my query"', element.$.heading.textContent!.trim());
+  });
+
+  test('DisplaysResults', async () => {
+    element.results = [
+      {domain: 'google.com', title: 'Google', url: 'http://google.com'},
+      {domain: 'youtube.com', title: 'Youtube', url: 'http://youtube.com'},
+    ];
+    await flushTasks();
+    const resultsElements =
+        element.shadowRoot!.querySelectorAll('cr-url-list-item');
+    assertEquals(2, resultsElements.length);
+    assertEquals('Google', resultsElements[0]!.title);
+    assertEquals('Youtube', resultsElements[1]!.title);
+  });
+
+  test('FiresClick', async () => {
+    element.results = [
+      {domain: 'google.com', title: 'Google', url: 'http://google.com'},
+      {domain: 'youtube.com', title: 'Youtube', url: 'http://youtube.com'},
+    ];
+    await flushTasks();
+    const resultsElements =
+        element.shadowRoot!.querySelectorAll('cr-url-list-item');
+    const resultClickEventPromise = eventToPromise('result-click', element);
+    resultsElements[0]!.click();
+    const resultClickEvent = await resultClickEventPromise;
+    assertEquals(element.results[0], resultClickEvent.detail);
+  });
 });
diff --git a/chrome/test/data/webui/cr_components/omnibox/realbox.gni b/chrome/test/data/webui/cr_components/omnibox/realbox.gni
deleted file mode 100644
index 7df534c..0000000
--- a/chrome/test/data/webui/cr_components/omnibox/realbox.gni
+++ /dev/null
@@ -1,11 +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.
-
-realbox_test_files = [
-  "omnibox/realbox_lens_test.ts",
-  "omnibox/realbox_match_test.ts",
-  "omnibox/realbox_test_utils.ts",
-  "omnibox/realbox_test.ts",
-  "omnibox/test_realbox_browser_proxy.ts",
-]
diff --git a/chrome/test/data/webui/cr_components/searchbox/realbox.gni b/chrome/test/data/webui/cr_components/searchbox/realbox.gni
new file mode 100644
index 0000000..efa5a83
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/searchbox/realbox.gni
@@ -0,0 +1,11 @@
+# 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.
+
+realbox_test_files = [
+  "searchbox/realbox_lens_test.ts",
+  "searchbox/realbox_match_test.ts",
+  "searchbox/realbox_test_utils.ts",
+  "searchbox/realbox_test.ts",
+  "searchbox/test_realbox_browser_proxy.ts",
+]
diff --git a/chrome/test/data/webui/cr_components/omnibox/realbox_lens_test.ts b/chrome/test/data/webui/cr_components/searchbox/realbox_lens_test.ts
similarity index 98%
rename from chrome/test/data/webui/cr_components/omnibox/realbox_lens_test.ts
rename to chrome/test/data/webui/cr_components/searchbox/realbox_lens_test.ts
index ed763cb..17e69910 100644
--- a/chrome/test/data/webui/cr_components/omnibox/realbox_lens_test.ts
+++ b/chrome/test/data/webui/cr_components/searchbox/realbox_lens_test.ts
@@ -6,7 +6,7 @@
 
 import type {RealboxElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {BrowserProxyImpl, MetricsReporterImpl, mojoString16, RealboxBrowserProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import type {AutocompleteMatch} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import type {AutocompleteMatch} from 'chrome://resources/cr_components/searchbox/omnibox.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PageMetricsCallbackRouter} from 'chrome://resources/js/metrics_reporter.mojom-webui.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_components/omnibox/realbox_match_test.ts b/chrome/test/data/webui/cr_components/searchbox/realbox_match_test.ts
similarity index 96%
rename from chrome/test/data/webui/cr_components/omnibox/realbox_match_test.ts
rename to chrome/test/data/webui/cr_components/searchbox/realbox_match_test.ts
index fe7cdab..c5824d24 100644
--- a/chrome/test/data/webui/cr_components/omnibox/realbox_match_test.ts
+++ b/chrome/test/data/webui/cr_components/searchbox/realbox_match_test.ts
@@ -3,11 +3,11 @@
 // found in the LICENSE file.
 
 import 'chrome://new-tab-page/strings.m.js';
-import 'chrome://resources/cr_components/omnibox/realbox_match.js';
+import 'chrome://resources/cr_components/searchbox/realbox_match.js';
 
-import {NavigationPredictor} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
-import {RealboxBrowserProxy} from 'chrome://resources/cr_components/omnibox/realbox_browser_proxy.js';
-import type {RealboxMatchElement} from 'chrome://resources/cr_components/omnibox/realbox_match.js';
+import {NavigationPredictor} from 'chrome://resources/cr_components/searchbox/omnibox.mojom-webui.js';
+import {RealboxBrowserProxy} from 'chrome://resources/cr_components/searchbox/realbox_browser_proxy.js';
+import type {RealboxMatchElement} from 'chrome://resources/cr_components/searchbox/realbox_match.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/cr_components/omnibox/realbox_test.ts b/chrome/test/data/webui/cr_components/searchbox/realbox_test.ts
similarity index 96%
rename from chrome/test/data/webui/cr_components/omnibox/realbox_test.ts
rename to chrome/test/data/webui/cr_components/searchbox/realbox_test.ts
index 894c9b9..54c0be6 100644
--- a/chrome/test/data/webui/cr_components/omnibox/realbox_test.ts
+++ b/chrome/test/data/webui/cr_components/searchbox/realbox_test.ts
@@ -6,8 +6,8 @@
 
 import type {RealboxElement, RealboxIconElement, RealboxMatchElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {$$, BrowserProxyImpl, decodeString16, MetricsReporterImpl, mojoString16, RealboxBrowserProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import type {AutocompleteMatch} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
-import {NavigationPredictor, RenderType, SideType} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import type {AutocompleteMatch} from 'chrome://resources/cr_components/searchbox/omnibox.mojom-webui.js';
+import {NavigationPredictor, RenderType, SideType} from 'chrome://resources/cr_components/searchbox/omnibox.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PageMetricsCallbackRouter} from 'chrome://resources/js/metrics_reporter.mojom-webui.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
@@ -158,8 +158,6 @@
     // Force a synchronous render.
     await testProxy.callbackRouterRemote.$.flushForTesting();
     await waitAfterNextRender(realbox);
-    [...realbox.$.matches.shadowRoot!.querySelectorAll('dom-repeat')].forEach(
-        template => template.render());
     return window.getComputedStyle(realbox.$.matches).display !== 'none';
   }
 
@@ -198,7 +196,7 @@
     // Arrange.
     loadTimeData.overrideValues({
       realboxDefaultIcon:
-          '//resources/cr_components/omnibox/icons/google_g.svg',
+          '//resources/cr_components/searchbox/icons/google_g.svg',
     });
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
     realbox = document.createElement('cr-realbox');
@@ -207,7 +205,7 @@
     // Assert.
     assertStyle(
         realbox.$.icon.$.icon, 'background-image',
-        `url("chrome://resources/cr_components/omnibox/icons/google_g.svg")`);
+        `url("chrome://resources/cr_components/searchbox/icons/google_g.svg")`);
     assertStyle(realbox.$.icon.$.icon, '-webkit-mask-image', 'none');
 
     // Restore.
@@ -254,7 +252,6 @@
   // Test Querying Autocomplete
   //============================================================================
 
-  // TODO(crbug.com/328270499): Uncomment once flakiness is fixed.
   test('left-clicking empty input queries autocomplete', async () => {
     // Query zero-prefix matches.
     realbox.$.input.value = '';
@@ -309,7 +306,6 @@
     assertEquals(0, testProxy.handler.getCallCount('queryAutocomplete'));
   });
 
-  // TODO(crbug.com/328270499): Uncomment once flakiness is fixed.
   test('focusing the input does not query autocomplete', async () => {
     assertEquals(0, testProxy.handler.getCallCount('onFocusChanged'));
     realbox.$.input.value = '';
@@ -321,72 +317,71 @@
     });
   });
 
-  // TODO(crbug.com/328270499): Uncomment once flakiness is fixed.
-    test('tabbing into empty input queries autocomplete', async () => {
-      // Query zero-prefix matches.
-      realbox.$.input.value = '';
-      realbox.$.input.dispatchEvent(new MouseEvent('mousedown', {button: 0}));
-      await testProxy.handler.whenCalled('queryAutocomplete').then((args) => {
-        assertEquals(decodeString16(args.input), realbox.$.input.value);
-        assertFalse(args.preventInlineAutocomplete);
-      });
-      assertEquals(1, testProxy.handler.getCallCount('queryAutocomplete'));
-
-      testProxy.handler.reset();
-
-      // Show zero-prefix matches.
-      const matches = [createSearchMatch(), createUrlMatch()];
-      testProxy.callbackRouterRemote.autocompleteResultChanged({
-        input: mojoString16(''),
-        matches,
-        suggestionGroupsMap: {},
-      });
-      assertTrue(await areMatchesShowing());
-
-      const matchEls =
-          realbox.$.matches.shadowRoot!.querySelectorAll('cr-realbox-match');
-      assertEquals(2, matchEls.length);
-
-      // Tabbing into input does not query autocomplete when matches are
-      // showing.
-      realbox.$.input.dispatchEvent(new KeyboardEvent('keyup', {
-        bubbles: true,
-        cancelable: true,
-        key: 'Tab',
-      }));
-      assertEquals(0, testProxy.handler.getCallCount('queryAutocomplete'));
-
-      // Hide the matches by focusing out.
-      matchEls[0]!.dispatchEvent(new FocusEvent('focusout', {
-        bubbles: true,
-        cancelable: true,
-        composed: true,  // So it propagates across shadow DOM boundary.
-        relatedTarget: document.body,
-      }));
-
-      // Tabbing into empty input queries autocomplete.
-      realbox.$.input.dispatchEvent(new KeyboardEvent('keyup', {
-        bubbles: true,
-        cancelable: true,
-        key: 'Tab',
-      }));
-      await testProxy.handler.whenCalled('queryAutocomplete').then((args) => {
-        assertEquals(decodeString16(args.input), realbox.$.input.value);
-        assertFalse(args.preventInlineAutocomplete);
-      });
-      assertEquals(1, testProxy.handler.getCallCount('queryAutocomplete'));
-
-      testProxy.handler.reset();
-
-      // Tabbing into non-empty input does not query autocomplete.
-      realbox.$.input.value = '   ';
-      realbox.$.input.dispatchEvent(new KeyboardEvent('keyup', {
-        bubbles: true,
-        cancelable: true,
-        key: 'Tab',
-      }));
-      assertEquals(0, testProxy.handler.getCallCount('queryAutocomplete'));
+  test('tabbing into empty input queries autocomplete', async () => {
+    // Query zero-prefix matches.
+    realbox.$.input.value = '';
+    realbox.$.input.dispatchEvent(new MouseEvent('mousedown', {button: 0}));
+    await testProxy.handler.whenCalled('queryAutocomplete').then((args) => {
+      assertEquals(decodeString16(args.input), realbox.$.input.value);
+      assertFalse(args.preventInlineAutocomplete);
     });
+    assertEquals(1, testProxy.handler.getCallCount('queryAutocomplete'));
+
+    testProxy.handler.reset();
+
+    // Show zero-prefix matches.
+    const matches = [createSearchMatch(), createUrlMatch()];
+    testProxy.callbackRouterRemote.autocompleteResultChanged({
+      input: mojoString16(''),
+      matches,
+      suggestionGroupsMap: {},
+    });
+    assertTrue(await areMatchesShowing());
+
+    const matchEls =
+        realbox.$.matches.shadowRoot!.querySelectorAll('cr-realbox-match');
+    assertEquals(2, matchEls.length);
+
+    // Tabbing into input does not query autocomplete when matches are
+    // showing.
+    realbox.$.input.dispatchEvent(new KeyboardEvent('keyup', {
+      bubbles: true,
+      cancelable: true,
+      key: 'Tab',
+    }));
+    assertEquals(0, testProxy.handler.getCallCount('queryAutocomplete'));
+
+    // Hide the matches by focusing out.
+    matchEls[0]!.dispatchEvent(new FocusEvent('focusout', {
+      bubbles: true,
+      cancelable: true,
+      composed: true,  // So it propagates across shadow DOM boundary.
+      relatedTarget: document.body,
+    }));
+
+    // Tabbing into empty input queries autocomplete.
+    realbox.$.input.dispatchEvent(new KeyboardEvent('keyup', {
+      bubbles: true,
+      cancelable: true,
+      key: 'Tab',
+    }));
+    await testProxy.handler.whenCalled('queryAutocomplete').then((args) => {
+      assertEquals(decodeString16(args.input), realbox.$.input.value);
+      assertFalse(args.preventInlineAutocomplete);
+    });
+    assertEquals(1, testProxy.handler.getCallCount('queryAutocomplete'));
+
+    testProxy.handler.reset();
+
+    // Tabbing into non-empty input does not query autocomplete.
+    realbox.$.input.value = '   ';
+    realbox.$.input.dispatchEvent(new KeyboardEvent('keyup', {
+      bubbles: true,
+      cancelable: true,
+      key: 'Tab',
+    }));
+    assertEquals(0, testProxy.handler.getCallCount('queryAutocomplete'));
+  });
 
   test('arrow up/down keys in empty input query autocomplete', async () => {
     // Query zero-prefix matches.
@@ -1542,7 +1537,6 @@
     assertEquals(0, matchEls.length);
   });
 
-  // TODO(crbug.com/328270499): Uncomment once flakiness is fixed.
   test('arrow up/down moves selection / focus', async () => {
     realbox.$.input.focus();
     realbox.$.input.value = 'hello';
diff --git a/chrome/test/data/webui/cr_components/omnibox/realbox_test_utils.ts b/chrome/test/data/webui/cr_components/searchbox/realbox_test_utils.ts
similarity index 96%
rename from chrome/test/data/webui/cr_components/omnibox/realbox_test_utils.ts
rename to chrome/test/data/webui/cr_components/searchbox/realbox_test_utils.ts
index 9337a474..1a232c5 100644
--- a/chrome/test/data/webui/cr_components/omnibox/realbox_test_utils.ts
+++ b/chrome/test/data/webui/cr_components/searchbox/realbox_test_utils.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 {AutocompleteMatch} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import type {AutocompleteMatch} from 'chrome://resources/cr_components/searchbox/omnibox.mojom-webui.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 export function createAutocompleteMatch(): AutocompleteMatch {
diff --git a/chrome/test/data/webui/cr_components/omnibox/test_realbox_browser_proxy.ts b/chrome/test/data/webui/cr_components/searchbox/test_realbox_browser_proxy.ts
similarity index 96%
rename from chrome/test/data/webui/cr_components/omnibox/test_realbox_browser_proxy.ts
rename to chrome/test/data/webui/cr_components/searchbox/test_realbox_browser_proxy.ts
index be246be..b0ab2a1 100644
--- a/chrome/test/data/webui/cr_components/omnibox/test_realbox_browser_proxy.ts
+++ b/chrome/test/data/webui/cr_components/searchbox/test_realbox_browser_proxy.ts
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import type {NavigationPredictor, PageHandlerInterface, PageRemote} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
-import {PageCallbackRouter} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import type {NavigationPredictor, PageHandlerInterface, PageRemote} from 'chrome://resources/cr_components/searchbox/omnibox.mojom-webui.js';
+import {PageCallbackRouter} from 'chrome://resources/cr_components/searchbox/omnibox.mojom-webui.js';
 import type {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
 import type {TimeTicks} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
 import type {Size} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_feedback_buttons_test.ts b/chrome/test/data/webui/cr_elements/cr_feedback_buttons_test.ts
index 7a9a4b4..f16c9b8 100644
--- a/chrome/test/data/webui/cr_elements/cr_feedback_buttons_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_feedback_buttons_test.ts
@@ -8,8 +8,7 @@
 import {CrFeedbackOption} from 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 suite('CrFeedbackButtonsTest', () => {
   let element: CrFeedbackButtonsElement;
@@ -25,7 +24,7 @@
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
     element = document.createElement('cr-feedback-buttons');
     document.body.appendChild(element);
-    await flushTasks();
+    await microtasksFinished();
   });
 
   test('SetsLabels', () => {
@@ -33,31 +32,40 @@
     assertEquals('thumbs down', element.$.thumbsDown.ariaLabel);
   });
 
-  test('TogglesIconState', () => {
+  test('TogglesIconState', async () => {
+    assertEquals(CrFeedbackOption.UNSPECIFIED, element.selectedOption);
     assertEquals('cr:thumbs-up', element.$.thumbsUp.ironIcon);
     assertEquals('false', element.$.thumbsUp.ariaPressed);
     assertEquals('cr:thumbs-down', element.$.thumbsDown.ironIcon);
     assertEquals('false', element.$.thumbsDown.ariaPressed);
 
     element.$.thumbsUp.click();
+    await eventToPromise('selected-option-changed', element);
+    assertEquals(CrFeedbackOption.THUMBS_UP, element.selectedOption);
     assertEquals('cr:thumbs-up-filled', element.$.thumbsUp.ironIcon);
     assertEquals('true', element.$.thumbsUp.ariaPressed);
     assertEquals('cr:thumbs-down', element.$.thumbsDown.ironIcon);
     assertEquals('false', element.$.thumbsDown.ariaPressed);
 
     element.$.thumbsUp.click();
+    await eventToPromise('selected-option-changed', element);
+    assertEquals(CrFeedbackOption.UNSPECIFIED, element.selectedOption);
     assertEquals('cr:thumbs-up', element.$.thumbsUp.ironIcon);
     assertEquals('false', element.$.thumbsUp.ariaPressed);
     assertEquals('cr:thumbs-down', element.$.thumbsDown.ironIcon);
     assertEquals('false', element.$.thumbsDown.ariaPressed);
 
     element.$.thumbsDown.click();
+    await eventToPromise('selected-option-changed', element);
+    assertEquals(CrFeedbackOption.THUMBS_DOWN, element.selectedOption);
     assertEquals('cr:thumbs-up', element.$.thumbsUp.ironIcon);
     assertEquals('false', element.$.thumbsUp.ariaPressed);
     assertEquals('cr:thumbs-down-filled', element.$.thumbsDown.ironIcon);
     assertEquals('true', element.$.thumbsDown.ariaPressed);
 
     element.$.thumbsDown.click();
+    await eventToPromise('selected-option-changed', element);
+    assertEquals(CrFeedbackOption.UNSPECIFIED, element.selectedOption);
     assertEquals('cr:thumbs-up', element.$.thumbsUp.ironIcon);
     assertEquals('false', element.$.thumbsUp.ariaPressed);
     assertEquals('cr:thumbs-down', element.$.thumbsDown.ironIcon);
@@ -82,8 +90,9 @@
     assertEquals(CrFeedbackOption.UNSPECIFIED, noThumbsEventArgs.detail.value);
   });
 
-  test('AcceptsSelectedOptionBinding', () => {
+  test('AcceptsSelectedOptionBinding', async () => {
     element.selectedOption = CrFeedbackOption.THUMBS_UP;
+    await microtasksFinished();
     assertEquals('cr:thumbs-up-filled', element.$.thumbsUp.ironIcon);
     assertEquals('true', element.$.thumbsUp.ariaPressed);
   });
diff --git a/chrome/test/data/webui/cr_elements/cr_grid_focus_test.ts b/chrome/test/data/webui/cr_elements/cr_grid_focus_test.ts
index 6c67a16..55d5e28 100644
--- a/chrome/test/data/webui/cr_elements/cr_grid_focus_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_grid_focus_test.ts
@@ -5,6 +5,7 @@
 import 'chrome://resources/cr_elements/cr_grid/cr_grid.js';
 
 import type {CrGridElement} from 'chrome://resources/cr_elements/cr_grid/cr_grid.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
@@ -294,4 +295,75 @@
     // Assert.
     assertFocus(document.body);
   });
+
+  test('ignoreModifiedKeyEvents', function() {
+    const grid = createGrid(3);
+    grid.ignoreModifiedKeyEvents = true;
+
+    const items = grid.$.items.assignedElements();
+    (items[0] as HTMLElement).focus();
+    assertFocus(items[0]!);
+
+    function assertFocusUnchanged(key: 'ArrowRight'|'ArrowLeft') {
+      assertFocus(items[0]!);
+      keyDownOn(items[0]!, 0, 'alt', key);
+      assertFocus(items[0]!);
+      keyDownOn(items[0]!, 0, 'ctrl', key);
+      assertFocus(items[0]!);
+      keyDownOn(items[0]!, 0, 'meta', key);
+      assertFocus(items[0]!);
+      keyDownOn(items[0]!, 0, 'shift', key);
+      assertFocus(items[0]!);
+    }
+
+    // Test non-modifier events still work.
+    keydown(items[0]!, 'ArrowRight');
+    assertFocus(items[1]!);
+    keydown(items[1]!, 'ArrowLeft');
+    assertFocus(items[0]!);
+
+    // Test modifier events don't change focus.
+    assertFocusUnchanged('ArrowRight');
+    assertFocusUnchanged('ArrowLeft');
+
+    // Test RTL case.
+    grid.dir = 'rtl';
+    assertFocusUnchanged('ArrowRight');
+    assertFocusUnchanged('ArrowLeft');
+
+    keydown(items[0]!, 'ArrowRight');
+    assertFocus(items[2]!);
+    keydown(items[2]!, 'ArrowLeft');
+    assertFocus(items[0]!);
+  });
+
+  // Test cases where keyboard events are coming from children of the slotted
+  // elements and ensure that keyboard navigation still works.
+  test('focusSelector focuses right item', function() {
+    document.body.innerHTML = getTrustedHTML`
+      <cr-grid focus-selector="button">
+        <div><button id="0"></button></div>
+        <div><button id="1"></button></div>
+        <div><button id="2"></button></div>
+      </cr-grid>`;
+
+    const grid = document.body.querySelector('cr-grid')!;
+    assertEquals('button', grid.focusSelector);
+    const items = grid.$.items.assignedElements();
+    const focusableChildren = items.map(i => i.querySelector('button'));
+
+    // Focus first element.
+    focusableChildren[0]!.focus();
+    assertFocus(focusableChildren[0]!);
+
+    // Navigate via keyboard.
+    keydown(focusableChildren[0]!, 'ArrowRight');
+    assertFocus(focusableChildren[1]!);
+
+    keydown(focusableChildren[1]!, 'ArrowRight');
+    assertFocus(focusableChildren[2]!);
+
+    keydown(focusableChildren[2]!, 'ArrowRight');
+    assertFocus(focusableChildren[0]!);
+  });
 });
diff --git a/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_test.ts b/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_test.ts
index a3bbc32..3ffdfb1e 100644
--- a/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_test.ts
@@ -7,7 +7,6 @@
 
 import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import type {CrProfileAvatarSelectorElement} from 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js';
-import type {CrProfileAvatarSelectorGridElement} from 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn, pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -157,47 +156,14 @@
     assertTrue(items[2]!.parentElement!.classList.contains('iron-selected'));
   });
 
-  test('ignores modified key events', function() {
-    const selector =
-        avatarSelector.shadowRoot!
-            .querySelector<CrProfileAvatarSelectorGridElement>('#avatar-grid')!;
-    const items = getGridItems();
+  test('sets ignoreModifiedKeyEvents', function() {
+    const grid = avatarSelector.shadowRoot!.querySelector('cr-grid');
+    assertTrue(!!grid);
 
-    items[0]!.focus();
-    assertEquals(getDeepActiveElement(), items[0]);
-
-    keyDownOn(items[0]!, 39, [], 'ArrowRight');
-    assertEquals(getDeepActiveElement(), items[1]);
-
-    keyDownOn(items[0]!, 37, [], 'ArrowLeft');
-    assertEquals(getDeepActiveElement(), items[0]);
+    assertFalse(avatarSelector.ignoreModifiedKeyEvents);
+    assertFalse(grid.ignoreModifiedKeyEvents);
 
     avatarSelector.ignoreModifiedKeyEvents = true;
-
-    keyDownOn(items[0]!, 39, 'alt', 'ArrowRight');
-    assertEquals(getDeepActiveElement(), items[0]);
-
-    keyDownOn(items[0]!, 39, 'ctrl', 'ArrowRight');
-    assertEquals(getDeepActiveElement(), items[0]);
-
-    keyDownOn(items[0]!, 39, 'meta', 'ArrowRight');
-    assertEquals(getDeepActiveElement(), items[0]);
-
-    keyDownOn(items[0]!, 39, 'shift', 'ArrowRight');
-    assertEquals(getDeepActiveElement(), items[0]);
-
-    // Test RTL case.
-    selector.dir = 'rtl';
-    keyDownOn(items[0]!, 37, [], 'ArrowLeft');
-    assertEquals(getDeepActiveElement(), items[1]);
-
-    keyDownOn(items[0]!, 37, [], 'ArrowLeft');
-    assertEquals(getDeepActiveElement(), items[2]);
-
-    keyDownOn(items[0]!, 37, [], 'ArrowRight');
-    assertEquals(getDeepActiveElement(), items[1]);
-
-    keyDownOn(items[0]!, 37, [], 'ArrowRight');
-    assertEquals(getDeepActiveElement(), items[0]);
+    assertTrue(grid.ignoreModifiedKeyEvents);
   });
 });
diff --git a/chrome/test/data/webui/new_tab_page/BUILD.gn b/chrome/test/data/webui/new_tab_page/BUILD.gn
index 800df8a..d89b9a5 100644
--- a/chrome/test/data/webui/new_tab_page/BUILD.gn
+++ b/chrome/test/data/webui/new_tab_page/BUILD.gn
@@ -47,8 +47,8 @@
     "//ui/webui/resources/cr_components/help_bubble:build_ts",
     "//ui/webui/resources/cr_components/history_clusters:build_ts",
     "//ui/webui/resources/cr_components/most_visited:build_ts",
-    "//ui/webui/resources/cr_components/omnibox:build_ts",
     "//ui/webui/resources/cr_components/page_image_service:build_ts",
+    "//ui/webui/resources/cr_components/searchbox:build_ts",
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
diff --git a/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts b/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts
index d82fd703a..007ad9c 100644
--- a/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts
+++ b/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts
@@ -11,9 +11,9 @@
 import {PrivacySandboxCombinedDialogStep} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_combined_dialog_app.js';
 import type {PrivacySandboxDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_app.js';
 import {PrivacySandboxDialogBrowserProxy, PrivacySandboxPromptAction} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_browser_proxy.js';
-import type {PrivacySandboxDialogConsentStepElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_consent_step';
+import type {PrivacySandboxDialogConsentStepElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_consent_step.js';
 import {PrivacySandboxDialogMixin} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_mixin.js';
-import type {PrivacySandboxDialogNoticeStepElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_notice_step';
+import type {PrivacySandboxDialogNoticeStepElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_notice_step.js';
 import type {PrivacySandboxNoticeDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_notice_dialog_app.js';
 import type {PrivacySandboxNoticeRestrictedDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_notice_restricted_dialog_app.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
diff --git a/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_geolocation_subpage_test.ts b/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_geolocation_subpage_test.ts
index 9a015488..b723500 100644
--- a/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_geolocation_subpage_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_geolocation_subpage_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://os-settings/lazy_load.js';
 
 import {PrivacyHubBrowserProxyImpl, SettingsPrivacyHubGeolocationSubpage} from 'chrome://os-settings/lazy_load.js';
-import {appPermissionHandlerMojom, CrLinkRowElement, GeolocationAccessLevel, Router, routes, setAppPermissionProviderForTesting, SettingsDropdownMenuElement, SettingsPrivacyHubSystemServiceRow} from 'chrome://os-settings/os_settings.js';
+import {appPermissionHandlerMojom, CrLinkRowElement, GeolocationAccessLevel, LocalizedLinkElement, Router, routes, setAppPermissionProviderForTesting, SettingsDropdownMenuElement, SettingsPrivacyHubSystemServiceRow} from 'chrome://os-settings/os_settings.js';
 import {PermissionType, TriState} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {DomRepeat, flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -152,13 +152,15 @@
     assertEquals(4, systemServices.length);
     checkService(
         systemServices[0]!, 'privacyHubSystemServicesAutomaticTimeZoneName',
-        'Automatic time zone', 'privacyHubSystemServicesAllowedText', 'Allowed',
+        'Automatic time zone based on Wi-Fi or mobile networks',
+        'privacyHubSystemServicesAllowedText', 'Allowed',
         'Blocked. Time zone is currently set to ' +
             'Test Time Zone' +
             ' and can only be updated manually.');
     checkService(
         systemServices[1]!, 'privacyHubSystemServicesSunsetScheduleName',
-        'Sunset schedule', 'privacyHubSystemServicesAllowedText', 'Allowed',
+        'Automatic sunset schedule', 'privacyHubSystemServicesAllowedText',
+        'Allowed',
         'Blocked. Schedule is currently set to 7:00AM - 8:00PM' +
             ' and can only be updated manually.');
     checkService(
@@ -167,10 +169,63 @@
         'Blocked');
     checkService(
         systemServices[3]!, 'privacyHubSystemServicesDarkThemeName',
-        'Dark theme', 'privacyHubSystemServicesAllowedText', 'Allowed',
-        'Blocked');
+        'Automatic light/dark theme', 'privacyHubSystemServicesAllowedText',
+        'Allowed', 'Blocked');
   }
 
+  test('Geolocation sub-label updates on location change', async () => {
+    await initPage();
+
+    let subLabelElement: LocalizedLinkElement|null;
+    let subLabel: string;
+
+    // Helper function to remove HTML tags from the localizedString.
+    const removeAnchorTags = (text: string) =>
+        text.replace('<a>', '').replace('</a>', '');
+
+    // Check "Allowed"
+    assertTrue(getGeolocationAccessLevel() === GeolocationAccessLevel.ALLOWED);
+    subLabelElement =
+        privacyHubGeolocationSubpage.shadowRoot!
+            .querySelector<LocalizedLinkElement>(
+                '#geolocationModeDescriptionDiv > localized-link');
+    assertTrue(!!subLabelElement);
+    subLabel = subLabelElement.localizedString.toString();
+    assertEquals(
+        privacyHubGeolocationSubpage.i18n('geolocationAllowedModeDescription'),
+        removeAnchorTags(subLabel));
+
+    // Check "Allowed For System Services"
+    setGeolocationAccessLevel(GeolocationAccessLevel.ONLY_ALLOWED_FOR_SYSTEM);
+    assertTrue(
+        getGeolocationAccessLevel() ===
+        GeolocationAccessLevel.ONLY_ALLOWED_FOR_SYSTEM);
+    subLabelElement =
+        privacyHubGeolocationSubpage.shadowRoot!
+            .querySelector<LocalizedLinkElement>(
+                '#geolocationModeDescriptionDiv > localized-link');
+    assertTrue(!!subLabelElement);
+    subLabel = subLabelElement.localizedString.toString();
+    assertEquals(
+        privacyHubGeolocationSubpage.i18n(
+            'geolocationOnlyAllowedForSystemModeDescription'),
+        removeAnchorTags(subLabel));
+
+    // Check "Blocked for all"
+    setGeolocationAccessLevel(GeolocationAccessLevel.DISALLOWED);
+    assertTrue(
+        getGeolocationAccessLevel() === GeolocationAccessLevel.DISALLOWED);
+    subLabelElement =
+        privacyHubGeolocationSubpage.shadowRoot!
+            .querySelector<LocalizedLinkElement>(
+                '#geolocationModeDescriptionDiv > localized-link');
+    assertTrue(!!subLabelElement);
+    subLabel = subLabelElement.localizedString.toString();
+    assertEquals(
+        privacyHubGeolocationSubpage.i18n('geolocationBlockedModeDescription'),
+        removeAnchorTags(subLabel));
+  });
+
   test('App list displayed when geolocation allowed', async () => {
     await initPage();
     assertEquals(
diff --git a/chrome/test/data/webui/side_panel/bookmarks/commerce/test_shopping_service_api_proxy.ts b/chrome/test/data/webui/side_panel/bookmarks/commerce/test_shopping_service_api_proxy.ts
index 3a1fbfd..f7e3559 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/commerce/test_shopping_service_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/commerce/test_shopping_service_api_proxy.ts
@@ -3,8 +3,9 @@
 // found in the LICENSE file.
 
 import type {BrowserProxy} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
-import type {BookmarkProductInfo, PageRemote, PriceInsightsInfo, ProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import type {BookmarkProductInfo, PageRemote, PriceInsightsInfo, ProductInfo, ProductSpecifications} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
 import {PageCallbackRouter, PriceInsightsInfo_PriceBucket} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {TestBrowserProxy as BaseTestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestBrowserProxy extends BaseTestBrowserProxy implements
@@ -34,6 +35,11 @@
     locale: '',
     currencyCode: '',
   };
+  private productSpecs_: ProductSpecifications = {
+    products: [],
+    productDimensionMap: new Map<bigint, string>(),
+  };
+
   private shoppingCollectionId_: bigint = BigInt(-1);
 
   constructor() {
@@ -53,6 +59,8 @@
       'setPriceTrackingStatusForCurrentUrl',
       'getParentBookmarkFolderNameForCurrentUrl',
       'showBookmarkEditorForCurrentUrl',
+      'getProductInfoForUrl',
+      'getProductSpecificationsForUrls',
     ]);
 
     this.callbackRouter = new PageCallbackRouter();
@@ -87,6 +95,16 @@
     this.methodCalled('untrackPriceForBookmark', bookmarkId);
   }
 
+  getProductInfoForUrl(url: Url) {
+    this.methodCalled('getProductInfoForUrl', url);
+    return Promise.resolve({productInfo: this.product_});
+  }
+
+  getProductSpecificationsForUrls(urls: Url[]) {
+    this.methodCalled('getProductSpecificationsForUrls', urls);
+    return Promise.resolve({productSpecs: this.productSpecs_});
+  }
+
   getProductInfoForCurrentUrl() {
     this.methodCalled('getProductInfoForCurrentUrl');
     return Promise.resolve({productInfo: this.product_});
diff --git a/chrome/test/data/webui/side_panel/performance_controls/test_battery_saver_card_api_proxy.ts b/chrome/test/data/webui/side_panel/performance_controls/test_battery_saver_card_api_proxy.ts
index 15b28b5..061dc5f 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/test_battery_saver_card_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/test_battery_saver_card_api_proxy.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 {BatterySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/battery_saver_card_api_proxy';
+import type {BatterySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/battery_saver_card_api_proxy.js';
 import {BatterySaverCardCallbackRouter} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/side_panel/performance_controls/test_memory_saver_card_api_proxy.ts b/chrome/test/data/webui/side_panel/performance_controls/test_memory_saver_card_api_proxy.ts
index e9453b4..952ef55 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/test_memory_saver_card_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/test_memory_saver_card_api_proxy.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 {MemorySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/memory_saver_card_api_proxy';
+import type {MemorySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/memory_saver_card_api_proxy.js';
 import {MemorySaverCardCallbackRouter} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/side_panel/performance_controls/test_performance_page_api_proxy.ts b/chrome/test/data/webui/side_panel/performance_controls/test_performance_page_api_proxy.ts
index 75ede46f..32decc2 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/test_performance_page_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/test_performance_page_api_proxy.ts
@@ -4,7 +4,7 @@
 
 import type {PerformancePageRemote} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
 import {PerformancePageCallbackRouter, PerformancePageHandlerRemote} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
-import type {PerformancePageApiProxy} from 'chrome://performance-side-panel.top-chrome/performance_page_api_proxy';
+import type {PerformancePageApiProxy} from 'chrome://performance-side-panel.top-chrome/performance_page_api_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 
diff --git a/chrome/test/data/webui/signin/profile_picker_app_test.ts b/chrome/test/data/webui/signin/profile_picker_app_test.ts
index 62efa8b..5e7d1f5 100644
--- a/chrome/test/data/webui/signin/profile_picker_app_test.ts
+++ b/chrome/test/data/webui/signin/profile_picker_app_test.ts
@@ -187,7 +187,8 @@
     await whenCheck(choice!, () => choice!.classList.contains('active'));
     verifyProfileCreationViewStyle(choice!);
     choice!.$.notNowButton.click();
-    const args = await browserProxy.whenCalled('continueWithoutAccount');
+    const args = await browserProxy.whenCalled(
+        'createProfileAndOpenCustomizationDialog');
     assertEquals(args[0], browserProxy.profileThemeInfo.color);
     assertTrue(testElement.profileCreationInProgress);
     assertTrue(choice.profileCreationInProgress);
@@ -213,7 +214,8 @@
     });
     await resetTestElement(Routes.NEW_PROFILE);
     await browserProxy.whenCalled('getNewProfileSuggestedThemeInfo');
-    const args = await browserProxy.whenCalled('continueWithoutAccount');
+    const args = await browserProxy.whenCalled(
+        'createProfileAndOpenCustomizationDialog');
     assertEquals(args[0], browserProxy.profileThemeInfo.color);
   });
 });
diff --git a/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.ts b/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.ts
index 7773ad6..734b0ee1 100644
--- a/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.ts
+++ b/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.ts
@@ -17,10 +17,10 @@
       'openManageProfileSettingsSubPage', 'launchSelectedProfile',
       'askOnStartupChanged', 'getNewProfileSuggestedThemeInfo',
       'getProfileThemeInfo', 'removeProfile', 'getProfileStatistics',
-      'closeProfileStatistics', 'selectNewAccount', 'continueWithoutAccount',
-      'setProfileName', 'recordSignInPromoImpression', 'getAvailableIcons',
-      'getSwitchProfile', 'confirmProfileSwitch', 'cancelProfileSwitch',
-      'updateProfileOrder',
+      'closeProfileStatistics', 'selectNewAccount',
+      'createProfileAndOpenCustomizationDialog', 'setProfileName',
+      'recordSignInPromoImpression', 'getAvailableIcons', 'getSwitchProfile',
+      'confirmProfileSwitch', 'cancelProfileSwitch', 'updateProfileOrder',
       // <if expr="chromeos_lacros">
       'getAvailableAccounts', 'openAshAccountSettingsPage',
       'selectExistingAccountLacros', 'openDeviceGuestLinkLacros',
@@ -110,8 +110,9 @@
     this.methodCalled('selectNewAccount', [profileColor]);
   }
 
-  continueWithoutAccount(profileColor: number) {
-    this.methodCalled('continueWithoutAccount', [profileColor]);
+  createProfileAndOpenCustomizationDialog(profileColor: number) {
+    this.methodCalled(
+        'createProfileAndOpenCustomizationDialog', [profileColor]);
   }
 
   setProfileName(profilePath: string, profileName: string) {
diff --git a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
index d5ab2a63..0aafe75 100644
--- a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
+++ b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
@@ -6,7 +6,7 @@
 import {PageCallbackRouter} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
 import type {Tab, TabGroupVisualData} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
 import {TabNetworkState} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
-import type {CloseTabAction, TabsApiProxy} from 'chrome://tab-strip.top-chrome/tabs_api_proxy';
+import type {CloseTabAction, TabsApiProxy} from 'chrome://tab-strip.top-chrome/tabs_api_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export function createTab(override?: Partial<Tab>): Tab {
diff --git a/chrome/test/interaction/interactive_browser_test_interactive_uitest.cc b/chrome/test/interaction/interactive_browser_test_interactive_uitest.cc
index aace6d5e..2a3dd4c 100644
--- a/chrome/test/interaction/interactive_browser_test_interactive_uitest.cc
+++ b/chrome/test/interaction/interactive_browser_test_interactive_uitest.cc
@@ -406,7 +406,8 @@
 }
 
 IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestUiTest,
-                       InstrumentNonTabAsTestStep) {
+                       // TODO(crbug.com/330210402): Re-enable this test
+                       DISABLED_InstrumentNonTabAsTestStep) {
   DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsId);
   const char kTabSearchWebViewName[] = "Tab Search WebView";
 
diff --git a/chrome/test/interaction/webcontents_interaction_test_util_interactive_uitest.cc b/chrome/test/interaction/webcontents_interaction_test_util_interactive_uitest.cc
index d89406f..15980ac 100644
--- a/chrome/test/interaction/webcontents_interaction_test_util_interactive_uitest.cc
+++ b/chrome/test/interaction/webcontents_interaction_test_util_interactive_uitest.cc
@@ -165,8 +165,15 @@
 }
 
 // This test checks that we can attach to a WebUI that isn't embedded in a tab.
+// TODO(crbug.com/330210402) Test is flaky on ChromeOS.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_OpenTabSearchMenuAndAccessWebUI \
+  DISABLED_OpenTabSearchMenuAndAccessWebUI
+#else
+#define MAYBE_OpenTabSearchMenuAndAccessWebUI OpenTabSearchMenuAndAccessWebUI
+#endif
 IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilInteractiveUiTest,
-                       OpenTabSearchMenuAndAccessWebUI) {
+                       MAYBE_OpenTabSearchMenuAndAccessWebUI) {
   UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
   UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
 
diff --git a/chrome/utility/services.cc b/chrome/utility/services.cc
index 0ee7e3a2..cbbeed1 100644
--- a/chrome/utility/services.cc
+++ b/chrome/utility/services.cc
@@ -127,6 +127,7 @@
 #include "chromeos/ash/services/orca/orca_library.h"
 #include "chromeos/ash/services/quick_pair/quick_pair_service.h"
 #include "chromeos/ash/services/recording/recording_service.h"
+#include "chromeos/constants/chromeos_features.h"  // nogncheck
 #include "chromeos/services/tts/public/mojom/tts_service.mojom.h"
 #include "chromeos/services/tts/tts_service.h"
 
@@ -141,7 +142,6 @@
 #include "chromeos/components/mahi/public/mojom/content_extraction.mojom.h"
 #include "chromeos/components/quick_answers/public/cpp/service/spell_check_service.h"
 #include "chromeos/components/quick_answers/public/mojom/spell_check.mojom.h"
-#include "chromeos/constants/chromeos_features.h"  // nogncheck
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 namespace {
@@ -531,9 +531,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
   services.Add(RunQuickAnswersSpellCheckService);
-  if (chromeos::features::IsMahiEnabled()) {
-    services.Add(RunMahiContentExtractionServiceFactory);
-  }
+  services.Add(RunMahiContentExtractionServiceFactory);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
diff --git a/chromecast/media/audio/cast_audio_device_factory.cc b/chromecast/media/audio/cast_audio_device_factory.cc
index efcf3bb..76c339d 100644
--- a/chromecast/media/audio/cast_audio_device_factory.cc
+++ b/chromecast/media/audio/cast_audio_device_factory.cc
@@ -249,6 +249,7 @@
 CastAudioDeviceFactory::NewMixableSink(
     blink::WebAudioDeviceSourceType source_type,
     const blink::LocalFrameToken& frame_token,
+    const blink::FrameToken& main_frame_token,
     const ::media::AudioSinkParameters& params) {
   return base::MakeRefCounted<NonSwitchableAudioRendererSink>(frame_token,
                                                               params);
diff --git a/chromecast/media/audio/cast_audio_device_factory.h b/chromecast/media/audio/cast_audio_device_factory.h
index f2a3ea3..2c30384 100644
--- a/chromecast/media/audio/cast_audio_device_factory.h
+++ b/chromecast/media/audio/cast_audio_device_factory.h
@@ -27,6 +27,7 @@
   scoped_refptr<::media::SwitchableAudioRendererSink> NewMixableSink(
       blink::WebAudioDeviceSourceType source_type,
       const blink::LocalFrameToken& frame_token,
+      const blink::FrameToken& main_frame_token,
       const ::media::AudioSinkParameters& params) override;
 };
 
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 4a4bf749..4438267 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -229,7 +229,7 @@
   tast_test("cq_medium_tast_tests") {
     if (is_skylab) {
       # TODO(b/284405976): Add a !no_chrome_dcheck condition automatically by reading the build configuration.
-      tast_attr_expr = "(\"group:cq-medium\" && \"dep:chrome\" && !\"dep:no_chrome_dcheck\" && !informational)"
+      tast_attr_expr = "(\"group:cq-medium\" && \"dep:chrome\" && !\"dep:no_chrome_dcheck\" && \"group:mainline\" && !informational)"
     } else {
       deploy_lacros_chrome = true
     }
diff --git a/chromeos/ash/components/data_migration/BUILD.gn b/chromeos/ash/components/data_migration/BUILD.gn
index 7dcb2a0..5d873f3 100644
--- a/chromeos/ash/components/data_migration/BUILD.gn
+++ b/chromeos/ash/components/data_migration/BUILD.gn
@@ -23,6 +23,15 @@
   ]
 }
 
+source_set("pending_file_transfer_queue") {
+  sources = [
+    "pending_file_transfer_queue.cc",
+    "pending_file_transfer_queue.h",
+  ]
+
+  deps = [ "//base" ]
+}
+
 source_set("test_support") {
   testonly = true
 
@@ -46,11 +55,15 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "file_receiver_unittest.cc" ]
+  sources = [
+    "file_receiver_unittest.cc",
+    "pending_file_transfer_queue_unittest.cc",
+  ]
 
   deps = [
     ":constants",
     ":file_receiver",
+    ":pending_file_transfer_queue",
     ":test_support",
     "//base",
     "//base/test:test_support",
diff --git a/chromeos/ash/components/data_migration/pending_file_transfer_queue.cc b/chromeos/ash/components/data_migration/pending_file_transfer_queue.cc
new file mode 100644
index 0000000..60f59ea
--- /dev/null
+++ b/chromeos/ash/components/data_migration/pending_file_transfer_queue.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 "chromeos/ash/components/data_migration/pending_file_transfer_queue.h"
+
+#include <utility>
+
+#include "base/check.h"
+
+namespace data_migration {
+
+PendingFileTransferQueue::PendingFileTransferQueue() = default;
+
+PendingFileTransferQueue::~PendingFileTransferQueue() = default;
+
+void PendingFileTransferQueue::Push(int64_t payload_id) {
+  // TODO(esum): Add a maximum size to `pending_payload_ids_` to prevent it from
+  // consuming an indefinite amount of memory.
+  pending_payload_ids_.push(payload_id);
+  if (pending_pop_cb_) {
+    RunPendingPopCallback();
+  }
+}
+
+void PendingFileTransferQueue::Pop(
+    base::OnceCallback<void(int64_t)> completion_cb) {
+  CHECK(completion_cb);
+  CHECK(!pending_pop_cb_);
+  pending_pop_cb_ = std::move(completion_cb);
+  if (!pending_payload_ids_.empty()) {
+    RunPendingPopCallback();
+  }
+}
+
+void PendingFileTransferQueue::RunPendingPopCallback() {
+  CHECK(pending_pop_cb_);
+  int64_t popped_payload_id = pending_payload_ids_.front();
+  pending_payload_ids_.pop();
+  std::move(pending_pop_cb_).Run(popped_payload_id);
+}
+
+}  // namespace data_migration
diff --git a/chromeos/ash/components/data_migration/pending_file_transfer_queue.h b/chromeos/ash/components/data_migration/pending_file_transfer_queue.h
new file mode 100644
index 0000000..f5cda02
--- /dev/null
+++ b/chromeos/ash/components/data_migration/pending_file_transfer_queue.h
@@ -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.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_DATA_MIGRATION_PENDING_FILE_TRANSFER_QUEUE_H_
+#define CHROMEOS_ASH_COMPONENTS_DATA_MIGRATION_PENDING_FILE_TRANSFER_QUEUE_H_
+
+#include <cstdint>
+#include <queue>
+
+#include "base/functional/callback.h"
+
+namespace data_migration {
+
+// A queue of files (identified by their payload id) that the remote device
+// wants to transfer but hasn't yet. The remote device has sent a
+// "Request-To-Send" message for each file in this queue and is awaiting a
+// "Clear-To-Send" message to be sent back before the transfer actually can
+// commence. "Clear-To-Send" is sent after a pending file is popped from the
+// queue and registered with the NC library.
+class PendingFileTransferQueue {
+ public:
+  PendingFileTransferQueue();
+  PendingFileTransferQueue(const PendingFileTransferQueue&) = delete;
+  PendingFileTransferQueue& operator=(const PendingFileTransferQueue&) = delete;
+  ~PendingFileTransferQueue();
+
+  // Adds file with the given `payload_id` to the queue.
+  void Push(int64_t payload_id);
+
+  // Pops a pending file from the queue, returning the file's `payload_id` to
+  // the caller. The `completion_cb` may be run synchronously in some cases if
+  // there are pending files already waiting in the queue when this method is
+  // called. There can only be one active `Pop()` call at any given time.
+  void Pop(base::OnceCallback<void(int64_t)> completion_cb);
+
+ private:
+  void RunPendingPopCallback();
+
+  std::queue<int64_t> pending_payload_ids_;
+  base::OnceCallback<void(int64_t)> pending_pop_cb_;
+};
+
+}  // namespace data_migration
+
+#endif  // CHROMEOS_ASH_COMPONENTS_DATA_MIGRATION_PENDING_FILE_TRANSFER_QUEUE_H_
diff --git a/chromeos/ash/components/data_migration/pending_file_transfer_queue_unittest.cc b/chromeos/ash/components/data_migration/pending_file_transfer_queue_unittest.cc
new file mode 100644
index 0000000..f462a09
--- /dev/null
+++ b/chromeos/ash/components/data_migration/pending_file_transfer_queue_unittest.cc
@@ -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 "chromeos/ash/components/data_migration/pending_file_transfer_queue.h"
+
+#include <cstdint>
+
+#include "base/test/test_future.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace data_migration {
+
+class PendingFileTransferQueueTest : public testing::Test {
+ protected:
+  PendingFileTransferQueue queue_;
+};
+
+TEST_F(PendingFileTransferQueueTest, PopWithItemsAlreadyInQueue) {
+  queue_.Push(1);
+  queue_.Push(2);
+
+  base::test::TestFuture<int64_t> future;
+  queue_.Pop(future.GetCallback());
+  EXPECT_EQ(future.Get(), 1);
+
+  future.Clear();
+  queue_.Pop(future.GetCallback());
+  EXPECT_EQ(future.Get(), 2);
+}
+
+TEST_F(PendingFileTransferQueueTest, PopWithNoItemsInQueue) {
+  base::test::TestFuture<int64_t> future;
+  queue_.Pop(future.GetCallback());
+
+  queue_.Push(1);
+  EXPECT_EQ(future.Get(), 1);
+
+  future.Clear();
+  queue_.Pop(future.GetCallback());
+  EXPECT_FALSE(future.IsReady());
+}
+
+}  // namespace data_migration
diff --git a/chromeos/ash/components/dbus/fwupd/OWNERS b/chromeos/ash/components/dbus/fwupd/OWNERS
index cbaa826..05bed0f 100644
--- a/chromeos/ash/components/dbus/fwupd/OWNERS
+++ b/chromeos/ash/components/dbus/fwupd/OWNERS
@@ -1,2 +1,3 @@
 jimmyxgong@chromium.org
+dpad@google.com
 zentaro@chromium.org
\ No newline at end of file
diff --git a/chromeos/ash/components/dbus/shill/fake_shill_manager_client.cc b/chromeos/ash/components/dbus/shill/fake_shill_manager_client.cc
index cfacfbd5..5518900 100644
--- a/chromeos/ash/components/dbus/shill/fake_shill_manager_client.cc
+++ b/chromeos/ash/components/dbus/shill/fake_shill_manager_client.cc
@@ -557,6 +557,46 @@
   return;
 }
 
+void FakeShillManagerClient::CreateP2PGroup(
+    const base::Value::Dict& properties,
+    base::OnceCallback<void(base::Value::Dict result)> callback,
+    ErrorCallback error_callback) {
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(error_callback), "Error", "Fake failure"));
+  return;
+}
+
+void FakeShillManagerClient::ConnectToP2PGroup(
+    const base::Value::Dict& properties,
+    base::OnceCallback<void(base::Value::Dict result)> callback,
+    ErrorCallback error_callback) {
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(error_callback), "Error", "Fake failure"));
+  return;
+}
+
+void FakeShillManagerClient::DestroyP2PGroup(
+    const uint32_t shill_id,
+    base::OnceCallback<void(base::Value::Dict result)> callback,
+    ErrorCallback error_callback) {
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(error_callback), "Error", "Fake failure"));
+  return;
+}
+
+void FakeShillManagerClient::DisconnectFromP2PGroup(
+    const uint32_t shill_id,
+    base::OnceCallback<void(base::Value::Dict result)> callback,
+    ErrorCallback error_callback) {
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(error_callback), "Error", "Fake failure"));
+  return;
+}
+
 ShillManagerClient::TestInterface* FakeShillManagerClient::GetTestInterface() {
   return this;
 }
diff --git a/chromeos/ash/components/dbus/shill/fake_shill_manager_client.h b/chromeos/ash/components/dbus/shill/fake_shill_manager_client.h
index c6b8650..d85f93d 100644
--- a/chromeos/ash/components/dbus/shill/fake_shill_manager_client.h
+++ b/chromeos/ash/components/dbus/shill/fake_shill_manager_client.h
@@ -86,6 +86,26 @@
                       base::OnceClosure callback,
                       ErrorCallback error_callback) override;
 
+  void CreateP2PGroup(
+      const base::Value::Dict& properties,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) override;
+
+  void ConnectToP2PGroup(
+      const base::Value::Dict& properties,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) override;
+
+  void DestroyP2PGroup(
+      const uint32_t shill_id,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) override;
+
+  void DisconnectFromP2PGroup(
+      const uint32_t shill_id,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) override;
+
   ShillManagerClient::TestInterface* GetTestInterface() override;
 
   // ShillManagerClient::TestInterface overrides.
diff --git a/chromeos/ash/components/dbus/shill/shill_manager_client.cc b/chromeos/ash/components/dbus/shill/shill_manager_client.cc
index 323c198..9367d978 100644
--- a/chromeos/ash/components/dbus/shill/shill_manager_client.cc
+++ b/chromeos/ash/components/dbus/shill/shill_manager_client.cc
@@ -229,6 +229,54 @@
                                              std::move(error_callback));
   }
 
+  void CreateP2PGroup(
+      const base::Value::Dict& properties,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) override {
+    dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
+                                 shill::kCreateP2PGroupFunction);
+    dbus::MessageWriter writer(&method_call);
+    ShillClientHelper::AppendServiceProperties(&writer, properties);
+    helper_->CallDictValueMethodWithErrorCallback(
+        &method_call, std::move(callback), std::move(error_callback));
+  }
+
+  void ConnectToP2PGroup(
+      const base::Value::Dict& properties,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) override {
+    dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
+                                 shill::kConnectToP2PGroupFunction);
+    dbus::MessageWriter writer(&method_call);
+    ShillClientHelper::AppendServiceProperties(&writer, properties);
+    helper_->CallDictValueMethodWithErrorCallback(
+        &method_call, std::move(callback), std::move(error_callback));
+  }
+
+  void DestroyP2PGroup(
+      const uint32_t shill_id,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) override {
+    dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
+                                 shill::kDestroyP2PGroupFunction);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendUint32(shill_id);
+    helper_->CallDictValueMethodWithErrorCallback(
+        &method_call, std::move(callback), std::move(error_callback));
+  }
+
+  void DisconnectFromP2PGroup(
+      const uint32_t shill_id,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) override {
+    dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
+                                 shill::kDisconnectFromP2PGroupFunction);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendUint32(shill_id);
+    helper_->CallDictValueMethodWithErrorCallback(
+        &method_call, std::move(callback), std::move(error_callback));
+  }
+
   TestInterface* GetTestInterface() override { return nullptr; }
 
   void Init(dbus::Bus* bus) {
diff --git a/chromeos/ash/components/dbus/shill/shill_manager_client.h b/chromeos/ash/components/dbus/shill/shill_manager_client.h
index a38c5746..70ff35b7 100644
--- a/chromeos/ash/components/dbus/shill/shill_manager_client.h
+++ b/chromeos/ash/components/dbus/shill/shill_manager_client.h
@@ -274,6 +274,30 @@
                               base::OnceClosure callback,
                               ErrorCallback error_callback) = 0;
 
+  // Creates a P2P group that uses WiFi direct as the underlying medium.
+  virtual void CreateP2PGroup(
+      const base::Value::Dict& properties,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) = 0;
+
+  // Connects to a P2P group
+  virtual void ConnectToP2PGroup(
+      const base::Value::Dict& properties,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) = 0;
+
+  // Destroys P2PGroup
+  virtual void DestroyP2PGroup(
+      const uint32_t shill_id,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) = 0;
+
+  // Disconnects from P2PGroup
+  virtual void DisconnectFromP2PGroup(
+      const uint32_t shill_id,
+      base::OnceCallback<void(base::Value::Dict result)> callback,
+      ErrorCallback error_callback) = 0;
+
   // Returns an interface for testing (stub only), or returns null.
   virtual TestInterface* GetTestInterface() = 0;
 
diff --git a/chromeos/ash/components/dbus/shill/shill_manager_client_unittest.cc b/chromeos/ash/components/dbus/shill/shill_manager_client_unittest.cc
index fe42813..0d5a8eb 100644
--- a/chromeos/ash/components/dbus/shill/shill_manager_client_unittest.cc
+++ b/chromeos/ash/components/dbus/shill/shill_manager_client_unittest.cc
@@ -404,4 +404,131 @@
   EXPECT_FALSE(error_result.IsReady());
 }
 
+TEST_F(ShillManagerClientTest, CreateP2PGroup) {
+  const char kShillId[] = "sample_shill_id";
+  const char kCreateGroupResult[] = "success";
+
+  const char kSSID[] = "test_ssid";
+  const char kPassphrase[] = "test_password";
+
+  // Create response.
+  base::Value::Dict result_dictionary;
+  result_dictionary.Set("shill_id", kShillId);
+  result_dictionary.Set("result", kCreateGroupResult);
+
+  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
+  dbus::MessageWriter writer(response.get());
+  AppendValueDataAsVariant(&writer, result_dictionary);
+
+  // Create input dictionary
+  base::Value::Dict input_dictionary;
+  input_dictionary.Set("ssid", kSSID);
+  input_dictionary.Set("passphrase", kPassphrase);
+
+  // Set expectation.
+  const bool string_valued = false;
+  PrepareForMethodCall(shill::kCreateP2PGroupFunction,
+                       base::BindRepeating(&ExpectValueDictionaryArgument,
+                                           &input_dictionary, string_valued),
+                       response.get());
+
+  base::test::TestFuture<base::Value::Dict> create_p2p_group_result;
+  base::test::TestFuture<std::string, std::string> error_result;
+  client_->CreateP2PGroup(
+      input_dictionary,
+      create_p2p_group_result.GetCallback<base::Value::Dict>(),
+      error_result.GetCallback<const std::string&, const std::string&>());
+  EXPECT_EQ(create_p2p_group_result.Get(), result_dictionary);
+}
+
+TEST_F(ShillManagerClientTest, ConnectToP2PGroup) {
+  const char kShillId[] = "sample_shill_id";
+  const char kConnectToGroupResult[] = "success";
+
+  const char kSSID[] = "test_ssid";
+  const char kPassphrase[] = "test_passphrase";
+
+  // Create response.
+  base::Value::Dict result_dictionary;
+  result_dictionary.Set("shill_id", kShillId);
+  result_dictionary.Set("result", kConnectToGroupResult);
+
+  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
+  dbus::MessageWriter writer(response.get());
+  AppendValueDataAsVariant(&writer, result_dictionary);
+
+  // Create input dictionary
+  base::Value::Dict input_dictionary;
+  input_dictionary.Set("ssid", kSSID);
+  input_dictionary.Set("passphrase", kPassphrase);
+
+  // Set expectation.
+  const bool string_valued = false;
+  PrepareForMethodCall(shill::kConnectToP2PGroupFunction,
+                       base::BindRepeating(&ExpectValueDictionaryArgument,
+                                           &input_dictionary, string_valued),
+                       response.get());
+
+  base::test::TestFuture<base::Value::Dict> connect_to_p2p_group_result;
+  base::test::TestFuture<std::string, std::string> error_result;
+  client_->ConnectToP2PGroup(
+      input_dictionary,
+      connect_to_p2p_group_result.GetCallback<base::Value::Dict>(),
+      error_result.GetCallback<const std::string&, const std::string&>());
+  EXPECT_EQ(connect_to_p2p_group_result.Get(), result_dictionary);
+}
+
+TEST_F(ShillManagerClientTest, DestroyP2PGroup) {
+  const int kShillId = 57;
+  const char kDestroyGroupResult[] = "success";
+
+  // Create response.
+  base::Value::Dict result_dictionary;
+  result_dictionary.Set("shill_id", kShillId);
+  result_dictionary.Set("result", kDestroyGroupResult);
+
+  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
+  dbus::MessageWriter writer(response.get());
+  AppendValueDataAsVariant(&writer, result_dictionary);
+
+  // Set expectation.
+  PrepareForMethodCall(shill::kDestroyP2PGroupFunction,
+                       base::BindRepeating(&ExpectUint32Argument, kShillId),
+                       response.get());
+
+  base::test::TestFuture<base::Value::Dict> destroy_p2p_group_result;
+  base::test::TestFuture<std::string, std::string> error_result;
+  client_->DestroyP2PGroup(
+      kShillId, destroy_p2p_group_result.GetCallback<base::Value::Dict>(),
+      error_result.GetCallback<const std::string&, const std::string&>());
+  EXPECT_EQ(destroy_p2p_group_result.Get(), result_dictionary);
+}
+
+TEST_F(ShillManagerClientTest, DisconnectFromP2PGroup) {
+  const int kShillId = 57;
+  const char kDisconnectFromGroupResult[] = "success";
+
+  // Create response.
+  base::Value::Dict result_dictionary;
+  result_dictionary.Set("shill_id", kShillId);
+  result_dictionary.Set("result", kDisconnectFromGroupResult);
+
+  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
+  dbus::MessageWriter writer(response.get());
+  AppendValueDataAsVariant(&writer, result_dictionary);
+
+  // Set expectation.
+  PrepareForMethodCall(shill::kDisconnectFromP2PGroupFunction,
+                       base::BindRepeating(&ExpectUint32Argument, kShillId),
+                       response.get());
+
+  base::test::TestFuture<base::Value::Dict> disconnect_from_p2p_group_result;
+  base::test::TestFuture<std::string, std::string> error_result;
+  client_->DisconnectFromP2PGroup(
+      kShillId,
+      disconnect_from_p2p_group_result.GetCallback<base::Value::Dict>(),
+      error_result.GetCallback<const std::string&, const std::string&>());
+  EXPECT_EQ(disconnect_from_p2p_group_result.Get(), result_dictionary);
+}
+
 }  // namespace ash
diff --git a/chromeos/ash/components/fwupd/OWNERS b/chromeos/ash/components/fwupd/OWNERS
index cbaa826..05bed0f 100644
--- a/chromeos/ash/components/fwupd/OWNERS
+++ b/chromeos/ash/components/fwupd/OWNERS
@@ -1,2 +1,3 @@
 jimmyxgong@chromium.org
+dpad@google.com
 zentaro@chromium.org
\ No newline at end of file
diff --git a/chromeos/ash/components/growth/action_performer.h b/chromeos/ash/components/growth/action_performer.h
index 832bb65..b54182a 100644
--- a/chromeos/ash/components/growth/action_performer.h
+++ b/chromeos/ash/components/growth/action_performer.h
@@ -13,10 +13,17 @@
 namespace growth {
 
 // The different actions that the Growth framework can run.
+// These values are deserialized from Growth Campaign, so entries should not
+// be renumbered and numeric values should never be reused
 enum class ActionType {
-  kInstallWebApp = 0,
-  kPinWebApp = 1,
-  kOpenUrl = 2,
+  // This is a special action that handled by surfaces like Nudge which has
+  // different implementation of dismissal (instead of action performers that
+  // are used by different surfaces).
+  kDismiss = 0,
+
+  kInstallWebApp = 1,
+  kPinWebApp = 2,
+  kOpenUrl = 3,
 };
 
 enum class ActionResult {
@@ -25,11 +32,12 @@
 };
 
 enum class ActionResultReason {
-  kParsingActionFailed = 0,
+  kUnknown = 0,
+  kParsingActionFailed = 1,
 
   // For kInstallWebApp action
-  kWebAppProviderNotAvailable = 1,
-  kWebAppInstallFailedOther = 2,
+  kWebAppProviderNotAvailable = 2,
+  kWebAppInstallFailedOther = 3,
 };
 
 // Abstract interface for the different actions that Growth framework
diff --git a/chromeos/ash/components/growth/campaigns_manager.cc b/chromeos/ash/components/growth/campaigns_manager.cc
index 5f2b792..0fe87fcc 100644
--- a/chromeos/ash/components/growth/campaigns_manager.cc
+++ b/chromeos/ash/components/growth/campaigns_manager.cc
@@ -133,6 +133,40 @@
   matcher_.SetOpenedApp(app_id);
 }
 
+void CampaignsManager::PerformAction(const Action* action) {
+  CHECK(action);
+
+  auto* params = action->GetParams();
+  auto action_type = action->GetActionType();
+  if (!action_type || !params) {
+    // TODO(b/306023057): Record invalid action error.
+    return;
+  }
+
+  auto& action_performer = actions_map_.at(action_type.value());
+  if (!action_performer) {
+    // TODO(b/306023057): Record unrecognized action error.
+    return;
+  }
+
+  action_performer->Run(
+      params,
+      base::BindOnce(
+          [](growth::ActionType action_type, growth::ActionResult result,
+             std::optional<growth::ActionResultReason> reason) {
+            if (result == growth::ActionResult::kSuccess) {
+              return;
+            }
+
+            // TODO(b/306023057) Record perform action fail error.
+            LOG(ERROR) << "Error running action. Action type: "
+                       << int(action_type) << ". Error code:"
+                       << static_cast<int>(reason.value_or(
+                              growth::ActionResultReason::kUnknown));
+          },
+          action_type.value()));
+}
+
 void CampaignsManager::OnCampaignsComponentLoaded(
     base::OnceClosure load_callback,
     bool in_oobe,
diff --git a/chromeos/ash/components/growth/campaigns_manager.h b/chromeos/ash/components/growth/campaigns_manager.h
index 1f8c189d..628543c 100644
--- a/chromeos/ash/components/growth/campaigns_manager.h
+++ b/chromeos/ash/components/growth/campaigns_manager.h
@@ -63,7 +63,9 @@
   // opened app targeting.
   void SetOpenedApp(const std::string& app_id);
 
-  ActionMap& actions_map() { return actions_map_; }
+  // Select action performer based on the action type and perform action with
+  // action params.
+  void PerformAction(const Action* action);
 
  private:
   // Triggred when campaigns component loaded.
diff --git a/chromeos/ash/components/growth/campaigns_model.cc b/chromeos/ash/components/growth/campaigns_model.cc
index dde43e7c..6291315 100644
--- a/chromeos/ash/components/growth/campaigns_model.cc
+++ b/chromeos/ash/components/growth/campaigns_model.cc
@@ -5,6 +5,7 @@
 #include "chromeos/ash/components/growth/campaigns_model.h"
 
 #include <memory>
+#include <optional>
 
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
@@ -58,6 +59,15 @@
 inline constexpr char kPayloadPathTemplate[] = "payload.%s";
 inline constexpr char kDemoModePayloadPath[] = "demoModeApp";
 
+// Actions
+inline constexpr char kActionTypePath[] = "type";
+inline constexpr char kActionParamsPath[] = "params";
+
+// Anchor paths.
+inline constexpr char kActiveAppWindowAnchorType[] =
+    "activeAppWindowAnchorType";
+inline constexpr char kShelfAppButtonId[] = "shelfAppButtonId";
+
 }  // namespace
 
 const Campaigns* GetCampaignsBySlot(const CampaignsPerSlot* campaigns_per_slot,
@@ -253,6 +263,22 @@
   return GetListCriteria(kExperimentTargetings);
 }
 
+Action::Action(const base::Value::Dict* action_dict)
+    : action_dict_(action_dict) {}
+
+std::optional<growth::ActionType> Action::GetActionType() const {
+  auto action_type_value = action_dict_->FindInt(kActionTypePath);
+  if (!action_type_value) {
+    return std::nullopt;
+  }
+
+  return static_cast<growth::ActionType>(action_type_value.value());
+}
+
+const base::Value::Dict* Action::GetParams() const {
+  return action_dict_->FindDict(kActionParamsPath);
+}
+
 const std::vector<std::unique_ptr<AppTargeting>>
 SessionTargeting::GetAppsOpened() const {
   std::vector<std::unique_ptr<AppTargeting>> app_targetings;
@@ -274,4 +300,33 @@
   return app_targetings;
 }
 
+// Anchor.
+Anchor::Anchor(const Targeting* anchor_dict) : anchor_dict_(anchor_dict) {}
+
+const std::optional<WindowAnchorType> Anchor::GetActiveAppWindowAnchorType()
+    const {
+  if (!anchor_dict_) {
+    // No valid anchor dict.
+    return std::nullopt;
+  }
+
+  const auto anchor_type = anchor_dict_->FindInt(kActiveAppWindowAnchorType);
+  if (!anchor_type) {
+    // Invalid anchor type.
+    // TODO(b/329698643): Record invalid anchor type metric.
+    return std::nullopt;
+  }
+
+  return static_cast<WindowAnchorType>(anchor_type.value());
+}
+
+const std::string* Anchor::GetShelfAppButtonId() const {
+  if (!anchor_dict_) {
+    // No valid anchor dict.
+    return nullptr;
+  }
+
+  return anchor_dict_->FindString(kShelfAppButtonId);
+}
+
 }  // namespace growth
diff --git a/chromeos/ash/components/growth/campaigns_model.h b/chromeos/ash/components/growth/campaigns_model.h
index 22c4b387..9052d60 100644
--- a/chromeos/ash/components/growth/campaigns_model.h
+++ b/chromeos/ash/components/growth/campaigns_model.h
@@ -10,6 +10,7 @@
 
 #include "base/component_export.h"
 #include "base/values.h"
+#include "chromeos/ash/components/growth/action_performer.h"
 
 namespace base {
 class Time;
@@ -27,6 +28,13 @@
   kMaxValue = kNudge
 };
 
+// Supported window anchor element.
+// These values are deserialized from Growth Campaign, so entries should not
+// be renumbered and numeric values should never be reused.
+enum class WindowAnchorType {
+  kCaptionButtonContainer = 0,
+};
+
 // Dictionary of supported targetings. For example:
 // {
 //    "demoMode" : {...},
@@ -234,6 +242,53 @@
   const std::vector<std::unique_ptr<AppTargeting>> GetAppsOpened() const;
 };
 
+// Wrapper around the action dictionary for performing an action, including
+// action type and action params.
+// For example:
+// {
+//   "action": {
+//     "type": 3,
+//     "params": {
+//       "url": "https://www.google.com",
+//       "disposition": 0
+//     }
+//   }
+// }
+class Action {
+ public:
+  explicit Action(const base::Value::Dict* action_dict);
+  Action(const Action&) = delete;
+  Action& operator=(const Action) = delete;
+  ~Action();
+
+  std::optional<growth::ActionType> GetActionType() const;
+  const base::Value::Dict* GetParams() const;
+
+  raw_ptr<const base::Value::Dict> action_dict_;
+};
+
+// Wrapper around anchor.
+//
+// The structure looks like:
+// {
+//   "activeAppWindowAnchorType": 0  // CAPTION_BUTTON_CONTAINER
+// }
+// TODO(b/329698643): Consider moving to nudge controller if Anchor is not used
+// by other surfaces.
+class Anchor {
+ public:
+  explicit Anchor(const base::Value::Dict* anchor_dict);
+  Anchor(const Anchor&) = delete;
+  Anchor& operator=(const Anchor) = delete;
+  ~Anchor();
+
+  const std::optional<WindowAnchorType> GetActiveAppWindowAnchorType() const;
+  const std::string* GetShelfAppButtonId() const;
+
+ private:
+  raw_ptr<const base::Value::Dict> anchor_dict_;
+};
+
 }  // namespace growth
 
 #endif  // CHROMEOS_ASH_COMPONENTS_GROWTH_CAMPAIGNS_MODEL_H_
diff --git a/chromeos/ash/components/network/network_connect.cc b/chromeos/ash/components/network/network_connect.cc
index 564dd3b..728b749a 100644
--- a/chromeos/ash/components/network/network_connect.cc
+++ b/chromeos/ash/components/network/network_connect.cc
@@ -64,8 +64,6 @@
   // NetworkConnect
   void ConnectToNetworkId(const std::string& network_id) override;
   void DisconnectFromNetworkId(const std::string& network_id) override;
-  void SetTechnologyEnabled(const NetworkTypePattern& technology,
-                            bool enabled_state) override;
   void ShowMobileSetup(const std::string& network_id) override;
   void ShowCarrierAccountDetail(const std::string& network_id) override;
   void ShowCarrierUnlockNotification() override;
@@ -402,59 +400,6 @@
       base::BindOnce(&IgnoreDisconnectError));
 }
 
-void NetworkConnectImpl::SetTechnologyEnabled(
-    const NetworkTypePattern& technology,
-    bool enabled_state) {
-  const std::string technology_string = technology.ToDebugString();
-  std::string log_string = base::StringPrintf(
-      "technology %s, target state: %s", technology_string.c_str(),
-      (enabled_state ? "ENABLED" : "DISABLED"));
-  NET_LOG(USER) << "SetTechnologyEnabled: " << log_string;
-  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
-  TechnologyStateController* controller =
-      NetworkHandler::Get()->technology_state_controller();
-  bool enabled = handler->IsTechnologyEnabled(technology);
-  if (enabled_state == enabled) {
-    NET_LOG(USER) << "Technology already in target state: " << log_string;
-    return;
-  }
-  if (enabled) {
-    // User requested to disable the technology.
-    NET_LOG(USER) << __func__ << " " << technology_string << ":" << false;
-    controller->SetTechnologiesEnabled(technology, false,
-                                       network_handler::ErrorCallback());
-    return;
-  }
-  // If we're dealing with a cellular network, then handle SIM lock here.
-  // SIM locking only applies to cellular.
-  if (technology.MatchesPattern(NetworkTypePattern::Cellular())) {
-    const DeviceState* mobile = handler->GetDeviceStateByType(technology);
-    if (!mobile) {
-      NET_LOG(ERROR) << "SetTechnologyEnabled with no device: " << log_string;
-      return;
-    }
-    if (mobile->IsSimAbsent()) {
-      // If this is true, then we have a cellular device with no SIM
-      // inserted. TODO(armansito): Chrome should display a notification here,
-      // prompting the user to insert a SIM card and restart the device to
-      // enable cellular. See crbug.com/125171.
-      NET_LOG(USER) << "Cannot enable cellular device without SIM: "
-                    << log_string;
-      return;
-    }
-    if (!mobile->IsSimLocked()) {
-      // A SIM has been inserted, but it is locked. Let the user unlock it
-      // via Settings or the details dialog.
-      const NetworkState* network = handler->FirstNetworkByType(technology);
-      delegate_->ShowNetworkSettings(network ? network->guid() : "");
-      return;
-    }
-  }
-  NET_LOG(USER) << __func__ << " " << technology_string << ":" << true;
-  controller->SetTechnologiesEnabled(technology, true,
-                                     network_handler::ErrorCallback());
-}
-
 void NetworkConnectImpl::ActivateCellular(const std::string& network_id) {
   NET_LOG(USER) << "ActivateCellular: " << NetworkGuidId(network_id);
   const NetworkState* cellular = GetNetworkStateFromId(network_id);
diff --git a/chromeos/ash/components/network/network_connect.h b/chromeos/ash/components/network/network_connect.h
index 34d0bb4..83f78d89 100644
--- a/chromeos/ash/components/network/network_connect.h
+++ b/chromeos/ash/components/network/network_connect.h
@@ -12,8 +12,6 @@
 
 namespace ash {
 
-class NetworkTypePattern;
-
 // NetworkConnect is a state machine designed to handle the complex UI flows
 // associated with connecting to a network (and related tasks). Any showing
 // of UI is handled by the NetworkConnect::Delegate implementation.
@@ -90,12 +88,6 @@
   // Requests a network disconnection. Ignores any errors and notifications.
   virtual void DisconnectFromNetworkId(const std::string& network_id) = 0;
 
-  // Enables or disables a network technology. If |technology| refers to
-  // cellular and the device cannot be enabled due to a SIM lock, this function
-  // will launch the SIM unlock dialog.
-  virtual void SetTechnologyEnabled(const NetworkTypePattern& technology,
-                                    bool enabled_state) = 0;
-
   // Determines whether or not a network requires a connection to activate or
   // setup and either shows a notification or opens the mobile setup dialog.
   virtual void ShowMobileSetup(const std::string& network_id) = 0;
diff --git a/chromeos/ash/components/tether/wifi_hotspot_connector_unittest.cc b/chromeos/ash/components/tether/wifi_hotspot_connector_unittest.cc
index 9885c620..254fc2c 100644
--- a/chromeos/ash/components/tether/wifi_hotspot_connector_unittest.cc
+++ b/chromeos/ash/components/tether/wifi_hotspot_connector_unittest.cc
@@ -201,8 +201,6 @@
     }
 
     // NetworkConnect:
-    void SetTechnologyEnabled(const NetworkTypePattern& technology,
-                              bool enabled_state) override {}
     void ShowMobileSetup(const std::string& network_id) override {}
     void ShowCarrierAccountDetail(const std::string& network_id) override {}
     void ShowCarrierUnlockNotification() override {}
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 7d92556..471ae7a 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -4064,6 +4064,9 @@
         <message name="IDS_TRAFFIC_COUNTERS_DATA_USAGE_DIFFERENT_FROM_PROVIDER_LABEL" desc="Settings > Network > Mobile data > SIM > Data usage, disclaimer about differences with data provider">
           Data is measured by your Chromebook. This may be different from provider data.
         </message>
+        <message name="IDS_TRAFFIC_COUNTERS_DATA_USAGE_RESET_DAY_TOOLTIP_TEXT" desc="Settings > Network > Mobile data > SIM > Data usage, text explaining what happens if last day of the month is before selected day">
+          If the last day of the month is before this day, data will be reset on the last day of the month
+        </message>
 
         <!-- Bluetooth -->
         <message name="IDS_BLUETOOTH_DEVICE_LIST_CURRENTLY_CONNECTED" desc="Title for list displaying paired Bluetooth devices that are currently connected.">
diff --git a/chromeos/chromeos_strings_grd/IDS_TRAFFIC_COUNTERS_DATA_USAGE_RESET_DAY_TOOLTIP_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_TRAFFIC_COUNTERS_DATA_USAGE_RESET_DAY_TOOLTIP_TEXT.png.sha1
new file mode 100644
index 0000000..29caf0f
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_TRAFFIC_COUNTERS_DATA_USAGE_RESET_DAY_TOOLTIP_TEXT.png.sha1
@@ -0,0 +1 @@
+4f6374084425afc7eb9d47dcf7bf90e21ad2cefe
\ No newline at end of file
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 687318cc..89b039b 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -168,10 +168,10 @@
 BASE_FEATURE(kKioskHeartbeatsViaERP,
              "KioskHeartbeatsViaERP",
              base::FEATURE_DISABLED_BY_DEFAULT);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Controls enabling / disabling the mahi feature.
 BASE_FEATURE(kMahi, "Mahi", base::FEATURE_DISABLED_BY_DEFAULT);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Controls enabling / disabling the orca feature.
 BASE_FEATURE(kOrca, "Orca", base::FEATURE_DISABLED_BY_DEFAULT);
@@ -372,7 +372,11 @@
 }
 
 bool IsMahiEnabled() {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  return chromeos::BrowserParamsProxy::Get()->IsMahiEnabled();
+#else
   return base::FeatureList::IsEnabled(kMahi);
+#endif
 }
 
 bool IsOrcaEnabled() {
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 8df5f073..87935402 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -70,8 +70,8 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 BASE_DECLARE_FEATURE(kKioskHeartbeatsViaERP);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kMahi);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kOrca);
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kOrcaDogfood);
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index cc2a969..4aa6ce5 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -1158,8 +1158,8 @@
 // parameters here. If a new parameter is added and its value is only known
 // after the user has logged in, please update BrowserPostLoginParams as well.
 //
-// Next version: 82
-// Next id: 82
+// Next version: 83
+// Next id: 83
 [Stable, RenamedFrom="crosapi.mojom.LacrosInitParams"]
 struct BrowserInitParams {
   // This is ash-chrome's version of the Crosapi interface. This is used by
@@ -1633,6 +1633,10 @@
   // Ash.
   [MinVersion=81]
   bool is_cros_mall_enabled@81;
+
+  // If true, "Mahi" will be enabled.
+  [MinVersion=82]
+  bool is_mahi_enabled@82;
 };
 
 // BrowserPostLoginParams is the subset of parameters in BrowserInitParams
diff --git a/chromeos/crosapi/mojom/download_status_updater.mojom b/chromeos/crosapi/mojom/download_status_updater.mojom
index 0fe6f397..6fcb6cab 100644
--- a/chromeos/crosapi/mojom/download_status_updater.mojom
+++ b/chromeos/crosapi/mojom/download_status_updater.mojom
@@ -24,7 +24,7 @@
   int64 received_bytes@1;
 
   // The total number of bytes expected to be written to the underlying download
-  // file, or -1 if the total number of bytes is unknown.
+  // file, or 0 if the total number of bytes is unknown.
   int64 total_bytes@2;
 
   // Indicates whether the progress should be visibly represented.
@@ -52,7 +52,7 @@
   [MinVersion=1] int64? received_bytes_deprecated@2;
 
   // Deprecated. The total number of bytes expected to be written to the
-  // underlying download file, or -1 if the total number of bytes is unknown.
+  // underlying download file, or 0 if the total number of bytes is unknown.
   [MinVersion=1] int64? total_bytes_deprecated@3;
 
   // The target path of an in-progress download. Note that the actual path being
diff --git a/chromeos/crosapi/mojom/native_theme.mojom b/chromeos/crosapi/mojom/native_theme.mojom
index bcfdd64..c0d1587 100644
--- a/chromeos/crosapi/mojom/native_theme.mojom
+++ b/chromeos/crosapi/mojom/native_theme.mojom
@@ -4,6 +4,7 @@
 
 module crosapi.mojom;
 
+import "mojo/public/mojom/base/time.mojom";
 import "skia/public/mojom/skcolor.mojom";
 import "ui/color/scheme_variant.mojom";
 
@@ -18,6 +19,9 @@
   // Describes the algorithm used to generate the color palette based on
   // `seed_color`.
   [MinVersion=1] color.mojom.SchemeVariant? scheme_variant;
+  // The interval at which to blink the text caret. If 0, the caret will
+  // not blink. If not set, a system default will be used.
+  [MinVersion=2] mojo_base.mojom.TimeDelta? caret_blink_interval;
 };
 
 // Interface for native theme info observers. Implemented by lacros-chrome.
diff --git a/chromeos/crosapi/mojom/test_controller.mojom b/chromeos/crosapi/mojom/test_controller.mojom
index fe2ca91..d4354c5 100644
--- a/chromeos/crosapi/mojom/test_controller.mojom
+++ b/chromeos/crosapi/mojom/test_controller.mojom
@@ -78,8 +78,8 @@
 // Implemented in lacros-chrome.
 // Lets the Ash browser tests that require Lacros to send commands to this
 // lacros-chrome instance.
-// Next version: 10
-// Next method id: 12
+// Next version: 11
+// Next method id: 13
 [Stable, Uuid="20e7f031-f4e1-4ad9-bd91-ad59eb8b1504"]
 interface StandaloneBrowserTestController {
   // Installs a test web app in lacros-chrome given a start URL and mode (open
@@ -141,6 +141,11 @@
   // Sets WebAppInstallForceList pref for a primary user profile.
   [MinVersion=9]
   SetWebAppInstallForceListPref@11(string policy) => (bool success);
+
+  // Installs a component extension at the given directory with the given
+  // extension id in Lacros.
+  [MinVersion=10]
+  InstallComponentExtension@12(string path, string extension_id) => ();
 };
 
 // Allows callers running in lacros to trigger test events that are passed to
diff --git a/chromeos/lacros/native_theme_cache.cc b/chromeos/lacros/native_theme_cache.cc
index 8a7687b8..8708065 100644
--- a/chromeos/lacros/native_theme_cache.cc
+++ b/chromeos/lacros/native_theme_cache.cc
@@ -6,9 +6,12 @@
 
 #include <optional>
 
+#include "base/time/time.h"
+#include "chromeos/crosapi/mojom/native_theme.mojom-forward.h"
 #include "chromeos/lacros/lacros_service.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/color/color_provider_key.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/native_theme/native_theme_aura.h"
 
 namespace chromeos {
@@ -49,6 +52,17 @@
   theme->set_scheme_variant(ConvertSchemeVariant(*info->scheme_variant));
 }
 
+void SetCaretBlinkIntervalOnTheme(
+    ui::NativeTheme* theme,
+    const crosapi::mojom::NativeThemeInfoPtr& info) {
+  if (!info->caret_blink_interval.has_value()) {
+    // A missing value indicates we should use the existing default within
+    // NativeTheme, so no further work is required.
+    return;
+  }
+  theme->set_caret_blink_interval(info->caret_blink_interval.value());
+}
+
 }  // namespace
 
 NativeThemeCache::NativeThemeCache(const crosapi::mojom::NativeThemeInfo& info)
@@ -79,6 +93,7 @@
   native_theme->set_use_dark_colors(dark_mode);
   SetSeedOnTheme(native_theme, info_);
   SetVariantOnTheme(native_theme, info_);
+  SetCaretBlinkIntervalOnTheme(native_theme, info_);
   native_theme->NotifyOnNativeThemeUpdated();
 
   auto* native_theme_web = ui::NativeTheme::GetInstanceForWeb();
@@ -88,6 +103,7 @@
                 : ui::NativeTheme::PreferredColorScheme::kLight);
   SetSeedOnTheme(native_theme_web, info_);
   SetVariantOnTheme(native_theme_web, info_);
+  SetCaretBlinkIntervalOnTheme(native_theme_web, info_);
   native_theme_web->NotifyOnNativeThemeUpdated();
 }
 
diff --git a/chromeos/startup/browser_params_proxy.cc b/chromeos/startup/browser_params_proxy.cc
index fa749f16..5c63d73 100644
--- a/chromeos/startup/browser_params_proxy.cc
+++ b/chromeos/startup/browser_params_proxy.cc
@@ -372,4 +372,8 @@
   return BrowserInitParams::Get()->is_cros_mall_enabled;
 }
 
+bool BrowserParamsProxy::IsMahiEnabled() const {
+  return BrowserInitParams::Get()->is_mahi_enabled;
+}
+
 }  // namespace chromeos
diff --git a/chromeos/startup/browser_params_proxy.h b/chromeos/startup/browser_params_proxy.h
index f46e337..647f420f 100644
--- a/chromeos/startup/browser_params_proxy.h
+++ b/chromeos/startup/browser_params_proxy.h
@@ -172,6 +172,8 @@
 
   bool IsCrosMallEnabled() const;
 
+  bool IsMahiEnabled() const;
+
  private:
   friend base::NoDestructor<BrowserParamsProxy>;
 
diff --git a/clank b/clank
index 2ab9ac8..11eafed 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 2ab9ac86f0c274f5df3781390b997f901bd4a958
+Subproject commit 11eafed3dc08cb3219299d340a5928280140e87a
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 143d751..b540f135 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -889,7 +889,7 @@
       "//base:base_java_test_support",
       "//content/public/test/android:content_java_test_support",
       "//content/shell/android:content_shell_browsertests_java",
-      "//testing/android/native_test:native_test_java",
+      "//testing/android/native_test:native_browser_test_java",
       "//ui/android:ui_full_java",
     ]
     sources = [
diff --git a/components/attribution_reporting/BUILD.gn b/components/attribution_reporting/BUILD.gn
index 8d9a5f11..57feb6a 100644
--- a/components/attribution_reporting/BUILD.gn
+++ b/components/attribution_reporting/BUILD.gn
@@ -8,6 +8,7 @@
 
 mojom("mojom") {
   sources = [
+    "os_registration_error.mojom",
     "registration_eligibility.mojom",
     "registration_header_type.mojom",
     "source_registration_error.mojom",
@@ -72,6 +73,7 @@
     "registrar.h",
     "registrar_info.cc",
     "registrar_info.h",
+    "registration_header_error.cc",
     "registration_header_error.h",
     "registration_info.cc",
     "registration_info.h",
@@ -145,6 +147,7 @@
     "parsing_utils_unittest.cc",
     "privacy_math_unittest.cc",
     "registrar_info_unittest.cc",
+    "registration_header_error_unittest.cc",
     "registration_info_unittest.cc",
     "source_registration_unittest.cc",
     "suitable_origin_unittest.cc",
diff --git a/components/attribution_reporting/aggregatable_trigger_config.cc b/components/attribution_reporting/aggregatable_trigger_config.cc
index 94b712c..f95a99b2 100644
--- a/components/attribution_reporting/aggregatable_trigger_config.cc
+++ b/components/attribution_reporting/aggregatable_trigger_config.cc
@@ -15,6 +15,7 @@
 #include "base/types/expected.h"
 #include "base/types/expected_macros.h"
 #include "base/values.h"
+#include "components/attribution_reporting/constants.h"
 #include "components/attribution_reporting/features.h"
 #include "components/attribution_reporting/source_registration_time_config.mojom.h"
 #include "components/attribution_reporting/trigger_registration_error.mojom.h"
@@ -26,15 +27,6 @@
 using ::attribution_reporting::mojom::SourceRegistrationTimeConfig;
 using ::attribution_reporting::mojom::TriggerRegistrationError;
 
-constexpr char kAggregatableSourceRegistrationTime[] =
-    "aggregatable_source_registration_time";
-constexpr char kTriggerContextId[] = "trigger_context_id";
-
-constexpr char kInclude[] = "include";
-constexpr char kExclude[] = "exclude";
-
-constexpr size_t kMaxTriggerContextIdLength = 64;
-
 base::expected<mojom::SourceRegistrationTimeConfig, TriggerRegistrationError>
 ParseAggregatableSourceRegistrationTime(const base::Value* value) {
   if (!value) {
@@ -48,11 +40,11 @@
             kAggregatableSourceRegistrationTimeValueInvalid);
   }
 
-  if (*str == kInclude) {
+  if (*str == kSourceRegistrationTimeInclude) {
     return SourceRegistrationTimeConfig::kInclude;
   }
 
-  if (*str == kExclude) {
+  if (*str == kSourceRegistrationTimeExclude) {
     return SourceRegistrationTimeConfig::kExclude;
   }
 
@@ -64,9 +56,9 @@
     SourceRegistrationTimeConfig config) {
   switch (config) {
     case SourceRegistrationTimeConfig::kInclude:
-      return kInclude;
+      return kSourceRegistrationTimeInclude;
     case SourceRegistrationTimeConfig::kExclude:
-      return kExclude;
+      return kSourceRegistrationTimeExclude;
   }
 }
 
diff --git a/components/attribution_reporting/aggregatable_trigger_data.cc b/components/attribution_reporting/aggregatable_trigger_data.cc
index 971f0911..c25345e7 100644
--- a/components/attribution_reporting/aggregatable_trigger_data.cc
+++ b/components/attribution_reporting/aggregatable_trigger_data.cc
@@ -13,6 +13,7 @@
 #include "base/types/expected.h"
 #include "base/types/expected_macros.h"
 #include "base/values.h"
+#include "components/attribution_reporting/constants.h"
 #include "components/attribution_reporting/filters.h"
 #include "components/attribution_reporting/parsing_utils.h"
 #include "components/attribution_reporting/trigger_registration_error.mojom.h"
@@ -24,9 +25,6 @@
 
 using ::attribution_reporting::mojom::TriggerRegistrationError;
 
-constexpr char kKeyPiece[] = "key_piece";
-constexpr char kSourceKeys[] = "source_keys";
-
 bool AreSourceKeysValid(const AggregatableTriggerData::Keys& source_keys) {
   return base::ranges::all_of(source_keys, [](const auto& key) {
     return AggregationKeyIdHasValidLength(key);
@@ -55,7 +53,7 @@
   base::Value::List* l = v->GetIfList();
   if (!l) {
     return base::unexpected(
-        TriggerRegistrationError::kAggregatableTriggerDataSourceKeysWrongType);
+        TriggerRegistrationError::kAggregatableTriggerDataSourceKeysInvalid);
   }
 
   AggregatableTriggerData::Keys source_keys;
@@ -64,8 +62,8 @@
   for (auto& maybe_string_value : *l) {
     std::string* s = maybe_string_value.GetIfString();
     if (!s || !AggregationKeyIdHasValidLength(*s)) {
-      return base::unexpected(TriggerRegistrationError::
-                                  kAggregatableTriggerDataSourceKeysKeyInvalid);
+      return base::unexpected(
+          TriggerRegistrationError::kAggregatableTriggerDataSourceKeysInvalid);
     }
 
     source_keys.emplace_back(std::move(*s));
diff --git a/components/attribution_reporting/aggregatable_trigger_data_unittest.cc b/components/attribution_reporting/aggregatable_trigger_data_unittest.cc
index 6aecde6..a8b2455 100644
--- a/components/attribution_reporting/aggregatable_trigger_data_unittest.cc
+++ b/components/attribution_reporting/aggregatable_trigger_data_unittest.cc
@@ -134,21 +134,21 @@
           base::test::ParseJson(
               R"json({"key_piece":"0x1234", "source_keys":{}})json"),
           ErrorIs(TriggerRegistrationError::
-                      kAggregatableTriggerDataSourceKeysWrongType),
+                      kAggregatableTriggerDataSourceKeysInvalid),
       },
       {
           "source_keys_key_wrong_type",
           base::test::ParseJson(
               R"json({"key_piece":"0x1234", "source_keys":[123]})json"),
           ErrorIs(TriggerRegistrationError::
-                      kAggregatableTriggerDataSourceKeysKeyInvalid),
+                      kAggregatableTriggerDataSourceKeysInvalid),
       },
       {
           "source_keys_key_too_long",
           make_aggregatable_trigger_data_with_key_length(
               kMaxBytesPerAggregationKeyId + 1),
           ErrorIs(TriggerRegistrationError::
-                      kAggregatableTriggerDataSourceKeysKeyInvalid),
+                      kAggregatableTriggerDataSourceKeysInvalid),
       },
       {
           "filters_wrong_type",
diff --git a/components/attribution_reporting/aggregatable_values.cc b/components/attribution_reporting/aggregatable_values.cc
index 9a79ed9..40d6904 100644
--- a/components/attribution_reporting/aggregatable_values.cc
+++ b/components/attribution_reporting/aggregatable_values.cc
@@ -25,8 +25,6 @@
 
 using ::attribution_reporting::mojom::TriggerRegistrationError;
 
-constexpr char kValues[] = "values";
-
 bool IsValueInRange(int value) {
   return value > 0 && value <= kMaxAggregatableValue;
 }
@@ -39,19 +37,19 @@
 }
 
 base::expected<AggregatableValues::Values, mojom::TriggerRegistrationError>
-ParseValues(const base::Value::Dict& dict) {
+ParseValues(const base::Value::Dict& dict,
+            TriggerRegistrationError key_error,
+            TriggerRegistrationError value_error) {
   AggregatableValues::Values::container_type container;
 
   for (auto [id, key_value] : dict) {
     if (!AggregationKeyIdHasValidLength(id)) {
-      return base::unexpected(
-          TriggerRegistrationError::kAggregatableValuesKeyTooLong);
+      return base::unexpected(key_error);
     }
 
     std::optional<int> int_value = key_value.GetIfInt();
     if (!int_value.has_value() || !IsValueInRange(*int_value)) {
-      return base::unexpected(
-          TriggerRegistrationError::kAggregatableValuesValueInvalid);
+      return base::unexpected(value_error);
     }
 
     container.emplace_back(id, *int_value);
@@ -81,7 +79,11 @@
   }
 
   if (base::Value::Dict* dict = input_value->GetIfDict()) {
-    ASSIGN_OR_RETURN(Values values, ParseValues(*dict));
+    ASSIGN_OR_RETURN(
+        Values values,
+        ParseValues(*dict,
+                    TriggerRegistrationError::kAggregatableValuesKeyTooLong,
+                    TriggerRegistrationError::kAggregatableValuesValueInvalid));
     if (!values.empty()) {
       configs.push_back(AggregatableValues(std::move(values), FilterPair()));
     }
@@ -91,7 +93,7 @@
       base::Value::Dict* dict_value = maybe_dict_value.GetIfDict();
       if (!dict_value) {
         return base::unexpected(
-            TriggerRegistrationError::kAggregatableValuesListWrongType);
+            TriggerRegistrationError::kAggregatableValuesWrongType);
       }
 
       const base::Value::Dict* agg_values_dict = dict_value->FindDict(kValues);
@@ -100,7 +102,12 @@
                                     kAggregatableValuesListValuesFieldMissing);
       }
 
-      ASSIGN_OR_RETURN(Values values, ParseValues(*agg_values_dict));
+      ASSIGN_OR_RETURN(
+          Values values,
+          ParseValues(
+              *agg_values_dict,
+              TriggerRegistrationError::kAggregatableValuesListKeyTooLong,
+              TriggerRegistrationError::kAggregatableValuesListValueInvalid));
       ASSIGN_OR_RETURN(FilterPair filters, FilterPair::FromJSON(*dict_value));
 
       configs.push_back(
diff --git a/components/attribution_reporting/aggregatable_values_unittest.cc b/components/attribution_reporting/aggregatable_values_unittest.cc
index 0933f0db..de54e52 100644
--- a/components/attribution_reporting/aggregatable_values_unittest.cc
+++ b/components/attribution_reporting/aggregatable_values_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/values_test_util.h"
 #include "base/types/expected.h"
 #include "base/values.h"
+#include "components/attribution_reporting/constants.h"
 #include "components/attribution_reporting/filters.h"
 #include "components/attribution_reporting/test_utils.h"
 #include "components/attribution_reporting/trigger_registration_error.mojom.h"
@@ -83,7 +84,7 @@
       {
           "list_element_wrong_type",
           R"json([123])json",
-          ErrorIs(TriggerRegistrationError::kAggregatableValuesListWrongType),
+          ErrorIs(TriggerRegistrationError::kAggregatableValuesWrongType),
       },
       {
           "list_values_field_missing",
@@ -97,6 +98,18 @@
                       kAggregatableValuesListValuesFieldMissing),
       },
       {
+          "list_values_invalid_value",
+          R"json([
+                {
+                  "values": {
+                    "a": 65537,
+                  }
+                }
+          ])json",
+          ErrorIs(
+              TriggerRegistrationError::kAggregatableValuesListValueInvalid),
+      },
+      {
           "list_filters_field_wrong_type",
           R"json([
                 {
@@ -164,6 +177,25 @@
               ErrorIs(TriggerRegistrationError::kAggregatableValuesKeyTooLong));
 }
 
+TEST(AggregatableValuesTest, Parse_ListKeyLength) {
+  auto parse_dict_with_key_length = [](size_t length) {
+    base::Value::Dict values;
+    values.Set(std::string(length, 'a'), 1);
+
+    base::Value value(base::Value::List().Append(
+        base::Value::Dict().Set(kValues, std::move(values))));
+    return AggregatableValues::FromJSON(&value);
+  };
+
+  for (size_t length = 0; length < 26; length++) {
+    EXPECT_THAT(parse_dict_with_key_length(length), ValueIs(_));
+  }
+
+  EXPECT_THAT(
+      parse_dict_with_key_length(26),
+      ErrorIs(TriggerRegistrationError::kAggregatableValuesListKeyTooLong));
+}
+
 TEST(AggregatableValuesTest, ToJson) {
   const struct {
     AggregatableValues input;
diff --git a/components/attribution_reporting/constants.h b/components/attribution_reporting/constants.h
index 02594e0..23e3336 100644
--- a/components/attribution_reporting/constants.h
+++ b/components/attribution_reporting/constants.h
@@ -12,28 +12,81 @@
 
 namespace attribution_reporting {
 
-constexpr size_t kMaxBytesPerFilterString = 25;
-constexpr size_t kMaxValuesPerFilter = 50;
-constexpr size_t kMaxFiltersPerSource = 50;
+inline constexpr size_t kMaxBytesPerFilterString = 25;
+inline constexpr size_t kMaxValuesPerFilter = 50;
+inline constexpr size_t kMaxFiltersPerSource = 50;
 
-constexpr size_t kMaxDestinations = 3;
+inline constexpr size_t kMaxDestinations = 3;
 
-constexpr size_t kMaxEventLevelReportWindows = 5;
+inline constexpr size_t kMaxEventLevelReportWindows = 5;
 
-constexpr size_t kMaxBytesPerAggregationKeyId = 25;
-constexpr size_t kMaxAggregationKeysPerSource = 20;
+inline constexpr size_t kMaxBytesPerAggregationKeyId = 25;
+inline constexpr size_t kMaxAggregationKeysPerSource = 20;
 
-constexpr int kMaxAggregatableValue = 65536;
+inline constexpr int kMaxAggregatableValue = 65536;
 
-constexpr base::TimeDelta kMinSourceExpiry = base::Days(1);
-constexpr base::TimeDelta kMaxSourceExpiry = base::Days(30);
+inline constexpr base::TimeDelta kMinSourceExpiry = base::Days(1);
+inline constexpr base::TimeDelta kMaxSourceExpiry = base::Days(30);
 
 static_assert(kMinSourceExpiry < kMaxSourceExpiry);
 
-constexpr base::TimeDelta kMinReportWindow = base::Hours(1);
+inline constexpr base::TimeDelta kMinReportWindow = base::Hours(1);
 
 static_assert(kMinReportWindow <= kMinSourceExpiry);
 
+inline constexpr int kMaxSettableEventLevelAttributionsPerSource = 20;
+
+// https://wicg.github.io/attribution-reporting-api/#max-distinct-trigger-data-per-source
+inline constexpr uint8_t kMaxTriggerDataPerSource = 32;
+
+inline constexpr size_t kMaxTriggerContextIdLength = 64;
+
+inline constexpr char kTriggerDataMatchingExact[] = "exact";
+inline constexpr char kTriggerDataMatchingModulus[] = "modulus";
+
+inline constexpr char kSummaryWindowOperatorCount[] = "count";
+inline constexpr char kSummaryWindowOperatorValueSum[] = "value_sum";
+
+inline constexpr char kAggregatableReportWindow[] =
+    "aggregatable_report_window";
+inline constexpr char kAggregationKeys[] = "aggregation_keys";
+inline constexpr char kDestination[] = "destination";
+inline constexpr char kEndTimes[] = "end_times";
+inline constexpr char kEventLevelEpsilon[] = "event_level_epsilon";
+inline constexpr char kEventReportWindow[] = "event_report_window";
+inline constexpr char kEventReportWindows[] = "event_report_windows";
+inline constexpr char kExpiry[] = "expiry";
+inline constexpr char kFilterData[] = "filter_data";
+inline constexpr char kMaxEventLevelReports[] = "max_event_level_reports";
+inline constexpr char kPriority[] = "priority";
+inline constexpr char kSourceEventId[] = "source_event_id";
+inline constexpr char kStartTime[] = "start_time";
+inline constexpr char kSummaryBuckets[] = "summary_buckets";
+inline constexpr char kSummaryWindowOperator[] = "summary_window_operator";
+inline constexpr char kTriggerData[] = "trigger_data";
+inline constexpr char kTriggerDataMatching[] = "trigger_data_matching";
+inline constexpr char kTriggerSpecs[] = "trigger_specs";
+
+inline constexpr char kAggregatableDeduplicationKeys[] =
+    "aggregatable_deduplication_keys";
+inline constexpr char kAggregatableSourceRegistrationTime[] =
+    "aggregatable_source_registration_time";
+inline constexpr char kAggregatableTriggerData[] = "aggregatable_trigger_data";
+inline constexpr char kAggregatableValues[] = "aggregatable_values";
+inline constexpr char kAggregationCoordinatorOrigin[] =
+    "aggregation_coordinator_origin";
+inline constexpr char kDeduplicationKey[] = "deduplication_key";
+inline constexpr char kEventTriggerData[] = "event_trigger_data";
+inline constexpr char kFilters[] = "filters";
+inline constexpr char kKeyPiece[] = "key_piece";
+inline constexpr char kSourceKeys[] = "source_keys";
+inline constexpr char kTriggerContextId[] = "trigger_context_id";
+inline constexpr char kValue[] = "value";
+inline constexpr char kValues[] = "values";
+
+inline constexpr char kSourceRegistrationTimeInclude[] = "include";
+inline constexpr char kSourceRegistrationTimeExclude[] = "exclude";
+
 }  // namespace attribution_reporting
 
 #endif  // COMPONENTS_ATTRIBUTION_REPORTING_CONSTANTS_H_
diff --git a/components/attribution_reporting/destination_set.cc b/components/attribution_reporting/destination_set.cc
index 1cbbb64..ea914830c 100644
--- a/components/attribution_reporting/destination_set.cc
+++ b/components/attribution_reporting/destination_set.cc
@@ -55,25 +55,25 @@
   using AppendIfValidResult = base::expected<void, SourceRegistrationError>;
 
   const auto append_if_valid =
-      [&](const std::string& str) -> AppendIfValidResult {
+      [&](const std::string& str,
+          SourceRegistrationError error) -> AppendIfValidResult {
     auto origin = SuitableOrigin::Deserialize(str);
     if (!origin.has_value()) {
-      return base::unexpected(
-          SourceRegistrationError::kDestinationUntrustworthy);
+      return base::unexpected(error);
     }
     destination_sites.emplace_back(*origin);
     return base::ok();
   };
 
   RETURN_IF_ERROR(v->Visit(base::Overloaded{
-      [&](const std::string& str) { return append_if_valid(str); },
+      [&](const std::string& str) {
+        return append_if_valid(
+            str, SourceRegistrationError::kDestinationUntrustworthy);
+      },
       [&](const base::Value::List& list) -> AppendIfValidResult {
-        if (list.empty()) {
-          return base::unexpected(SourceRegistrationError::kDestinationMissing);
-        }
-        if (list.size() > kMaxDestinations) {
+        if (list.empty() || list.size() > kMaxDestinations) {
           return base::unexpected(
-              SourceRegistrationError::kDestinationListTooLong);
+              SourceRegistrationError::kDestinationWrongType);
         }
 
         destination_sites.reserve(list.size());
@@ -84,7 +84,8 @@
             return base::unexpected(
                 SourceRegistrationError::kDestinationWrongType);
           }
-          RETURN_IF_ERROR(append_if_valid(*str));
+          RETURN_IF_ERROR(append_if_valid(
+              *str, SourceRegistrationError::kDestinationListUntrustworthy));
         }
 
         return base::ok();
diff --git a/components/attribution_reporting/destination_set_unittest.cc b/components/attribution_reporting/destination_set_unittest.cc
index 8f3683d2..2cd454b 100644
--- a/components/attribution_reporting/destination_set_unittest.cc
+++ b/components/attribution_reporting/destination_set_unittest.cc
@@ -58,7 +58,7 @@
       {
           "destination_list_empty",
           R"json([])json",
-          ErrorIs(SourceRegistrationError::kDestinationMissing),
+          ErrorIs(SourceRegistrationError::kDestinationWrongType),
       },
       {
           "destination_in_list_wrong_type",
@@ -68,7 +68,7 @@
       {
           "destination_in_list_untrustworthy",
           R"json(["http://d.example"])json",
-          ErrorIs(SourceRegistrationError::kDestinationUntrustworthy),
+          ErrorIs(SourceRegistrationError::kDestinationListUntrustworthy),
       },
       {
           "multiple_destinations",
@@ -92,7 +92,7 @@
             "https://f.example",
             "https://g.example"
           ])json",
-          ErrorIs(SourceRegistrationError::kDestinationListTooLong),
+          ErrorIs(SourceRegistrationError::kDestinationWrongType),
       },
   };
 
diff --git a/components/attribution_reporting/event_level_epsilon.cc b/components/attribution_reporting/event_level_epsilon.cc
index a67bfb7..e06fde0 100644
--- a/components/attribution_reporting/event_level_epsilon.cc
+++ b/components/attribution_reporting/event_level_epsilon.cc
@@ -10,6 +10,7 @@
 #include "base/check_op.h"
 #include "base/types/expected.h"
 #include "base/values.h"
+#include "components/attribution_reporting/constants.h"
 #include "components/attribution_reporting/source_registration_error.mojom.h"
 
 namespace attribution_reporting {
@@ -18,8 +19,6 @@
 
 using ::attribution_reporting::mojom::SourceRegistrationError;
 
-constexpr char kEventLevelEpsilon[] = "event_level_epsilon";
-
 double g_max_event_level_epsilon = 14;
 
 bool IsEventLevelEpsilonValid(double epsilon) {
@@ -45,6 +44,11 @@
   return EventLevelEpsilon(*d);
 }
 
+// static
+double EventLevelEpsilon::max() {
+  return g_max_event_level_epsilon;
+}
+
 EventLevelEpsilon::EventLevelEpsilon()
     : EventLevelEpsilon(g_max_event_level_epsilon) {}
 
diff --git a/components/attribution_reporting/event_level_epsilon.h b/components/attribution_reporting/event_level_epsilon.h
index f716f00b..48fe30c1 100644
--- a/components/attribution_reporting/event_level_epsilon.h
+++ b/components/attribution_reporting/event_level_epsilon.h
@@ -21,6 +21,8 @@
   static base::expected<EventLevelEpsilon, mojom::SourceRegistrationError>
   Parse(const base::Value::Dict&);
 
+  static double max();
+
   // Creates an epsilon with the maximum allowed value.
   EventLevelEpsilon();
 
diff --git a/components/attribution_reporting/event_report_windows.cc b/components/attribution_reporting/event_report_windows.cc
index f451ec71..2818b7d 100644
--- a/components/attribution_reporting/event_report_windows.cc
+++ b/components/attribution_reporting/event_report_windows.cc
@@ -33,11 +33,6 @@
 using ::attribution_reporting::mojom::SourceRegistrationError;
 using ::attribution_reporting::mojom::SourceType;
 
-constexpr char kEventReportWindow[] = "event_report_window";
-constexpr char kEventReportWindows[] = "event_report_windows";
-constexpr char kStartTime[] = "start_time";
-constexpr char kEndTimes[] = "end_times";
-
 bool IsValid(base::TimeDelta start_time,
              const base::flat_set<base::TimeDelta>& end_times) {
   return !start_time.is_negative() && !end_times.empty() &&
diff --git a/components/attribution_reporting/event_trigger_data.cc b/components/attribution_reporting/event_trigger_data.cc
index 1ad1a9a..dfd81f3 100644
--- a/components/attribution_reporting/event_trigger_data.cc
+++ b/components/attribution_reporting/event_trigger_data.cc
@@ -12,6 +12,7 @@
 #include "base/types/expected.h"
 #include "base/types/expected_macros.h"
 #include "base/values.h"
+#include "components/attribution_reporting/constants.h"
 #include "components/attribution_reporting/filters.h"
 #include "components/attribution_reporting/parsing_utils.h"
 #include "components/attribution_reporting/trigger_registration_error.mojom.h"
@@ -22,9 +23,6 @@
 
 using ::attribution_reporting::mojom::TriggerRegistrationError;
 
-constexpr char kTriggerData[] = "trigger_data";
-constexpr char kValue[] = "value";
-
 }  // namespace
 
 // static
diff --git a/components/attribution_reporting/filters.cc b/components/attribution_reporting/filters.cc
index 4760c5a..1cc25e3 100644
--- a/components/attribution_reporting/filters.cc
+++ b/components/attribution_reporting/filters.cc
@@ -43,7 +43,6 @@
   kValueTooLong,
 };
 
-constexpr char kFilters[] = "filters";
 constexpr char kNotFilters[] = "not_filters";
 
 bool IsValidForSource(const FilterValues& filter_values) {
@@ -366,14 +365,15 @@
     return FiltersDisjunction();
   }
 
-  const auto map_errors = [](FilterValuesError error) {
+  const auto map_errors = [](FilterValuesError error,
+                             TriggerRegistrationError value_error,
+                             TriggerRegistrationError reserved_key_error) {
     switch (error) {
       case FilterValuesError::kValueWrongType:
-        return TriggerRegistrationError::kFiltersValueWrongType;
-      case FilterValuesError::kKeyReserved:
-        return TriggerRegistrationError::kFiltersUsingReservedKey;
       case FilterValuesError::kListWrongType:
-        return TriggerRegistrationError::kFiltersListWrongType;
+        return value_error;
+      case FilterValuesError::kKeyReserved:
+        return reserved_key_error;
       case FilterValuesError::kTooManyKeys:
       case FilterValuesError::kKeyTooLong:
       case FilterValuesError::kListTooLong:
@@ -386,7 +386,10 @@
 
   using AppendIfValidResult = base::expected<void, TriggerRegistrationError>;
 
-  const auto append_if_valid = [&](base::Value& value) -> AppendIfValidResult {
+  const auto append_if_valid =
+      [&](base::Value& value, TriggerRegistrationError value_error,
+          TriggerRegistrationError lookback_window_error,
+          TriggerRegistrationError reserved_key_error) -> AppendIfValidResult {
     base::Value::Dict* dict = value.GetIfDict();
     if (!dict) {
       return base::unexpected(TriggerRegistrationError::kFiltersWrongType);
@@ -397,24 +400,25 @@
             dict->Extract(FilterConfig::kLookbackWindowKey)) {
       if (std::optional<int> int_val = lookback_window_value->GetIfInt()) {
         lookback_window = base::Seconds(*int_val);
+        if (!lookback_window->is_positive()) {
+          return base::unexpected(lookback_window_error);
+        }
       } else {
-        return base::unexpected(
-            TriggerRegistrationError::kFiltersValueWrongType);
+        return base::unexpected(lookback_window_error);
       }
     }
 
     ASSIGN_OR_RETURN(
         auto filter_values,
         ParseFilterValuesFromJSON(std::move(*dict), /*check_sizes=*/false)
-            .transform_error(map_errors));
+            .transform_error([&](FilterValuesError error) {
+              return map_errors(error, value_error, reserved_key_error);
+            }));
 
     if (!filter_values.empty() || lookback_window.has_value()) {
       auto config =
           FilterConfig::Create(std::move(filter_values), lookback_window);
-      if (!config.has_value()) {
-        return base::unexpected(
-            TriggerRegistrationError::kFiltersValueWrongType);
-      }
+      CHECK(config.has_value());
       disjunction.emplace_back(std::move(*config));
     }
     return base::ok();
@@ -423,10 +427,16 @@
   if (base::Value::List* list = input_value->GetIfList()) {
     disjunction.reserve(list->size());
     for (base::Value& item : *list) {
-      RETURN_IF_ERROR(append_if_valid(item));
+      RETURN_IF_ERROR(append_if_valid(
+          item, TriggerRegistrationError::kFiltersListValueInvalid,
+          TriggerRegistrationError::kFiltersListLookbackWindowValueInvalid,
+          TriggerRegistrationError::kFiltersListUsingReservedKey));
     }
   } else {
-    RETURN_IF_ERROR(append_if_valid(*input_value));
+    RETURN_IF_ERROR(append_if_valid(
+        *input_value, TriggerRegistrationError::kFiltersValueInvalid,
+        TriggerRegistrationError::kFiltersLookbackWindowValueInvalid,
+        TriggerRegistrationError::kFiltersUsingReservedKey));
   }
 
   return disjunction;
diff --git a/components/attribution_reporting/filters_unittest.cc b/components/attribution_reporting/filters_unittest.cc
index d33c15d..0cb64c6 100644
--- a/components/attribution_reporting/filters_unittest.cc
+++ b/components/attribution_reporting/filters_unittest.cc
@@ -85,18 +85,22 @@
   std::optional<base::Value> json;
   base::expected<FilterData, SourceRegistrationError> expected_filter_data;
   base::expected<FiltersDisjunction, TriggerRegistrationError> expected_filters;
+  base::expected<FiltersDisjunction, TriggerRegistrationError>
+      expected_filters_list;
 } kParseTestCases[] = {
     {
         "Null",
         std::nullopt,
         FilterData(),
         FiltersDisjunction(),
+        FiltersDisjunction(),
     },
     {
         "empty",
         base::Value(base::Value::Dict()),
         FilterData(),
         FiltersDisjunction(),
+        FiltersDisjunction(),
     },
     {
         "multiple",
@@ -115,6 +119,11 @@
             {"c", {"e", "d"}},
             {"f", {}},
         })}),
+        FiltersDisjunction({*FilterConfig::Create({
+            {"a", {"b"}},
+            {"c", {"e", "d"}},
+            {"f", {}},
+        })}),
     },
     {
         "source_type_key",
@@ -123,6 +132,7 @@
         })json"),
         base::unexpected(SourceRegistrationError::kFilterDataKeyReserved),
         FiltersDisjunction({*FilterConfig::Create({{{"source_type", {"a"}}}})}),
+        FiltersDisjunction({*FilterConfig::Create({{{"source_type", {"a"}}}})}),
     },
     {
         "lookback_window_key",
@@ -132,6 +142,8 @@
         base::unexpected(SourceRegistrationError::kFilterDataKeyReserved),
         FiltersDisjunction(
             {*FilterConfig::Create({}, /*lookback_window=*/base::Seconds(1))}),
+        FiltersDisjunction(
+            {*FilterConfig::Create({}, /*lookback_window=*/base::Seconds(1))}),
     },
     {
         "reserved_key",
@@ -140,36 +152,47 @@
         })json"),
         base::unexpected(SourceRegistrationError::kFilterDataKeyReserved),
         base::unexpected(TriggerRegistrationError::kFiltersUsingReservedKey),
+        base::unexpected(
+            TriggerRegistrationError::kFiltersListUsingReservedKey),
     },
     {
         "lookback_window_list",
         base::test::ParseJson(R"json({"_lookback_window": ["a"]})json"),
         base::unexpected(SourceRegistrationError::kFilterDataKeyReserved),
-        base::unexpected(TriggerRegistrationError::kFiltersValueWrongType),
+        base::unexpected(
+            TriggerRegistrationError::kFiltersLookbackWindowValueInvalid),
+        base::unexpected(
+            TriggerRegistrationError::kFiltersListLookbackWindowValueInvalid),
     },
     {
         "lookback_window_not_positive",
         base::test::ParseJson(R"json({"_lookback_window": 0})json"),
         base::unexpected(SourceRegistrationError::kFilterDataKeyReserved),
-        base::unexpected(TriggerRegistrationError::kFiltersValueWrongType),
+        base::unexpected(
+            TriggerRegistrationError::kFiltersLookbackWindowValueInvalid),
+        base::unexpected(
+            TriggerRegistrationError::kFiltersListLookbackWindowValueInvalid),
     },
     {
         "wrong_type",
         base::Value("foo"),
         base::unexpected(SourceRegistrationError::kFilterDataDictInvalid),
         base::unexpected(TriggerRegistrationError::kFiltersWrongType),
+        base::unexpected(TriggerRegistrationError::kFiltersWrongType),
     },
     {
         "value_not_array",
         base::test::ParseJson(R"json({"a": true})json"),
         base::unexpected(SourceRegistrationError::kFilterDataListInvalid),
-        base::unexpected(TriggerRegistrationError::kFiltersListWrongType),
+        base::unexpected(TriggerRegistrationError::kFiltersValueInvalid),
+        base::unexpected(TriggerRegistrationError::kFiltersListValueInvalid),
     },
     {
         "array_element_not_string",
         base::test::ParseJson(R"json({"a": [true]})json"),
         base::unexpected(SourceRegistrationError::kFilterDataListValueInvalid),
-        base::unexpected(TriggerRegistrationError::kFiltersValueWrongType),
+        base::unexpected(TriggerRegistrationError::kFiltersValueInvalid),
+        base::unexpected(TriggerRegistrationError::kFiltersListValueInvalid),
     },
 };
 
@@ -332,7 +355,8 @@
     auto list = base::Value::List();
     list.Append(test_case.json->Clone());
     auto json_copy = base::Value(std::move(list));
-    EXPECT_EQ(FiltersFromJSONForTesting(&json_copy), test_case.expected_filters)
+    EXPECT_EQ(FiltersFromJSONForTesting(&json_copy),
+              test_case.expected_filters_list)
         << test_case.description << " within a list";
   }
   {
diff --git a/components/attribution_reporting/max_event_level_reports.cc b/components/attribution_reporting/max_event_level_reports.cc
index 5bfe9524..655b7a8 100644
--- a/components/attribution_reporting/max_event_level_reports.cc
+++ b/components/attribution_reporting/max_event_level_reports.cc
@@ -9,6 +9,7 @@
 #include "base/check.h"
 #include "base/types/expected.h"
 #include "base/values.h"
+#include "components/attribution_reporting/constants.h"
 #include "components/attribution_reporting/source_registration_error.mojom.h"
 #include "components/attribution_reporting/source_type.mojom.h"
 
@@ -19,10 +20,6 @@
 using ::attribution_reporting::mojom::SourceRegistrationError;
 using ::attribution_reporting::mojom::SourceType;
 
-constexpr int kMaxSettableEventLevelAttributionsPerSource = 20;
-
-constexpr char kMaxEventLevelReports[] = "max_event_level_reports";
-
 bool IsMaxEventLevelReportsValid(int i) {
   return i >= 0 && i <= kMaxSettableEventLevelAttributionsPerSource;
 }
diff --git a/components/attribution_reporting/os_registration.cc b/components/attribution_reporting/os_registration.cc
index 61dda11..da36eb14 100644
--- a/components/attribution_reporting/os_registration.cc
+++ b/components/attribution_reporting/os_registration.cc
@@ -10,23 +10,29 @@
 
 #include "base/check_op.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/types/expected.h"
+#include "components/attribution_reporting/os_registration_error.mojom-shared.h"
 #include "net/http/structured_headers.h"
 #include "url/gurl.h"
 
 namespace attribution_reporting {
 
-std::vector<OsRegistrationItem> ParseOsSourceOrTriggerHeader(
-    std::string_view header) {
+namespace {
+using ::attribution_reporting::mojom::OsRegistrationError;
+}  // namespace
+
+base::expected<std::vector<OsRegistrationItem>, OsRegistrationError>
+ParseOsSourceOrTriggerHeader(std::string_view header) {
   const auto list = net::structured_headers::ParseList(header);
   if (!list) {
-    return {};
+    return base::unexpected(OsRegistrationError::kInvalidList);
   }
 
   return ParseOsSourceOrTriggerHeader(*list);
 }
 
-std::vector<OsRegistrationItem> ParseOsSourceOrTriggerHeader(
-    const net::structured_headers::List& list) {
+base::expected<std::vector<OsRegistrationItem>, OsRegistrationError>
+ParseOsSourceOrTriggerHeader(const net::structured_headers::List& list) {
   std::vector<OsRegistrationItem> items;
   items.reserve(list.size());
 
@@ -66,6 +72,10 @@
   base::UmaHistogramCounts100("Conversions.OsRegistrationItemsPerHeader",
                               items.size());
 
+  if (items.empty()) {
+    return base::unexpected(OsRegistrationError::kInvalidList);
+  }
+
   return items;
 }
 
diff --git a/components/attribution_reporting/os_registration.h b/components/attribution_reporting/os_registration.h
index 085a2ac..9e567f7 100644
--- a/components/attribution_reporting/os_registration.h
+++ b/components/attribution_reporting/os_registration.h
@@ -9,6 +9,8 @@
 #include <vector>
 
 #include "base/component_export.h"
+#include "base/types/expected.h"
+#include "components/attribution_reporting/os_registration_error.mojom-forward.h"
 #include "net/http/structured_headers.h"
 #include "url/gurl.h"
 
@@ -25,20 +27,21 @@
 // Parses an Attribution-Reporting-OS-Source or
 // Attribution-Reporting-Register-OS-Trigger header.
 //
-// Returns an empty vector if the string is not parsable as a structured-header
-// list. List members that are not strings or do not contain a valid URL are
-// ignored.
+// Returns `OsRegistrationError` if the string is not parsable as a
+// structured-header list. List members that are not strings or do not contain a
+// valid URL are ignored.
 //
 // Example:
 //
 // "https://x.test/abc", "https://y.test/123"
 COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
-std::vector<OsRegistrationItem> ParseOsSourceOrTriggerHeader(std::string_view);
+base::expected<std::vector<OsRegistrationItem>, mojom::OsRegistrationError>
+    ParseOsSourceOrTriggerHeader(std::string_view);
 
 // Same as the above, but using an already-parsed structured-header list.
 COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
-std::vector<OsRegistrationItem> ParseOsSourceOrTriggerHeader(
-    const net::structured_headers::List&);
+base::expected<std::vector<OsRegistrationItem>, mojom::OsRegistrationError>
+ParseOsSourceOrTriggerHeader(const net::structured_headers::List&);
 
 }  // namespace attribution_reporting
 
diff --git a/components/attribution_reporting/os_registration_error.mojom b/components/attribution_reporting/os_registration_error.mojom
new file mode 100644
index 0000000..15f9d944
--- /dev/null
+++ b/components/attribution_reporting/os_registration_error.mojom
@@ -0,0 +1,10 @@
+// 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.
+
+module attribution_reporting.mojom;
+
+// Represents error from OS registration header validation.
+enum OsRegistrationError {
+  kInvalidList,
+};
diff --git a/components/attribution_reporting/os_registration_unittest.cc b/components/attribution_reporting/os_registration_unittest.cc
index 095c050..54aa061 100644
--- a/components/attribution_reporting/os_registration_unittest.cc
+++ b/components/attribution_reporting/os_registration_unittest.cc
@@ -5,100 +5,111 @@
 #include "components/attribution_reporting/os_registration.h"
 
 #include <string_view>
+#include <tuple>
 #include <vector>
 
+#include "base/test/gmock_expected_support.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/types/expected.h"
+#include "components/attribution_reporting/os_registration_error.mojom-shared.h"
 #include "components/attribution_reporting/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
 namespace attribution_reporting {
 namespace {
 
+using ::attribution_reporting::mojom::OsRegistrationError;
+using ::base::test::ErrorIs;
+using ::base::test::ValueIs;
+using ::testing::ElementsAre;
+
 TEST(OsRegistration, ParseOsSourceOrTriggerHeader) {
   const struct {
     const char* description;
     std::string_view header;
-    std::vector<OsRegistrationItem> expected;
+    ::testing::Matcher<
+        base::expected<std::vector<OsRegistrationItem>, OsRegistrationError>>
+        matches;
   } kTestCases[] = {
       {
           "empty",
           "",
-          {},
+          ErrorIs(OsRegistrationError::kInvalidList),
       },
       {
           "invalid_url",
           R"("foo")",
-          {},
+          ErrorIs(OsRegistrationError::kInvalidList),
       },
       {
           "integer",
           "123",
-          {},
+          ErrorIs(OsRegistrationError::kInvalidList),
       },
       {
           "token",
           "d",
-          {},
+          ErrorIs(OsRegistrationError::kInvalidList),
       },
       {
           "byte_sequence",
           ":YWJj:",
-          {},
+          ErrorIs(OsRegistrationError::kInvalidList),
       },
       {
           "valid_url_no_params",
           R"("https://d.test")",
-          {OsRegistrationItem{.url = GURL("https://d.test")}},
+          ValueIs(
+              ElementsAre(OsRegistrationItem{.url = GURL("https://d.test")})),
       },
       {
           "extra_params_ignored",
           R"("https://d.test"; y=1)",
-          {OsRegistrationItem{.url = GURL("https://d.test")}},
+          ValueIs(
+              ElementsAre(OsRegistrationItem{.url = GURL("https://d.test")})),
       },
       {
           "inner_list",
           R"(("https://d.test"))",
-          {},
+          ErrorIs(OsRegistrationError::kInvalidList),
       },
       {
           "multiple",
           R"(123, "https://d.test", "", "https://e.test")",
-          {
-              OsRegistrationItem{.url = GURL("https://d.test")},
-              OsRegistrationItem{.url = GURL("https://e.test")},
-          },
+          ValueIs(
+              ElementsAre(OsRegistrationItem{.url = GURL("https://d.test")},
+                          OsRegistrationItem{.url = GURL("https://e.test")})),
       },
       {
           "debug_reporting_param",
           R"("https://d.test", "https://e.test";debug-reporting, "https://f.test";debug-reporting=?0)",
-          {
-              OsRegistrationItem{.url = GURL("https://d.test")},
-              OsRegistrationItem{.url = GURL("https://e.test"),
-                                 .debug_reporting = true},
-              OsRegistrationItem{.url = GURL("https://f.test")},
-          },
+          ValueIs(
+              ElementsAre(OsRegistrationItem{.url = GURL("https://d.test")},
+                          OsRegistrationItem{.url = GURL("https://e.test"),
+                                             .debug_reporting = true},
+                          OsRegistrationItem{.url = GURL("https://f.test")})),
       },
       {
           "debug_reporting_param_wrong_type",
           R"("https://d.test"; debug-reporting=1)",
-          {
-              OsRegistrationItem{.url = GURL("https://d.test")},
-          },
+          ValueIs(
+              ElementsAre(OsRegistrationItem{.url = GURL("https://d.test")})),
       },
   };
 
   for (const auto& test_case : kTestCases) {
-    EXPECT_EQ(ParseOsSourceOrTriggerHeader(test_case.header),
-              test_case.expected)
-        << test_case.description;
+    SCOPED_TRACE(test_case.description);
+    EXPECT_THAT(ParseOsSourceOrTriggerHeader(test_case.header),
+                test_case.matches);
   }
 }
 
 TEST(OsRegistration, EmitItemsPerHeaderHistogram) {
   base::HistogramTester histogram;
 
-  ParseOsSourceOrTriggerHeader(
+  std::ignore = ParseOsSourceOrTriggerHeader(
       R"(123, "https://d.test", "", "https://e.test")");
 
   histogram.ExpectUniqueSample("Conversions.OsRegistrationItemsPerHeader", 2,
diff --git a/components/attribution_reporting/parse_os_header_fuzzer.cc b/components/attribution_reporting/parse_os_header_fuzzer.cc
index e4dc89d..11e49bc 100644
--- a/components/attribution_reporting/parse_os_header_fuzzer.cc
+++ b/components/attribution_reporting/parse_os_header_fuzzer.cc
@@ -6,6 +6,7 @@
 #include <stdint.h>
 
 #include <string_view>
+#include <tuple>
 
 #include "base/command_line.h"
 #include "base/i18n/icu_util.h"
@@ -25,7 +26,7 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   static Environment env;
-  attribution_reporting::ParseOsSourceOrTriggerHeader(
+  std::ignore = attribution_reporting::ParseOsSourceOrTriggerHeader(
       std::string_view(reinterpret_cast<const char*>(data), size));
   return 0;
 }
diff --git a/components/attribution_reporting/parsing_utils.cc b/components/attribution_reporting/parsing_utils.cc
index 4b4ace12..1dc050e 100644
--- a/components/attribution_reporting/parsing_utils.cc
+++ b/components/attribution_reporting/parsing_utils.cc
@@ -29,8 +29,6 @@
 
 constexpr char kDebugKey[] = "debug_key";
 constexpr char kDebugReporting[] = "debug_reporting";
-constexpr char kDeduplicationKey[] = "deduplication_key";
-constexpr char kPriority[] = "priority";
 
 template <typename T>
 base::expected<std::optional<T>, ParseError> ParseIntegerFromString(
diff --git a/components/attribution_reporting/registration_header_error.cc b/components/attribution_reporting/registration_header_error.cc
new file mode 100644
index 0000000..468b56d6
--- /dev/null
+++ b/components/attribution_reporting/registration_header_error.cc
@@ -0,0 +1,485 @@
+// 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/attribution_reporting/registration_header_error.h"
+
+#include <stdint.h>
+
+#include <limits>
+#include <string>
+#include <string_view>
+#include <utility>
+
+#include "base/containers/span.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "components/attribution_reporting/constants.h"
+#include "components/attribution_reporting/event_level_epsilon.h"
+#include "components/attribution_reporting/filters.h"
+#include "components/attribution_reporting/os_registration_error.mojom-shared.h"
+#include "components/attribution_reporting/registration_header_type.mojom-shared.h"
+#include "components/attribution_reporting/source_registration_error.mojom-shared.h"
+#include "components/attribution_reporting/trigger_registration_error.mojom-shared.h"
+
+namespace attribution_reporting {
+
+namespace {
+
+using ::attribution_reporting::mojom::OsRegistrationError;
+using ::attribution_reporting::mojom::RegistrationHeaderType;
+using ::attribution_reporting::mojom::SourceRegistrationError;
+using ::attribution_reporting::mojom::TriggerRegistrationError;
+
+constexpr char kIndex[] = "*";
+
+constexpr char kAggregationKeyPieceMsg[] =
+    R"(must be a base16-encoded string of a uint128 with a "0x" prefix)";
+constexpr char kBase10EncodedMsg[] = "must be a base10-encoded string of a ";
+constexpr char kDictionaryMsg[] = "must be a dictionary";
+constexpr char kInvalidJsonMsg[] = "invalid JSON";
+constexpr char kListOfDictionariesMsg[] = "must be a list of dictionaries";
+constexpr char kKeyLengthMsg[] = "key length must be less than or equal to ";
+constexpr char kOsInvalidListMsg[] = "must be a list of URLs";
+constexpr char kPositiveIntegerMsg[] = "must be a positive integer";
+constexpr char kRequiredMsg[] = "required";
+
+base::Value GetPath(base::span<const std::string_view> parts) {
+  base::Value::List list;
+  list.reserve(parts.size());
+  for (const std::string_view part : parts) {
+    list.Append(part);
+  }
+  return base::Value(std::move(list));
+}
+
+base::Value SerializeErrorDetails(base::Value path, std::string msg) {
+  if (msg.empty()) {
+    // Should only be possible with compromised renderers.
+    return base::Value();
+  }
+
+  base::Value::Dict dict;
+  if (path.is_list()) {
+    dict.Set("path", std::move(path));
+  }
+  dict.Set("msg", std::move(msg));
+  return base::Value(std::move(dict));
+}
+
+std::string MutuallyExclusiveMsg(base::span<const std::string_view> fields) {
+  return base::StrCat(
+      {"mutually exclusive fields: ", base::JoinString(fields, ", ")});
+}
+
+std::string ListLengthMsg(size_t min, std::string_view max) {
+  return base::StrCat({"must be a list whose length is in the range [",
+                       base::NumberToString(min), ", ", max, "]"});
+}
+
+std::string DictionarySizeMsg(size_t max) {
+  return base::StrCat({"must be a dictionary with size less than or equal to ",
+                       base::NumberToString(max)});
+}
+
+std::string EnumMsg(base::span<const std::string_view> fields) {
+  return base::StrCat({"must be one of the following (case-sensitive): ",
+                       base::JoinString(fields, ", ")});
+}
+
+std::string ExcessiveTriggerDataMsg() {
+  return base::StrCat({"must not exceed a maximum of ",
+                       base::NumberToString(kMaxTriggerDataPerSource),
+                       " distinct trigger_data values"});
+}
+
+std::string InvalidTriggerDataMsg() {
+  return base::StrCat(
+      {"must be a list of non-negative integers in the range [0, ",
+       base::NumberToString(std::numeric_limits<uint32_t>::max()), "]"});
+}
+
+std::string AggregationKeyTooLongMsg() {
+  return base::StrCat(
+      {kKeyLengthMsg, base::NumberToString(kMaxBytesPerAggregationKeyId)});
+}
+
+std::string AggregatableValueMsg() {
+  return base::StrCat({"must be an integer in the range [1, ",
+                       base::NumberToString(kMaxAggregatableValue), "]"});
+}
+
+base::Value ErrorDetails(OsRegistrationError error) {
+  std::string msg;
+  switch (error) {
+    case OsRegistrationError::kInvalidList:
+      msg = kOsInvalidListMsg;
+      break;
+  }
+
+  return SerializeErrorDetails(/*path=*/base::Value(), std::move(msg));
+}
+
+}  // namespace
+
+base::Value ErrorDetails(SourceRegistrationError error) {
+  static constexpr char kDestinationPotentiallyTrustworthyMsg[] =
+      "must be a potentially trustworthy URL that uses HTTP/HTTPS";
+  static constexpr char kDuplicateTriggerDataMsg[] =
+      "must not contain duplicate trigger_data";
+  static constexpr char kExpiryOrReportWindowInvalidMsg[] =
+      "must be a non-negative integer or a base10-encoded string of a uint64";
+
+  base::Value path;
+  std::string msg;
+
+  switch (error) {
+    case SourceRegistrationError::kInvalidJson:
+      msg = kInvalidJsonMsg;
+      break;
+    case SourceRegistrationError::kRootWrongType:
+      path = base::Value(base::Value::List());
+      msg = kDictionaryMsg;
+      break;
+    case SourceRegistrationError::kDestinationMissing:
+      path = GetPath({{kDestination}});
+      msg = kRequiredMsg;
+      break;
+    case SourceRegistrationError::kDestinationWrongType:
+      path = GetPath({{kDestination}});
+      msg = base::StrCat({"must be a string or a list of 1-",
+                          base::NumberToString(kMaxDestinations), " strings"});
+      break;
+    case SourceRegistrationError::kDestinationUntrustworthy:
+      path = GetPath({{kDestination}});
+      msg = kDestinationPotentiallyTrustworthyMsg;
+      break;
+    case SourceRegistrationError::kDestinationListUntrustworthy:
+      path = GetPath({{kDestination, kIndex}});
+      msg = kDestinationPotentiallyTrustworthyMsg;
+      break;
+    case SourceRegistrationError::kFilterDataKeyTooLong:
+      path = GetPath({{kFilterData}});
+      msg = base::StrCat(
+          {kKeyLengthMsg, base::NumberToString(kMaxBytesPerFilterString)});
+      break;
+    case SourceRegistrationError::kFilterDataKeyReserved:
+      path = GetPath({{kFilterData}});
+      msg = R"("source_type" and keys starting with "_" are reserved)";
+      break;
+    case SourceRegistrationError::kFilterDataDictInvalid:
+      path = GetPath({{kFilterData}});
+      msg = DictionarySizeMsg(kMaxFiltersPerSource);
+      break;
+    case SourceRegistrationError::kFilterDataListInvalid:
+      path = GetPath({{kFilterData, kIndex}});
+      msg = ListLengthMsg(0, base::NumberToString(kMaxValuesPerFilter));
+      break;
+    case SourceRegistrationError::kFilterDataListValueInvalid:
+      path = GetPath({{kFilterData, kIndex, kIndex}});
+      msg = base::StrCat({"must be a string with length less than or equal to ",
+                          base::NumberToString(kMaxBytesPerFilterString)});
+      break;
+    case SourceRegistrationError::kAggregationKeysKeyTooLong:
+      path = GetPath({{kAggregationKeys}});
+      msg = base::StrCat(
+          {kKeyLengthMsg, base::NumberToString(kMaxBytesPerAggregationKeyId)});
+      break;
+    case SourceRegistrationError::kAggregationKeysDictInvalid:
+      path = GetPath({{kAggregationKeys}});
+      msg = DictionarySizeMsg(kMaxAggregationKeysPerSource);
+      break;
+    case SourceRegistrationError::kAggregationKeysValueInvalid:
+      path = GetPath({{kAggregationKeys, kIndex}});
+      msg = kAggregationKeyPieceMsg;
+      break;
+    case SourceRegistrationError::kSourceEventIdValueInvalid:
+      path = GetPath({{kSourceEventId}});
+      msg = base::StrCat({kBase10EncodedMsg, "uint64"});
+      break;
+    case SourceRegistrationError::kPriorityValueInvalid:
+      path = GetPath({{kPriority}});
+      msg = base::StrCat({kBase10EncodedMsg, "int64"});
+      break;
+    case SourceRegistrationError::kExpiryValueInvalid:
+      path = GetPath({{kExpiry}});
+      msg = kExpiryOrReportWindowInvalidMsg;
+      break;
+    case SourceRegistrationError::kEventReportWindowValueInvalid:
+      path = GetPath({{kEventReportWindow}});
+      msg = kExpiryOrReportWindowInvalidMsg;
+      break;
+    case SourceRegistrationError::kAggregatableReportWindowValueInvalid:
+      path = GetPath({{kAggregatableReportWindow}});
+      msg = kExpiryOrReportWindowInvalidMsg;
+      break;
+    case SourceRegistrationError::kMaxEventLevelReportsValueInvalid:
+      path = GetPath({{kMaxEventLevelReports}});
+      msg = base::StrCat(
+          {"must be a non-negative integer and less than or equal to ",
+           base::NumberToString(kMaxSettableEventLevelAttributionsPerSource)});
+      break;
+    case SourceRegistrationError::kEventReportWindowsWrongType:
+      path = GetPath({{kEventReportWindows}});
+      msg = kDictionaryMsg;
+      break;
+    case SourceRegistrationError::kEventReportWindowsEndTimesMissing:
+      path = GetPath({{kEventReportWindows, kEndTimes}});
+      msg = kRequiredMsg;
+      break;
+    case SourceRegistrationError::kEventReportWindowsEndTimeDurationLTEStart:
+      path = GetPath({{kEventReportWindows, kEndTimes, kIndex}});
+      msg =
+          "must be greater than start_time and greater than previous end_time";
+      break;
+    case SourceRegistrationError::kBothEventReportWindowFieldsFound:
+      path = base::Value(base::Value::List());
+      msg = MutuallyExclusiveMsg({{kEventReportWindow, kEventReportWindows}});
+      break;
+    case SourceRegistrationError::kEventReportWindowsEndTimesListInvalid:
+      path = GetPath({{kEventReportWindows, kEndTimes}});
+      msg = ListLengthMsg(1, base::NumberToString(kMaxEventLevelReportWindows));
+      break;
+    case SourceRegistrationError::kEventReportWindowsStartTimeInvalid:
+      path = GetPath({{kEventReportWindows, kStartTime}});
+      msg = base::StrCat(
+          {"must be a non-negative integer and less than or equal to ",
+           kExpiry});
+      break;
+    case SourceRegistrationError::kEventReportWindowsEndTimeValueInvalid:
+      path = GetPath({{kEventReportWindows, kEndTimes}});
+      msg = kPositiveIntegerMsg;
+      break;
+    case SourceRegistrationError::kTriggerDataMatchingValueInvalid:
+      path = GetPath({{kTriggerDataMatching}});
+      msg = EnumMsg({{kTriggerDataMatchingExact, kTriggerDataMatchingModulus}});
+      break;
+    case SourceRegistrationError::kTriggerSpecsWrongType:
+      path = GetPath({{kTriggerSpecs}});
+      msg = kListOfDictionariesMsg;
+      break;
+    case SourceRegistrationError::kTriggerSpecTriggerDataMissing:
+      path = GetPath({{kTriggerSpecs, kIndex, kTriggerData}});
+      msg = kRequiredMsg;
+      break;
+    case SourceRegistrationError::kTriggerDataListInvalid:
+      path = GetPath({{kTriggerData}});
+      msg = InvalidTriggerDataMsg();
+      break;
+    case SourceRegistrationError::kTriggerSpecTriggerDataListInvalid:
+      path = GetPath({{kTriggerSpecs, kIndex, kTriggerData}});
+      msg = InvalidTriggerDataMsg();
+      break;
+    case SourceRegistrationError::kDuplicateTriggerData:
+      path = GetPath({{kTriggerData}});
+      msg = kDuplicateTriggerDataMsg;
+      break;
+    case SourceRegistrationError::kTriggerSpecDuplicateTriggerData:
+      path = GetPath({{kTriggerSpecs}});
+      msg = kDuplicateTriggerDataMsg;
+      break;
+    case SourceRegistrationError::kExcessiveTriggerData:
+      path = GetPath({{kTriggerData}});
+      msg = ExcessiveTriggerDataMsg();
+      break;
+    case SourceRegistrationError::kTriggerSpecExcessiveTriggerData:
+      path = GetPath({{kTriggerSpecs}});
+      msg = ExcessiveTriggerDataMsg();
+      break;
+    case SourceRegistrationError::kInvalidTriggerDataForMatchingMode:
+      path = GetPath({{kTriggerDataMatching}});
+      msg = base::StrCat(
+          {"trigger_data must form a contiguous sequence of integers starting "
+           "at 0 for ",
+           kTriggerDataMatchingModulus});
+      break;
+    case SourceRegistrationError::kTopLevelTriggerDataAndTriggerSpecs:
+      path = base::Value(base::Value::List());
+      msg = MutuallyExclusiveMsg({{kTriggerData, kTriggerSpecs}});
+      break;
+    case SourceRegistrationError::kSummaryWindowOperatorValueInvalid:
+      path = GetPath({{kTriggerSpecs, kIndex, kSummaryWindowOperator}});
+      msg = EnumMsg(
+          {{kSummaryWindowOperatorCount, kSummaryWindowOperatorValueSum}});
+      break;
+    case SourceRegistrationError::kSummaryBucketsListInvalid:
+      path = GetPath({{kTriggerSpecs, kIndex, kSummaryBuckets}});
+      msg = ListLengthMsg(1, "max_event_level_reports");
+      break;
+    case SourceRegistrationError::kSummaryBucketsValueInvalid:
+      path = GetPath({{kTriggerSpecs, kIndex, kSummaryBuckets, kIndex}});
+      msg = base::StrCat(
+          {"must be a non-negative integer and greater than previous value and "
+           "less than or equal to ",
+           base::NumberToString(std::numeric_limits<uint32_t>::max())});
+      break;
+    case SourceRegistrationError::kEventLevelEpsilonValueInvalid:
+      path = GetPath({{kEventLevelEpsilon}});
+      msg = base::StrCat({"must be a number in the range [0, ",
+                          base::NumberToString(EventLevelEpsilon::max()), "]"});
+      break;
+  }
+
+  return SerializeErrorDetails(std::move(path), std::move(msg));
+}
+
+base::Value ErrorDetails(TriggerRegistrationError error) {
+  static constexpr char kDictionaryOrListOfDictionariesMsg[] =
+      "must be a dictionary or a list of dictionaries";
+  static constexpr char kFiltersReservedKeysMsg[] =
+      R"(strings starting with "_" are reserved keys)";
+  static constexpr char kFiltersValueInvalidMsg[] = "must be a list of strings";
+
+  base::Value path;
+  std::string msg;
+
+  switch (error) {
+    case TriggerRegistrationError::kInvalidJson:
+      msg = kInvalidJsonMsg;
+      break;
+    case TriggerRegistrationError::kRootWrongType:
+      path = base::Value(base::Value::List());
+      msg = kDictionaryMsg;
+      break;
+    case TriggerRegistrationError::kFiltersWrongType:
+      path = GetPath({{kFilters}});
+      msg = kDictionaryOrListOfDictionariesMsg;
+      break;
+    case TriggerRegistrationError::kFiltersValueInvalid:
+      path = GetPath({{kFilters, kIndex}});
+      msg = kFiltersValueInvalidMsg;
+      break;
+    case TriggerRegistrationError::kFiltersLookbackWindowValueInvalid:
+      path = GetPath({{kFilters, FilterConfig::kLookbackWindowKey}});
+      msg = kPositiveIntegerMsg;
+      break;
+    case TriggerRegistrationError::kFiltersUsingReservedKey:
+      path = GetPath({{kFilters}});
+      msg = kFiltersReservedKeysMsg;
+      break;
+    case TriggerRegistrationError::kFiltersListValueInvalid:
+      path = GetPath({{kFilters, kIndex, kIndex}});
+      msg = kFiltersValueInvalidMsg;
+      break;
+    case TriggerRegistrationError::kFiltersListLookbackWindowValueInvalid:
+      path = GetPath({{kFilters, kIndex, FilterConfig::kLookbackWindowKey}});
+      msg = kPositiveIntegerMsg;
+      break;
+    case TriggerRegistrationError::kFiltersListUsingReservedKey:
+      path = GetPath({{kFilters, kIndex}});
+      msg = kFiltersReservedKeysMsg;
+      break;
+    case TriggerRegistrationError::kAggregatableValuesWrongType:
+      path = GetPath({{kAggregatableValues}});
+      msg = kDictionaryOrListOfDictionariesMsg;
+      break;
+    case TriggerRegistrationError::kAggregatableValuesKeyTooLong:
+      path = GetPath({{kAggregatableValues}});
+      msg = AggregationKeyTooLongMsg();
+      break;
+    case TriggerRegistrationError::kAggregatableValuesListKeyTooLong:
+      path = GetPath({{kAggregatableValues, kIndex, kValues}});
+      msg = AggregationKeyTooLongMsg();
+      break;
+    case TriggerRegistrationError::kAggregatableValuesValueInvalid:
+      path = GetPath({{kAggregatableValues, kIndex}});
+      msg = AggregatableValueMsg();
+      break;
+    case TriggerRegistrationError::kAggregatableValuesListValueInvalid:
+      path = GetPath({{kAggregatableValues, kIndex, kValues, kIndex}});
+      msg = AggregatableValueMsg();
+      break;
+    case TriggerRegistrationError::kAggregatableValuesListValuesFieldMissing:
+      path = GetPath({{kAggregatableValues, kIndex, kValues}});
+      msg = kRequiredMsg;
+      break;
+    case TriggerRegistrationError::kAggregatableTriggerDataWrongType:
+      path = GetPath({{kAggregatableTriggerData}});
+      msg = kListOfDictionariesMsg;
+      break;
+    case TriggerRegistrationError::kAggregatableTriggerDataKeyPieceMissing:
+      path = GetPath({{kAggregatableTriggerData, kIndex, kKeyPiece}});
+      msg = kRequiredMsg;
+      break;
+    case TriggerRegistrationError::kAggregatableTriggerDataKeyPieceInvalid:
+      path = GetPath({{kAggregatableTriggerData, kIndex, kKeyPiece}});
+      msg = kAggregationKeyPieceMsg;
+      break;
+    case TriggerRegistrationError::kAggregatableTriggerDataSourceKeysInvalid:
+      path = GetPath({{kAggregatableTriggerData, kIndex, kSourceKeys}});
+      msg = base::StrCat(
+          {"must be a list of strings, each with length less than or equal to ",
+           base::NumberToString(kMaxBytesPerAggregationKeyId)});
+      break;
+    case TriggerRegistrationError::kEventTriggerDataWrongType:
+      path = GetPath({{kEventTriggerData}});
+      msg = kListOfDictionariesMsg;
+      break;
+    case TriggerRegistrationError::kEventTriggerDataValueInvalid:
+      path = GetPath({{kEventTriggerData, kIndex, kTriggerData}});
+      msg = base::StrCat({kBase10EncodedMsg, "uint64"});
+      break;
+    case TriggerRegistrationError::kEventPriorityValueInvalid:
+      path = GetPath({{kEventTriggerData, kIndex, kPriority}});
+      msg = base::StrCat({kBase10EncodedMsg, "int64"});
+      break;
+    case TriggerRegistrationError::kEventDedupKeyValueInvalid:
+      path = GetPath({{kEventTriggerData, kIndex, kDeduplicationKey}});
+      msg = base::StrCat({kBase10EncodedMsg, "uint64"});
+      break;
+    case TriggerRegistrationError::kAggregationCoordinatorValueInvalid:
+      path = GetPath({{kAggregationCoordinatorOrigin}});
+      msg =
+          "must be a potentially trustworthy URL on the allowlist that uses "
+          "HTTP/HTTPS";
+      break;
+    case TriggerRegistrationError::kAggregatableDedupKeyWrongType:
+      path = GetPath({{kAggregatableDeduplicationKeys}});
+      msg = kListOfDictionariesMsg;
+      break;
+    case TriggerRegistrationError::kAggregatableDedupKeyValueInvalid:
+      path = GetPath(
+          {{kAggregatableDeduplicationKeys, kIndex, kDeduplicationKey}});
+      msg = base::StrCat({kBase10EncodedMsg, "uint64"});
+      break;
+    case TriggerRegistrationError::
+        kAggregatableSourceRegistrationTimeValueInvalid:
+      path = GetPath({{kAggregatableSourceRegistrationTime}});
+      msg = EnumMsg(
+          {{kSourceRegistrationTimeInclude, kSourceRegistrationTimeExclude}});
+      break;
+    case TriggerRegistrationError::kTriggerContextIdInvalidValue:
+      path = GetPath({{kTriggerContextId}});
+      msg = base::StrCat(
+          {"must be a non-empty string with length less than or equal to ",
+           base::NumberToString(kMaxTriggerContextIdLength)});
+      break;
+    case TriggerRegistrationError::
+        kTriggerContextIdInvalidSourceRegistrationTimeConfig:
+      path = GetPath({{kTriggerContextId}});
+      msg = base::StrCat({"is prohibited for ",
+                          kAggregatableSourceRegistrationTime, " ",
+                          kSourceRegistrationTimeInclude});
+      break;
+    case TriggerRegistrationError::kEventValueInvalid:
+      path = GetPath({{kEventTriggerData, kIndex, kValue}});
+      msg = base::StrCat(
+          {kPositiveIntegerMsg, " in the range [1, ",
+           base::NumberToString(std::numeric_limits<uint32_t>::max()), "]"});
+      break;
+  }
+
+  return SerializeErrorDetails(std::move(path), std::move(msg));
+}
+
+base::Value ErrorDetails(OsSourceRegistrationError error) {
+  return ErrorDetails(*error);
+}
+
+base::Value ErrorDetails(OsTriggerRegistrationError error) {
+  return ErrorDetails(*error);
+}
+
+}  // namespace attribution_reporting
diff --git a/components/attribution_reporting/registration_header_error.h b/components/attribution_reporting/registration_header_error.h
index 48436da..d59891fd 100644
--- a/components/attribution_reporting/registration_header_error.h
+++ b/components/attribution_reporting/registration_header_error.h
@@ -9,10 +9,26 @@
 #include <string_view>
 
 #include "base/component_export.h"
+#include "base/types/strong_alias.h"
+#include "components/attribution_reporting/os_registration_error.mojom-forward.h"
 #include "components/attribution_reporting/registration_header_type.mojom-forward.h"
+#include "components/attribution_reporting/source_registration_error.mojom-forward.h"
+#include "components/attribution_reporting/trigger_registration_error.mojom-forward.h"
+
+namespace base {
+class Value;
+}  // namespace base
 
 namespace attribution_reporting {
 
+using OsSourceRegistrationError =
+    base::StrongAlias<struct OsSourceRegistrationErrorTag,
+                      mojom::OsRegistrationError>;
+
+using OsTriggerRegistrationError =
+    base::StrongAlias<struct OsTriggerRegistrationErrorTag,
+                      mojom::OsRegistrationError>;
+
 struct RegistrationHeaderError {
   mojom::RegistrationHeaderType header_type;
   std::string header_value;
@@ -26,6 +42,18 @@
       : header_type(header_type), header_value(header_value) {}
 };
 
+COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
+base::Value ErrorDetails(mojom::SourceRegistrationError);
+
+COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
+base::Value ErrorDetails(mojom::TriggerRegistrationError);
+
+COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
+base::Value ErrorDetails(OsSourceRegistrationError);
+
+COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
+base::Value ErrorDetails(OsTriggerRegistrationError);
+
 }  // namespace attribution_reporting
 
 #endif  // COMPONENTS_ATTRIBUTION_REPORTING_REGISTRATION_HEADER_ERROR_H_
diff --git a/components/attribution_reporting/registration_header_error_unittest.cc b/components/attribution_reporting/registration_header_error_unittest.cc
new file mode 100644
index 0000000..f63a3e7
--- /dev/null
+++ b/components/attribution_reporting/registration_header_error_unittest.cc
@@ -0,0 +1,576 @@
+// 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/attribution_reporting/registration_header_error.h"
+
+#include "base/test/values_test_util.h"
+#include "components/attribution_reporting/os_registration_error.mojom-shared.h"
+#include "components/attribution_reporting/source_registration_error.mojom-shared.h"
+#include "components/attribution_reporting/trigger_registration_error.mojom-shared.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace attribution_reporting {
+namespace {
+
+using ::attribution_reporting::mojom::OsRegistrationError;
+using ::attribution_reporting::mojom::SourceRegistrationError;
+using ::attribution_reporting::mojom::TriggerRegistrationError;
+
+TEST(RegistrationHeaderErrorTest, SourceRegistrationErrorDetails) {
+  const struct {
+    SourceRegistrationError error;
+    const char* expected_json;
+  } kTestCases[] = {
+      {
+          SourceRegistrationError::kInvalidJson,
+          R"json({
+            "msg": "invalid JSON"
+          })json",
+      },
+      {
+          SourceRegistrationError::kRootWrongType,
+          R"json({
+            "path": [],
+            "msg": "must be a dictionary"
+          })json",
+      },
+      {
+          SourceRegistrationError::kDestinationMissing,
+          R"json({
+            "path": ["destination"],
+            "msg": "required"
+          })json",
+      },
+      {
+          SourceRegistrationError::kDestinationWrongType,
+          R"json({
+            "path": ["destination"],
+            "msg": "must be a string or a list of 1-3 strings"
+          })json",
+      },
+      {
+          SourceRegistrationError::kDestinationUntrustworthy,
+          R"json({
+            "path": ["destination"],
+            "msg": "must be a potentially trustworthy URL that uses HTTP/HTTPS"
+          })json",
+      },
+      {
+          SourceRegistrationError::kDestinationListUntrustworthy,
+          R"json({
+            "path": ["destination", "*"],
+            "msg": "must be a potentially trustworthy URL that uses HTTP/HTTPS"
+          })json",
+      },
+      {
+          SourceRegistrationError::kFilterDataKeyTooLong,
+          R"json({
+            "path": ["filter_data"],
+            "msg": "key length must be less than or equal to 25"
+          })json",
+      },
+      {
+          SourceRegistrationError::kFilterDataKeyReserved,
+          R"json({
+            "path": ["filter_data"],
+            "msg": "\"source_type\" and keys starting with \"_\" are reserved"
+          })json",
+      },
+      {
+          SourceRegistrationError::kFilterDataDictInvalid,
+          R"json({
+            "path": ["filter_data"],
+            "msg": "must be a dictionary with size less than or equal to 50"
+          })json",
+      },
+      {
+          SourceRegistrationError::kFilterDataListInvalid,
+          R"json({
+            "path": ["filter_data", "*"],
+            "msg": "must be a list whose length is in the range [0, 50]"
+          })json",
+      },
+      {
+          SourceRegistrationError::kFilterDataListValueInvalid,
+          R"json({
+            "path": ["filter_data", "*", "*"],
+            "msg": "must be a string with length less than or equal to 25"
+          })json",
+      },
+      {
+          SourceRegistrationError::kAggregationKeysKeyTooLong,
+          R"json({
+            "path": ["aggregation_keys"],
+            "msg": "key length must be less than or equal to 25"
+          })json",
+      },
+      {
+          SourceRegistrationError::kAggregationKeysDictInvalid,
+          R"json({
+            "path": ["aggregation_keys"],
+            "msg": "must be a dictionary with size less than or equal to 20",
+          })json",
+      },
+      {
+          SourceRegistrationError::kAggregationKeysValueInvalid,
+          R"json({
+            "path": ["aggregation_keys", "*"],
+            "msg": "must be a base16-encoded string of a uint128 with a \"0x\" prefix"
+          })json",
+      },
+      {
+          SourceRegistrationError::kSourceEventIdValueInvalid,
+          R"json({
+            "path": ["source_event_id"],
+            "msg": "must be a base10-encoded string of a uint64"
+          })json",
+      },
+      {
+          SourceRegistrationError::kPriorityValueInvalid,
+          R"json({
+            "path": ["priority"],
+            "msg": "must be a base10-encoded string of a int64"
+          })json",
+      },
+      {
+          SourceRegistrationError::kExpiryValueInvalid,
+          R"json({
+            "path": ["expiry"],
+            "msg": "must be a non-negative integer or a base10-encoded string of a uint64"
+          })json",
+      },
+      {
+          SourceRegistrationError::kEventReportWindowValueInvalid,
+          R"json({
+            "path": ["event_report_window"],
+            "msg": "must be a non-negative integer or a base10-encoded string of a uint64"
+          })json",
+      },
+      {
+          SourceRegistrationError::kAggregatableReportWindowValueInvalid,
+          R"json({
+            "path": ["aggregatable_report_window"],
+            "msg": "must be a non-negative integer or a base10-encoded string of a uint64"
+          })json",
+      },
+      {
+          SourceRegistrationError::kMaxEventLevelReportsValueInvalid,
+          R"json({
+            "path": ["max_event_level_reports"],
+            "msg": "must be a non-negative integer and less than or equal to 20"
+          })json",
+      },
+      {
+          SourceRegistrationError::kEventReportWindowsWrongType,
+          R"json({
+            "path": ["event_report_windows"],
+            "msg": "must be a dictionary"
+          })json",
+      },
+      {
+          SourceRegistrationError::kEventReportWindowsEndTimesMissing,
+          R"json({
+            "path": ["event_report_windows", "end_times"],
+            "msg": "required"
+          })json",
+      },
+      {
+          SourceRegistrationError::kEventReportWindowsEndTimeDurationLTEStart,
+          R"json({
+            "path": ["event_report_windows", "end_times", "*"],
+            "msg": "must be greater than start_time and greater than previous end_time"
+          })json",
+      },
+      {
+          SourceRegistrationError::kBothEventReportWindowFieldsFound,
+          R"json({
+            "path": [],
+            "msg": "mutually exclusive fields: event_report_window, event_report_windows"
+          })json",
+      },
+      {
+          SourceRegistrationError::kEventReportWindowsEndTimesListInvalid,
+          R"json({
+            "path": ["event_report_windows", "end_times"],
+            "msg": "must be a list whose length is in the range [1, 5]"
+          })json",
+      },
+      {
+          SourceRegistrationError::kEventReportWindowsStartTimeInvalid,
+          R"json({
+            "path": ["event_report_windows", "start_time"],
+            "msg": "must be a non-negative integer and less than or equal to expiry"
+          })json",
+      },
+      {
+          SourceRegistrationError::kEventReportWindowsEndTimeValueInvalid,
+          R"json({
+            "path": ["event_report_windows", "end_times"],
+            "msg": "must be a positive integer"
+          })json",
+      },
+      {
+          SourceRegistrationError::kTriggerDataMatchingValueInvalid,
+          R"json({
+            "path": ["trigger_data_matching"],
+            "msg": "must be one of the following (case-sensitive): exact, modulus"
+          })json",
+      },
+      {
+          SourceRegistrationError::kTriggerSpecsWrongType,
+          R"json({
+            "path": ["trigger_specs"],
+            "msg": "must be a list of dictionaries"
+          })json",
+      },
+      {
+          SourceRegistrationError::kTriggerSpecTriggerDataMissing,
+          R"json({
+            "path": ["trigger_specs", "*", "trigger_data"],
+            "msg": "required"
+          })json",
+      },
+      {
+          SourceRegistrationError::kTriggerDataListInvalid,
+          R"json({
+            "path": ["trigger_data"],
+            "msg": "must be a list of non-negative integers in the range [0, 4294967295]"
+          })json",
+      },
+      {
+          SourceRegistrationError::kTriggerSpecTriggerDataListInvalid,
+          R"json({
+            "path": ["trigger_specs", "*", "trigger_data"],
+            "msg": "must be a list of non-negative integers in the range [0, 4294967295]"
+          })json",
+      },
+      {
+          SourceRegistrationError::kDuplicateTriggerData,
+          R"json({
+            "path": ["trigger_data"],
+            "msg": "must not contain duplicate trigger_data"
+          })json",
+      },
+      {
+          SourceRegistrationError::kTriggerSpecDuplicateTriggerData,
+          R"json({
+            "path": ["trigger_specs"],
+            "msg": "must not contain duplicate trigger_data"
+          })json",
+      },
+      {
+          SourceRegistrationError::kExcessiveTriggerData,
+          R"json({
+            "path": ["trigger_data"],
+            "msg": "must not exceed a maximum of 32 distinct trigger_data values"
+          })json",
+      },
+      {
+          SourceRegistrationError::kTriggerSpecExcessiveTriggerData,
+          R"json({
+            "path": ["trigger_specs"],
+            "msg": "must not exceed a maximum of 32 distinct trigger_data values"
+          })json",
+      },
+      {
+          SourceRegistrationError::kInvalidTriggerDataForMatchingMode,
+          R"json({
+            "path": ["trigger_data_matching"],
+            "msg": "trigger_data must form a contiguous sequence of integers starting at 0 for modulus"
+          })json",
+      },
+      {
+          SourceRegistrationError::kTopLevelTriggerDataAndTriggerSpecs,
+          R"json({
+            "path": [],
+            "msg": "mutually exclusive fields: trigger_data, trigger_specs"
+          })json",
+      },
+      {
+          SourceRegistrationError::kSummaryWindowOperatorValueInvalid,
+          R"json({
+            "path": ["trigger_specs", "*", "summary_window_operator"],
+            "msg": "must be one of the following (case-sensitive): count, value_sum"
+          })json",
+      },
+      {
+          SourceRegistrationError::kSummaryBucketsListInvalid,
+          R"json({
+            "path": ["trigger_specs", "*", "summary_buckets"],
+            "msg": "must be a list whose length is in the range [1, max_event_level_reports]"
+          })json",
+      },
+      {
+          SourceRegistrationError::kSummaryBucketsValueInvalid,
+          R"json({
+            "path": ["trigger_specs", "*", "summary_buckets", "*"],
+            "msg": "must be a non-negative integer and greater than previous value and less than or equal to 4294967295"
+          })json",
+      },
+      {
+          SourceRegistrationError::kEventLevelEpsilonValueInvalid,
+          R"json({
+            "path": ["event_level_epsilon"],
+            "msg": "must be a number in the range [0, 14]"
+          })json",
+      },
+  };
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.error);
+    EXPECT_THAT(ErrorDetails(test_case.error),
+                base::test::IsJson(test_case.expected_json));
+  }
+}
+
+TEST(RegistrationHeaderErrorTest, TriggerRegistrationErrorDetails) {
+  const struct {
+    TriggerRegistrationError error;
+    const char* expected_json;
+  } kTestCases[] = {
+      {
+          TriggerRegistrationError::kInvalidJson,
+          R"json({
+            "msg": "invalid JSON"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kRootWrongType,
+          R"json({
+            "path": [],
+            "msg": "must be a dictionary"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kFiltersWrongType,
+          R"json({
+            "path": ["filters"],
+            "msg": "must be a dictionary or a list of dictionaries"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kFiltersValueInvalid,
+          R"json({
+            "path": ["filters", "*"],
+            "msg": "must be a list of strings"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kFiltersLookbackWindowValueInvalid,
+          R"json({
+            "path": ["filters", "_lookback_window"],
+            "msg": "must be a positive integer"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kFiltersUsingReservedKey,
+          R"json({
+            "path": ["filters"],
+            "msg": "strings starting with \"_\" are reserved keys"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kFiltersListValueInvalid,
+          R"json({
+            "path": ["filters", "*", "*"],
+            "msg": "must be a list of strings"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kFiltersListLookbackWindowValueInvalid,
+          R"json({
+            "path": ["filters", "*", "_lookback_window"],
+            "msg": "must be a positive integer"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kFiltersListUsingReservedKey,
+          R"json({
+            "path": ["filters", "*"],
+            "msg": "strings starting with \"_\" are reserved keys"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableValuesWrongType,
+          R"json({
+            "path": ["aggregatable_values"],
+            "msg": "must be a dictionary or a list of dictionaries"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableValuesKeyTooLong,
+          R"json({
+            "path": ["aggregatable_values"],
+            "msg": "key length must be less than or equal to 25"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableValuesListValuesFieldMissing,
+          R"json({
+            "path": ["aggregatable_values", "*", "values"],
+            "msg": "required"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableValuesListKeyTooLong,
+          R"json({
+            "path": ["aggregatable_values", "*", "values"],
+            "msg": "key length must be less than or equal to 25"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableValuesValueInvalid,
+          R"json({
+            "path": ["aggregatable_values", "*"],
+            "msg": "must be an integer in the range [1, 65536]"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableValuesListValueInvalid,
+          R"json({
+            "path": ["aggregatable_values", "*", "values", "*"],
+            "msg": "must be an integer in the range [1, 65536]"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableTriggerDataWrongType,
+          R"json({
+            "path": ["aggregatable_trigger_data"],
+            "msg": "must be a list of dictionaries"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableTriggerDataKeyPieceMissing,
+          R"json({
+            "path": ["aggregatable_trigger_data", "*", "key_piece"],
+            "msg": "required"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableTriggerDataSourceKeysInvalid,
+          R"json({
+            "path": ["aggregatable_trigger_data", "*", "source_keys"],
+            "msg": "must be a list of strings, each with length less than or equal to 25"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableTriggerDataKeyPieceInvalid,
+          R"json({
+            "path": ["aggregatable_trigger_data", "*", "key_piece"],
+            "msg": "must be a base16-encoded string of a uint128 with a \"0x\" prefix"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kEventTriggerDataWrongType,
+          R"json({
+            "path": ["event_trigger_data"],
+            "msg": "must be a list of dictionaries"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kEventTriggerDataValueInvalid,
+          R"json({
+            "path": ["event_trigger_data", "*", "trigger_data"],
+            "msg": "must be a base10-encoded string of a uint64"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kEventPriorityValueInvalid,
+          R"json({
+            "path": ["event_trigger_data", "*", "priority"],
+            "msg": "must be a base10-encoded string of a int64"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kEventDedupKeyValueInvalid,
+          R"json({
+            "path": ["event_trigger_data", "*", "deduplication_key"],
+            "msg": "must be a base10-encoded string of a uint64"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregationCoordinatorValueInvalid,
+          R"json({
+            "path": ["aggregation_coordinator_origin"],
+            "msg": "must be a potentially trustworthy URL on the allowlist that uses HTTP/HTTPS"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableDedupKeyValueInvalid,
+          R"json({
+            "path": ["aggregatable_deduplication_keys", "*", "deduplication_key"],
+            "msg": "must be a base10-encoded string of a uint64"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kAggregatableDedupKeyWrongType,
+          R"json({
+            "path": ["aggregatable_deduplication_keys"],
+            "msg": "must be a list of dictionaries"
+          })json",
+      },
+      {
+          TriggerRegistrationError::
+              kAggregatableSourceRegistrationTimeValueInvalid,
+          R"json({
+            "path": ["aggregatable_source_registration_time"],
+            "msg": "must be one of the following (case-sensitive): include, exclude"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kTriggerContextIdInvalidValue,
+          R"json({
+            "path": ["trigger_context_id"],
+            "msg": "must be a non-empty string with length less than or equal to 64"
+          })json",
+      },
+      {
+          TriggerRegistrationError::
+              kTriggerContextIdInvalidSourceRegistrationTimeConfig,
+          R"json({
+            "path": ["trigger_context_id"],
+            "msg": "is prohibited for aggregatable_source_registration_time include"
+          })json",
+      },
+      {
+          TriggerRegistrationError::kEventValueInvalid,
+          R"json({
+            "path": ["event_trigger_data", "*", "value"],
+            "msg": "must be a positive integer in the range [1, 4294967295]"
+          })json",
+      },
+  };
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.error);
+    EXPECT_THAT(ErrorDetails(test_case.error),
+                base::test::IsJson(test_case.expected_json));
+  }
+}
+
+TEST(RegistrationHeaderErrorTest, OsRegistrationError) {
+  const struct {
+    OsRegistrationError error;
+    const char* expected_json;
+  } kTestCases[] = {
+      {
+          OsRegistrationError::kInvalidList,
+          R"json({
+            "msg": "must be a list of URLs"
+          })json",
+      },
+  };
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.error);
+    EXPECT_THAT(ErrorDetails(OsSourceRegistrationError(test_case.error)),
+                base::test::IsJson(test_case.expected_json));
+    EXPECT_THAT(ErrorDetails(OsTriggerRegistrationError(test_case.error)),
+                base::test::IsJson(test_case.expected_json));
+  }
+}
+
+}  // namespace
+}  // namespace attribution_reporting
diff --git a/components/attribution_reporting/source_registration.cc b/components/attribution_reporting/source_registration.cc
index ab3ed72b..956d48c 100644
--- a/components/attribution_reporting/source_registration.cc
+++ b/components/attribution_reporting/source_registration.cc
@@ -39,13 +39,6 @@
 using ::attribution_reporting::mojom::SourceRegistrationError;
 using ::attribution_reporting::mojom::SourceType;
 
-constexpr char kAggregatableReportWindow[] = "aggregatable_report_window";
-constexpr char kAggregationKeys[] = "aggregation_keys";
-constexpr char kDestination[] = "destination";
-constexpr char kExpiry[] = "expiry";
-constexpr char kFilterData[] = "filter_data";
-constexpr char kSourceEventId[] = "source_event_id";
-
 base::TimeDelta AdjustExpiry(base::TimeDelta expiry, SourceType source_type) {
   switch (source_type) {
     case SourceType::kNavigation:
@@ -61,7 +54,7 @@
   static_assert(SourceRegistrationError::kMaxValue ==
                     SourceRegistrationError::kEventLevelEpsilonValueInvalid,
                 "Update ConversionSourceRegistrationError enum.");
-  base::UmaHistogramEnumeration("Conversions.SourceRegistrationError12", error);
+  base::UmaHistogramEnumeration("Conversions.SourceRegistrationError13", error);
 }
 
 SourceRegistration::SourceRegistration(mojo::DefaultConstruct::Tag tag)
diff --git a/components/attribution_reporting/source_registration_error.mojom b/components/attribution_reporting/source_registration_error.mojom
index 7a94493..cc25a6f9 100644
--- a/components/attribution_reporting/source_registration_error.mojom
+++ b/components/attribution_reporting/source_registration_error.mojom
@@ -13,7 +13,7 @@
   kDestinationMissing = 2,
   kDestinationWrongType = 3,
   kDestinationUntrustworthy = 4,
-  kDestinationListTooLong = 5,
+  kDestinationListUntrustworthy = 5,
 
   kFilterDataKeyTooLong = 6,
   kFilterDataKeyReserved = 7,
@@ -43,20 +43,22 @@
   kTriggerDataMatchingValueInvalid = 27,
 
   kTriggerSpecsWrongType = 28,
-  kTriggerSpecWrongType = 29,
-  kTriggerSpecTriggerDataMissing = 30,
-  kTriggerSpecTriggerDataListInvalid = 31,
-  kTriggerSpecTriggerDataValueInvalid= 32,
+  kTriggerSpecTriggerDataMissing = 29,
+  kTriggerSpecTriggerDataListInvalid = 30,
+  kTriggerDataListInvalid = 31,
+  kDuplicateTriggerData = 32,
+  kTriggerSpecDuplicateTriggerData = 33,
 
-  kExcessiveTriggerData = 33,
-  kInvalidTriggerDataForMatchingMode = 34,
+  kExcessiveTriggerData = 34,
+  kTriggerSpecExcessiveTriggerData = 35,
+  kInvalidTriggerDataForMatchingMode = 36,
 
-  kTopLevelTriggerDataAndTriggerSpecs = 35,
+  kTopLevelTriggerDataAndTriggerSpecs = 37,
 
-  kSummaryWindowOperatorValueInvalid = 36,
+  kSummaryWindowOperatorValueInvalid = 38,
 
-  kSummaryBucketsListInvalid = 37,
-  kSummaryBucketsValueInvalid = 38,
+  kSummaryBucketsListInvalid = 39,
+  kSummaryBucketsValueInvalid = 40,
 
-  kEventLevelEpsilonValueInvalid = 39,
+  kEventLevelEpsilonValueInvalid = 41,
 };
diff --git a/components/attribution_reporting/source_registration_unittest.cc b/components/attribution_reporting/source_registration_unittest.cc
index 38aac3f..fd2073d 100644
--- a/components/attribution_reporting/source_registration_unittest.cc
+++ b/components/attribution_reporting/source_registration_unittest.cc
@@ -343,7 +343,7 @@
   };
 
   static constexpr char kSourceRegistrationErrorMetric[] =
-      "Conversions.SourceRegistrationError12";
+      "Conversions.SourceRegistrationError13";
 
   for (const auto& test_case : kTestCases) {
     SCOPED_TRACE(test_case.desc);
diff --git a/components/attribution_reporting/summary_buckets.cc b/components/attribution_reporting/summary_buckets.cc
index dc19c5c..6de1e7d 100644
--- a/components/attribution_reporting/summary_buckets.cc
+++ b/components/attribution_reporting/summary_buckets.cc
@@ -17,6 +17,7 @@
 #include "base/types/expected.h"
 #include "base/types/expected_macros.h"
 #include "base/values.h"
+#include "components/attribution_reporting/constants.h"
 #include "components/attribution_reporting/max_event_level_reports.h"
 #include "components/attribution_reporting/parsing_utils.h"
 #include "components/attribution_reporting/source_registration_error.mojom.h"
@@ -29,12 +30,6 @@
 using ::attribution_reporting::mojom::SourceRegistrationError;
 using ::attribution_reporting::mojom::SummaryWindowOperator;
 
-constexpr char kSummaryBuckets[] = "summary_buckets";
-constexpr char kSummaryWindowOperator[] = "summary_window_operator";
-
-constexpr char kSummaryWindowOperatorCount[] = "count";
-constexpr char kSummaryWindowOperatorValueSum[] = "value_sum";
-
 bool AreSummaryBucketsValid(const base::flat_set<uint32_t>& starts) {
   return !starts.empty() &&
          base::MakeStrictNum(starts.size()) <=
diff --git a/components/attribution_reporting/trigger_config.cc b/components/attribution_reporting/trigger_config.cc
index daf47e4f..9a1dd07 100644
--- a/components/attribution_reporting/trigger_config.cc
+++ b/components/attribution_reporting/trigger_config.cc
@@ -19,6 +19,7 @@
 #include "base/types/expected.h"
 #include "base/types/expected_macros.h"
 #include "base/values.h"
+#include "components/attribution_reporting/constants.h"
 #include "components/attribution_reporting/parsing_utils.h"
 #include "components/attribution_reporting/source_registration_error.mojom.h"
 #include "components/attribution_reporting/source_type.mojom.h"
@@ -32,16 +33,6 @@
 using ::attribution_reporting::mojom::SourceType;
 using ::attribution_reporting::mojom::TriggerDataMatching;
 
-constexpr char kTriggerData[] = "trigger_data";
-constexpr char kTriggerDataMatching[] = "trigger_data_matching";
-constexpr char kTriggerSpecs[] = "trigger_specs";
-
-constexpr char kTriggerDataMatchingExact[] = "exact";
-constexpr char kTriggerDataMatchingModulus[] = "modulus";
-
-// https://wicg.github.io/attribution-reporting-api/#max-distinct-trigger-data-per-source
-constexpr uint8_t kMaxTriggerDataPerSource = 32;
-
 constexpr uint32_t DefaultTriggerDataCardinality(SourceType source_type) {
   switch (source_type) {
     case SourceType::kNavigation:
@@ -55,35 +46,34 @@
     const base::Value& value,
     TriggerSpecs::TriggerDataIndices& trigger_data_indices,
     const uint8_t spec_index,
-    const bool allow_empty) {
+    const bool allow_empty,
+    SourceRegistrationError list_error,
+    SourceRegistrationError duplicate_error,
+    SourceRegistrationError excessive_error) {
   const base::Value::List* list = value.GetIfList();
   if (!list) {
-    return base::unexpected(
-        SourceRegistrationError::kTriggerSpecTriggerDataListInvalid);
+    return base::unexpected(list_error);
   }
 
   if (!allow_empty && list->empty()) {
-    return base::unexpected(
-        SourceRegistrationError::kTriggerSpecTriggerDataListInvalid);
+    return base::unexpected(list_error);
   }
 
   const size_t new_size = list->size() + trigger_data_indices.size();
   if (new_size > kMaxTriggerDataPerSource) {
-    return base::unexpected(SourceRegistrationError::kExcessiveTriggerData);
+    return base::unexpected(excessive_error);
   }
 
   trigger_data_indices.reserve(new_size);
 
   for (const base::Value& item : *list) {
-    ASSIGN_OR_RETURN(uint32_t trigger_data, ParseUint32(item), [](ParseError) {
-      return SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid;
-    });
+    ASSIGN_OR_RETURN(uint32_t trigger_data, ParseUint32(item),
+                     [&](ParseError) { return list_error; });
 
     auto [_, inserted] =
         trigger_data_indices.try_emplace(trigger_data, spec_index);
     if (!inserted) {
-      return base::unexpected(
-          SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid);
+      return base::unexpected(duplicate_error);
     }
   }
 
@@ -219,7 +209,8 @@
   }
 
   if (list->size() > kMaxTriggerDataPerSource) {
-    return base::unexpected(SourceRegistrationError::kExcessiveTriggerData);
+    return base::unexpected(
+        SourceRegistrationError::kTriggerSpecExcessiveTriggerData);
   }
 
   TriggerDataIndices trigger_data_indices;
@@ -230,7 +221,7 @@
   for (const base::Value& item : *list) {
     const base::Value::Dict* dict = item.GetIfDict();
     if (!dict) {
-      return base::unexpected(SourceRegistrationError::kTriggerSpecWrongType);
+      return base::unexpected(SourceRegistrationError::kTriggerSpecsWrongType);
     }
 
     const base::Value* trigger_data = dict->Find(kTriggerData);
@@ -242,7 +233,10 @@
     RETURN_IF_ERROR(ParseTriggerData(
         *trigger_data, trigger_data_indices,
         /*spec_index=*/base::checked_cast<uint8_t>(specs.size()),
-        /*allow_empty=*/false));
+        /*allow_empty=*/false,
+        SourceRegistrationError::kTriggerSpecTriggerDataListInvalid,
+        SourceRegistrationError::kTriggerSpecDuplicateTriggerData,
+        SourceRegistrationError::kTriggerSpecExcessiveTriggerData));
 
     ASSIGN_OR_RETURN(auto event_report_windows,
                      EventReportWindows::ParseWindows(*dict, expiry,
@@ -270,9 +264,12 @@
   }
 
   TriggerDataIndices trigger_data_indices;
-  RETURN_IF_ERROR(ParseTriggerData(*trigger_data, trigger_data_indices,
-                                   /*spec_index=*/0,
-                                   /*allow_empty=*/true));
+  RETURN_IF_ERROR(ParseTriggerData(
+      *trigger_data, trigger_data_indices,
+      /*spec_index=*/0,
+      /*allow_empty=*/true, SourceRegistrationError::kTriggerDataListInvalid,
+      SourceRegistrationError::kDuplicateTriggerData,
+      SourceRegistrationError::kExcessiveTriggerData));
 
   RETURN_IF_ERROR(ValidateSpecsForTriggerDataMatching(trigger_data_indices,
                                                       trigger_data_matching));
diff --git a/components/attribution_reporting/trigger_config_unittest.cc b/components/attribution_reporting/trigger_config_unittest.cc
index d25be2c..c1b0c10 100644
--- a/components/attribution_reporting/trigger_config_unittest.cc
+++ b/components/attribution_reporting/trigger_config_unittest.cc
@@ -194,15 +194,15 @@
             24, 25, 26, 27, 28, 29, 30, 31,
             32
           ]})json",
-          .matches_full_flex =
-              ErrorIs(SourceRegistrationError::kExcessiveTriggerData),
+          .matches_full_flex = ErrorIs(
+              SourceRegistrationError::kTriggerSpecExcessiveTriggerData),
           .matches_top_level_trigger_data = ValueIs(_),
       },
       {
           .desc = "spec_wrong_type",
           .json = R"json({"trigger_specs": [0]})json",
           .matches_full_flex =
-              ErrorIs(SourceRegistrationError::kTriggerSpecWrongType),
+              ErrorIs(SourceRegistrationError::kTriggerSpecsWrongType),
           .matches_top_level_trigger_data = ValueIs(_),
       },
       {
@@ -215,10 +215,10 @@
       {
           .desc = "trigger_data_wrong_type",
           .json = R"json({"trigger_data": 1})json",
-          .matches_full_flex = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataListInvalid),
-          .matches_top_level_trigger_data = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataListInvalid),
+          .matches_full_flex =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
+          .matches_top_level_trigger_data =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
       },
       {
           .desc = "spec_trigger_data_empty",
@@ -262,41 +262,41 @@
             ]},
             {"trigger_data": [32]}
           ]})json",
-          .matches_full_flex =
-              ErrorIs(SourceRegistrationError::kExcessiveTriggerData),
+          .matches_full_flex = ErrorIs(
+              SourceRegistrationError::kTriggerSpecExcessiveTriggerData),
           .matches_top_level_trigger_data = ValueIs(_),
       },
       {
           .desc = "trigger_data_value_wrong_type",
           .json = R"json({"trigger_data": ["1"]})json",
-          .matches_full_flex = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
-          .matches_top_level_trigger_data = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
+          .matches_full_flex =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
+          .matches_top_level_trigger_data =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
       },
       {
           .desc = "trigger_data_value_fractional",
           .json = R"json({"trigger_data": [1.5]})json",
-          .matches_full_flex = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
-          .matches_top_level_trigger_data = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
+          .matches_full_flex =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
+          .matches_top_level_trigger_data =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
       },
       {
           .desc = "trigger_data_value_negative",
           .json = R"json({"trigger_data": [-1]})json",
-          .matches_full_flex = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
-          .matches_top_level_trigger_data = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
+          .matches_full_flex =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
+          .matches_top_level_trigger_data =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
       },
       {
           .desc = "trigger_data_value_above_max",
           .json = R"json({"trigger_data": [4294967296]})json",
-          .matches_full_flex = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
-          .matches_top_level_trigger_data = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
+          .matches_full_flex =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
+          .matches_top_level_trigger_data =
+              ErrorIs(SourceRegistrationError::kTriggerDataListInvalid),
       },
       {
           .desc = "trigger_data_value_minimal",
@@ -318,10 +318,10 @@
       {
           .desc = "trigger_data_value_duplicate",
           .json = R"json({"trigger_data": [1, 3, 1, 2]})json",
-          .matches_full_flex = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
-          .matches_top_level_trigger_data = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
+          .matches_full_flex =
+              ErrorIs(SourceRegistrationError::kDuplicateTriggerData),
+          .matches_top_level_trigger_data =
+              ErrorIs(SourceRegistrationError::kDuplicateTriggerData),
       },
       {
           .desc = "trigger_data_value_duplicate_across_specs",
@@ -331,7 +331,7 @@
             {"trigger_data": [1, 5]},
           ]})json",
           .matches_full_flex = ErrorIs(
-              SourceRegistrationError::kTriggerSpecTriggerDataValueInvalid),
+              SourceRegistrationError::kTriggerSpecDuplicateTriggerData),
           .matches_top_level_trigger_data = ValueIs(_),
       },
       {
diff --git a/components/attribution_reporting/trigger_registration.cc b/components/attribution_reporting/trigger_registration.cc
index 4747313..3b578226 100644
--- a/components/attribution_reporting/trigger_registration.cc
+++ b/components/attribution_reporting/trigger_registration.cc
@@ -21,6 +21,7 @@
 #include "components/attribution_reporting/aggregatable_trigger_config.h"
 #include "components/attribution_reporting/aggregatable_trigger_data.h"
 #include "components/attribution_reporting/aggregatable_values.h"
+#include "components/attribution_reporting/constants.h"
 #include "components/attribution_reporting/event_trigger_data.h"
 #include "components/attribution_reporting/features.h"
 #include "components/attribution_reporting/filters.h"
@@ -34,14 +35,6 @@
 
 using ::attribution_reporting::mojom::TriggerRegistrationError;
 
-constexpr char kAggregationCoordinatorOrigin[] =
-    "aggregation_coordinator_origin";
-constexpr char kAggregatableDeduplicationKeys[] =
-    "aggregatable_deduplication_keys";
-constexpr char kAggregatableTriggerData[] = "aggregatable_trigger_data";
-constexpr char kAggregatableValues[] = "aggregatable_values";
-constexpr char kEventTriggerData[] = "event_trigger_data";
-
 base::expected<std::optional<SuitableOrigin>, TriggerRegistrationError>
 ParseAggregationCoordinator(const base::Value* value) {
   // The default value is used for backward compatibility prior to this
@@ -116,7 +109,7 @@
   static_assert(TriggerRegistrationError::kMaxValue ==
                     TriggerRegistrationError::kEventValueInvalid,
                 "Update ConversionTriggerRegistrationError enum.");
-  base::UmaHistogramEnumeration("Conversions.TriggerRegistrationError10",
+  base::UmaHistogramEnumeration("Conversions.TriggerRegistrationError11",
                                 error);
 }
 
@@ -127,24 +120,23 @@
 
   ASSIGN_OR_RETURN(registration.filters, FilterPair::FromJSON(dict));
 
-  ASSIGN_OR_RETURN(
-      registration.aggregatable_dedup_keys,
-      ParseList<AggregatableDedupKey>(
-          dict.Find(kAggregatableDeduplicationKeys),
-          TriggerRegistrationError::kAggregatableDedupKeyListWrongType,
-          &AggregatableDedupKey::FromJSON));
+  ASSIGN_OR_RETURN(registration.aggregatable_dedup_keys,
+                   ParseList<AggregatableDedupKey>(
+                       dict.Find(kAggregatableDeduplicationKeys),
+                       TriggerRegistrationError::kAggregatableDedupKeyWrongType,
+                       &AggregatableDedupKey::FromJSON));
 
   ASSIGN_OR_RETURN(registration.event_triggers,
                    ParseList<EventTriggerData>(
                        dict.Find(kEventTriggerData),
-                       TriggerRegistrationError::kEventTriggerDataListWrongType,
+                       TriggerRegistrationError::kEventTriggerDataWrongType,
                        &EventTriggerData::FromJSON));
 
   ASSIGN_OR_RETURN(
       registration.aggregatable_trigger_data,
       ParseList<AggregatableTriggerData>(
           dict.Find(kAggregatableTriggerData),
-          TriggerRegistrationError::kAggregatableTriggerDataListWrongType,
+          TriggerRegistrationError::kAggregatableTriggerDataWrongType,
           &AggregatableTriggerData::FromJSON));
 
   ASSIGN_OR_RETURN(
diff --git a/components/attribution_reporting/trigger_registration_error.mojom b/components/attribution_reporting/trigger_registration_error.mojom
index 78e848a..2280062 100644
--- a/components/attribution_reporting/trigger_registration_error.mojom
+++ b/components/attribution_reporting/trigger_registration_error.mojom
@@ -11,33 +11,32 @@
   kRootWrongType = 1,
 
   kFiltersWrongType = 2,
-  kFiltersListWrongType = 3,
-  kFiltersValueWrongType = 4,
+  kFiltersValueInvalid = 3,
+  kFiltersLookbackWindowValueInvalid = 4,
   kFiltersUsingReservedKey = 5,
+  kFiltersListValueInvalid = 6,
+  kFiltersListLookbackWindowValueInvalid = 7,
+  kFiltersListUsingReservedKey = 8,
 
-  kAggregatableValuesWrongType = 6,
-  kAggregatableValuesKeyTooLong = 7,
-  kAggregatableValuesListWrongType = 8,
-  kAggregatableValuesListValuesFieldMissing = 9,
-  kAggregatableValuesValueInvalid = 10,
+  kAggregatableValuesWrongType = 9,
+  kAggregatableValuesKeyTooLong = 10,
+  kAggregatableValuesListValuesFieldMissing = 11,
+  kAggregatableValuesValueInvalid = 12,
+  kAggregatableValuesListKeyTooLong = 13,
+  kAggregatableValuesListValueInvalid = 14,
 
-  kAggregatableTriggerDataListWrongType = 11,
-  kAggregatableTriggerDataWrongType = 12,
-  kAggregatableTriggerDataKeyPieceMissing = 13,
-  kAggregatableTriggerDataSourceKeysWrongType = 14,
-  kAggregatableTriggerDataKeyPieceInvalid = 15,
-  kAggregatableTriggerDataSourceKeysKeyInvalid = 16,
+  kAggregatableTriggerDataWrongType = 15,
+  kAggregatableTriggerDataKeyPieceMissing = 16,
+  kAggregatableTriggerDataSourceKeysInvalid = 17,
+  kAggregatableTriggerDataKeyPieceInvalid = 18,
 
-  kEventTriggerDataListWrongType = 17,
-  kEventTriggerDataValueInvalid = 18,
   kEventTriggerDataWrongType = 19,
+  kEventTriggerDataValueInvalid = 20,
+  kEventPriorityValueInvalid = 21,
+  kEventDedupKeyValueInvalid = 22,
 
-  kEventPriorityValueInvalid = 20,
-  kEventDedupKeyValueInvalid = 21,
+  kAggregationCoordinatorValueInvalid = 23,
 
-  kAggregationCoordinatorValueInvalid = 22,
-
-  kAggregatableDedupKeyListWrongType = 23,
   kAggregatableDedupKeyValueInvalid = 24,
   kAggregatableDedupKeyWrongType = 25,
 
diff --git a/components/attribution_reporting/trigger_registration_unittest.cc b/components/attribution_reporting/trigger_registration_unittest.cc
index 011c621..06b4712d 100644
--- a/components/attribution_reporting/trigger_registration_unittest.cc
+++ b/components/attribution_reporting/trigger_registration_unittest.cc
@@ -144,7 +144,7 @@
       {
           "event_triggers_wrong_type",
           R"json({"event_trigger_data":{}})json",
-          ErrorIs(TriggerRegistrationError::kEventTriggerDataListWrongType),
+          ErrorIs(TriggerRegistrationError::kEventTriggerDataWrongType),
       },
       {
           "event_trigger_data_wrong_type",
@@ -199,8 +199,7 @@
       {
           "aggregatable_trigger_data_list_wrong_type",
           R"json({"aggregatable_trigger_data": {}})json",
-          ErrorIs(
-              TriggerRegistrationError::kAggregatableTriggerDataListWrongType),
+          ErrorIs(TriggerRegistrationError::kAggregatableTriggerDataWrongType),
       },
       {
           "aggregatable_trigger_data_wrong_type",
@@ -245,7 +244,7 @@
       {
           "aggregatable_dedup_keys_wrong_type",
           R"json({"aggregatable_deduplication_keys":{}})json",
-          ErrorIs(TriggerRegistrationError::kAggregatableDedupKeyListWrongType),
+          ErrorIs(TriggerRegistrationError::kAggregatableDedupKeyWrongType),
       },
       {
           "aggregatable_dedup_key_wrong_type",
@@ -282,7 +281,7 @@
   };
 
   static constexpr char kTriggerRegistrationErrorMetric[] =
-      "Conversions.TriggerRegistrationError10";
+      "Conversions.TriggerRegistrationError11";
 
   for (const auto& test_case : kTestCases) {
     SCOPED_TRACE(test_case.description);
@@ -381,7 +380,7 @@
   };
 
   static constexpr char kTriggerRegistrationErrorMetric[] =
-      "Conversions.TriggerRegistrationError10";
+      "Conversions.TriggerRegistrationError11";
 
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 4b736895..4f7b301 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -788,12 +788,6 @@
   }
 
   // Notify PasswordManager about potential username fields for UFF.
-  // Do not consider fields that have no names or ids to avoid aggregation
-  // of multiple unrelated fields. (crbug.com/1209143)
-  if (element.NameForAutofill().IsEmpty()) {
-    return;
-  }
-
   // Exclude 1-symbol inputs, as they are unlikely to be usernames and likely
   // to be characters/digits of OTPs.
   // Exclude too large inputs, as they are usually not usernames.
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 2be06f9f..c72a6b8 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -377,8 +377,6 @@
     "payments/autofill_payments_feature_availability.h",
     "payments/autofill_wallet_model_type_controller.cc",
     "payments/autofill_wallet_model_type_controller.h",
-    "payments/card_unmask_challenge_option.cc",
-    "payments/card_unmask_challenge_option.h",
     "payments/card_unmask_delegate.cc",
     "payments/card_unmask_delegate.h",
     "payments/client_behavior_constants.h",
@@ -675,6 +673,7 @@
 
   public_deps = [
     ":legal_message_line",
+    "//components/autofill/core/browser/payments:card_unmask_challenge_option",
     "//components/autofill/core/browser/proto",
     "//components/autofill/core/common",
     "//components/autofill/core/common/mojom:mojo_types",
diff --git a/components/autofill/core/browser/metrics/manual_fallback_metrics.cc b/components/autofill/core/browser/metrics/manual_fallback_metrics.cc
index 968e1868..8e7b258b 100644
--- a/components/autofill/core/browser/metrics/manual_fallback_metrics.cc
+++ b/components/autofill/core/browser/metrics/manual_fallback_metrics.cc
@@ -11,6 +11,11 @@
 
 namespace autofill::autofill_metrics {
 
+void LogAddNewAddressPromptOutcome(AutofillAddNewAddressPromptOutcome outcome) {
+  base::UmaHistogramEnumeration(
+      "Autofill.ManualFallback.AddNewAddressPromptShown", outcome);
+}
+
 ManualFallbackEventLogger::~ManualFallbackEventLogger() {
   // Emit the explicit triggering metric for fields that were either
   // unclassified or classified as something differently from the targeted
diff --git a/components/autofill/core/browser/metrics/manual_fallback_metrics.h b/components/autofill/core/browser/metrics/manual_fallback_metrics.h
index ccfffce..58924e6 100644
--- a/components/autofill/core/browser/metrics/manual_fallback_metrics.h
+++ b/components/autofill/core/browser/metrics/manual_fallback_metrics.h
@@ -10,6 +10,18 @@
 
 namespace autofill::autofill_metrics {
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class AutofillAddNewAddressPromptOutcome {
+  kSaved = 0,
+  kCanceled = 1,
+  kMaxValue = kCanceled
+};
+
+// Called when the "Add new address" prompt (triggered from the context menu
+// when there are no addresses saved) gets a decision from the user.
+void LogAddNewAddressPromptOutcome(AutofillAddNewAddressPromptOutcome outcome);
+
 // Metrics logger when autofill is triggered from either an unclassified field
 // or a field that does not match the target `FillingProduct`, for instance when
 // an user uses address fallback on a field classified as credit card. Like
diff --git a/components/autofill/core/browser/payments/BUILD.gn b/components/autofill/core/browser/payments/BUILD.gn
new file mode 100644
index 0000000..0e99129
--- /dev/null
+++ b/components/autofill/core/browser/payments/BUILD.gn
@@ -0,0 +1,14 @@
+# 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.
+
+source_set("card_unmask_challenge_option") {
+  sources = [
+    "card_unmask_challenge_option.cc",
+    "card_unmask_challenge_option.h",
+  ]
+  deps = [
+    "//base",
+    "//url",
+  ]
+}
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.cc b/components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.cc
index 3e32f16..27088bd 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.cc
+++ b/components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.cc
@@ -257,4 +257,9 @@
   selected_challenge_option_id_ = selected_challenge_option_id;
 }
 
+base::WeakPtr<CardUnmaskAuthenticationSelectionDialogControllerImpl>
+CardUnmaskAuthenticationSelectionDialogControllerImpl::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.h b/components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.h
index 5eb2bf8..bb0f7511 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.h
+++ b/components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.h
@@ -82,6 +82,9 @@
     challenge_options_ = challenge_options;
   }
 
+  base::WeakPtr<CardUnmaskAuthenticationSelectionDialogControllerImpl>
+  GetWeakPtr();
+
  private:
   // Contains all of the challenge options an issuer has for the user.
   std::vector<CardUnmaskChallengeOption> challenge_options_;
@@ -117,6 +120,9 @@
   // `SetSelectedChallengeOptionId()` to `SetSelectedChallengeOptionForId()`.
   CardUnmaskChallengeOptionType selected_challenge_option_type_ =
       CardUnmaskChallengeOptionType::kUnknownType;
+
+  base::WeakPtrFactory<CardUnmaskAuthenticationSelectionDialogControllerImpl>
+      weak_ptr_factory_{this};
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 8a3b9202b..265895e2 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -292,12 +292,14 @@
              "AutofillExtractOnlyNonAdFrames",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// LINT.IfChange(autofill_xhr_submission_detection_ios)
 // If enabled, XHR form submissions are detected on iOS when the last interacted
 // form in a frame is removed. Otherwise only HTTP form submissions are detected
 // on iOS.
 BASE_FEATURE(kAutofillEnableXHRSubmissionDetectionIOS,
              "AutofillEnableXHRSubmissionDetectionIOS",
              base::FEATURE_DISABLED_BY_DEFAULT);
+// LINT.ThenChange(//components/autofill/ios/form_util/resources/autofill_form_features.ts:autofill_xhr_submission_detection_ios)
 
 // Implements a model that suppresses suggestions after N times the user ignores
 // the popup (i.e. doesn't select a suggestion from the popup).
diff --git a/components/autofill/ios/browser/autofill_agent.mm b/components/autofill/ios/browser/autofill_agent.mm
index f6cb9e8..6db6a025 100644
--- a/components/autofill/ios/browser/autofill_agent.mm
+++ b/components/autofill/ios/browser/autofill_agent.mm
@@ -889,6 +889,10 @@
       frame, base::FeatureList::IsEnabled(
                  autofill::features::kAutofillAcrossIframesIos));
 
+  FormUtilJavaScriptFeature::GetInstance()->SetAutofillXHRSubmissionDetection(
+      frame, base::FeatureList::IsEnabled(
+                 autofill::features::kAutofillEnableXHRSubmissionDetectionIOS));
+
   if (frame->IsMainFrame()) {
     _popupDelegate.reset();
     _suggestionsAvailableCompletion = nil;
diff --git a/components/autofill/ios/form_util/form_util_java_script_feature.h b/components/autofill/ios/form_util/form_util_java_script_feature.h
index 62a742d..f3836ef 100644
--- a/components/autofill/ios/form_util/form_util_java_script_feature.h
+++ b/components/autofill/ios/form_util/form_util_java_script_feature.h
@@ -29,6 +29,9 @@
   // Enables/disables the AutofillAcrossIframes feature in `frame`.
   void SetAutofillAcrossIframes(web::WebFrame* frame, bool enabled);
 
+  // Enables/disables XHR form submission detection in `frame`.
+  void SetAutofillXHRSubmissionDetection(web::WebFrame* frame, bool enabled);
+
  private:
   friend class base::NoDestructor<FormUtilJavaScriptFeature>;
 
diff --git a/components/autofill/ios/form_util/form_util_java_script_feature.mm b/components/autofill/ios/form_util/form_util_java_script_feature.mm
index 181e35c..d7f250a 100644
--- a/components/autofill/ios/form_util/form_util_java_script_feature.mm
+++ b/components/autofill/ios/form_util/form_util_java_script_feature.mm
@@ -61,4 +61,12 @@
                          base::Value::List().Append(enabled));
 }
 
+void FormUtilJavaScriptFeature::SetAutofillXHRSubmissionDetection(
+    web::WebFrame* frame,
+    bool enabled) {
+  CallJavaScriptFunction(
+      frame, "autofill_form_features.setAutofillXHRSubmissionDetection",
+      base::Value::List().Append(enabled));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/ios/form_util/resources/autofill_form_features.ts b/components/autofill/ios/form_util/resources/autofill_form_features.ts
index 162ef04fa..b627331 100644
--- a/components/autofill/ios/form_util/resources/autofill_form_features.ts
+++ b/components/autofill/ios/form_util/resources/autofill_form_features.ts
@@ -18,6 +18,14 @@
 let autofillAcrossIframes: boolean = false;
 // LINT.ThenChange(//components/autofill/core/common/autofill_features.cc:autofill_across_iframes_ios)
 
+// LINT.IfChange(autofill_xhr_submission_detection_ios)
+/**
+ * Enables sending all form removal events to the browser for submission detection.
+ * Corresponds to autofill::feature::AutofillEnableXHRSubmissionDetectionIOS.
+ */
+let autofillXHRSubmissionDetection: boolean = false;
+// LINT.ThenChange(//components/autofill/core/common/autofill_features.cc:autofill_xhr_submission_detection_ios)
+
 /**
  * @see autofillAcrossIframes
  */
@@ -32,9 +40,25 @@
   return autofillAcrossIframes;
 }
 
+/**
+ * @see autofillXHRSubmissionDetectionEnabled
+ */
+function setAutofillXHRSubmissionDetection(enabled: boolean): void {
+  autofillXHRSubmissionDetection = enabled;
+}
+
+/**
+ * @see autofillXHRSubmissionDetection
+ */
+function isAutofillXHRSubmissionDetectionEnabled(): boolean {
+  return autofillXHRSubmissionDetection;
+}
+
 // Expose globally via `gCrWeb` instead of `export` to ensure state (feature
 // on/off) is maintained across imports.
 gCrWeb.autofill_form_features = {
   setAutofillAcrossIframes,
   isAutofillAcrossIframesEnabled,
+  setAutofillXHRSubmissionDetection,
+  isAutofillXHRSubmissionDetectionEnabled,
 };
diff --git a/components/autofill/ios/form_util/resources/fill_util.ts b/components/autofill/ios/form_util/resources/fill_util.ts
index 2d3fda2..8375dcdf 100644
--- a/components/autofill/ios/form_util/resources/fill_util.ts
+++ b/components/autofill/ios/form_util/resources/fill_util.ts
@@ -310,7 +310,7 @@
 
   if (overrideProperty) {
     Object.defineProperty(input, propertyName, oldPropertyDescriptor);
-    if (!setterCalled && input[propertyName] !== value) {
+    if (!setterCalled) {
       // The setter was never called. This may be intentional (the framework
       // ignored the input event) or not (the event did not conform to what
       // framework expected). The whole function will likely fail, but try to
diff --git a/components/commerce/core/price_tracking_utils.cc b/components/commerce/core/price_tracking_utils.cc
index 4c4c0cf..a947991a 100644
--- a/components/commerce/core/price_tracking_utils.cc
+++ b/components/commerce/core/price_tracking_utils.cc
@@ -45,7 +45,12 @@
     std::vector<const bookmarks::BookmarkNode*> results =
         power_bookmarks::GetBookmarksMatchingProperties(model.get(), query, -1);
 
-    for (const auto* node : results) {
+    for (const bookmarks::BookmarkNode* node : results) {
+      CHECK(node);
+      if (model->IsLocalOnlyNode(*node)) {
+        continue;
+      }
+
       std::unique_ptr<power_bookmarks::PowerBookmarkMeta> meta =
           power_bookmarks::GetNodePowerBookmarkMeta(model.get(), node);
 
@@ -146,8 +151,9 @@
     bool enabled,
     base::OnceCallback<void(bool)> callback,
     bool was_bookmark_created_by_price_tracking) {
-  if (!service || !model || !node)
+  if (!service || !model || !node || model->IsLocalOnlyNode(*node)) {
     return;
+  }
 
   std::unique_ptr<power_bookmarks::PowerBookmarkMeta> meta =
       power_bookmarks::GetNodePowerBookmarkMeta(model, node);
@@ -312,7 +318,15 @@
 
   power_bookmarks::PowerBookmarkQueryFields query;
   query.type = power_bookmarks::PowerBookmarkType::SHOPPING;
-  return power_bookmarks::GetBookmarksMatchingProperties(model, query, -1);
+
+  std::vector<const bookmarks::BookmarkNode*> nodes =
+      power_bookmarks::GetBookmarksMatchingProperties(model, query, -1);
+
+  std::erase_if(nodes, [model](const bookmarks::BookmarkNode* node) {
+    return model->IsLocalOnlyNode(*node);
+  });
+
+  return nodes;
 }
 
 bool PopulateOrUpdateBookmarkMetaIfNeeded(
@@ -424,8 +438,10 @@
     const GURL& url) {
   const bookmarks::BookmarkNode* node =
       model->GetMostRecentlyAddedUserNodeForURL(url);
-  return node ? std::optional<std::u16string>(node->parent()->GetTitle())
-              : std::nullopt;
+  if (!node || model->IsLocalOnlyNode(*node)) {
+    return std::nullopt;
+  }
+  return std::optional<std::u16string>(node->parent()->GetTitle());
 }
 
 const bookmarks::BookmarkNode* GetShoppingCollectionBookmarkFolder(
@@ -475,7 +491,7 @@
   const bookmarks::BookmarkNode* node =
       model->GetMostRecentlyAddedUserNodeForURL(url);
 
-  if (!node) {
+  if (!node || model->IsLocalOnlyNode(*node)) {
     return std::nullopt;
   }
 
diff --git a/components/commerce/core/price_tracking_utils_unittest.cc b/components/commerce/core/price_tracking_utils_unittest.cc
index 01c0808..c3e69d8f 100644
--- a/components/commerce/core/price_tracking_utils_unittest.cc
+++ b/components/commerce/core/price_tracking_utils_unittest.cc
@@ -35,7 +35,10 @@
 class PriceTrackingUtilsTest : public testing::Test {
  protected:
   void SetUp() override {
-    bookmark_model_ = bookmarks::TestBookmarkClient::CreateModel();
+    auto client = std::make_unique<bookmarks::TestBookmarkClient>();
+    client->SetIsSyncFeatureEnabledIncludingBookmarks(true);
+    bookmark_model_ =
+        bookmarks::TestBookmarkClient::CreateModelWithClient(std::move(client));
     shopping_service_ = std::make_unique<MockShoppingService>();
     shopping_service_->SetBookmarkModelUsedForSync(bookmark_model_.get());
     pref_service_ = std::make_unique<TestingPrefServiceSimple>();
diff --git a/components/commerce/core/shopping_bookmark_model_observer.cc b/components/commerce/core/shopping_bookmark_model_observer.cc
index a6ee46a..cc71bf6 100644
--- a/components/commerce/core/shopping_bookmark_model_observer.cc
+++ b/components/commerce/core/shopping_bookmark_model_observer.cc
@@ -121,6 +121,10 @@
         "Commerce.PriceTracking.ShoppingCollection.Created"));
   }
 
+  if (model->IsLocalOnlyNode(*node)) {
+    return;
+  }
+
   // TODO(b:287289351): We should consider listening to metadata changes
   //                    instead. Presumably, shopping data is primarily being
   //                    added to new bookmarks, so we could potentially use the
diff --git a/components/commerce/core/shopping_bookmark_model_observer_unittest.cc b/components/commerce/core/shopping_bookmark_model_observer_unittest.cc
index 088d469d..809f0ebe 100644
--- a/components/commerce/core/shopping_bookmark_model_observer_unittest.cc
+++ b/components/commerce/core/shopping_bookmark_model_observer_unittest.cc
@@ -30,7 +30,10 @@
 class ShoppingBookmarkModelObserverTest : public testing::Test {
  protected:
   void SetUp() override {
-    bookmark_model_ = bookmarks::TestBookmarkClient::CreateModel();
+    auto client = std::make_unique<bookmarks::TestBookmarkClient>();
+    client->SetIsSyncFeatureEnabledIncludingBookmarks(true);
+    bookmark_model_ =
+        bookmarks::TestBookmarkClient::CreateModelWithClient(std::move(client));
     shopping_service_ = std::make_unique<MockShoppingService>();
     subscriptions_manager_ = std::make_unique<MockSubscriptionsManager>();
 
diff --git a/components/commerce/core/shopping_service.cc b/components/commerce/core/shopping_service.cc
index 753e0bc..2331839 100644
--- a/components/commerce/core/shopping_service.cc
+++ b/components/commerce/core/shopping_service.cc
@@ -554,18 +554,22 @@
   std::vector<GURL> urls;
   std::unordered_map<std::string, int64_t> url_to_id_map;
   for (uint64_t id : bookmark_ids) {
+    bookmarks::BookmarkModel* model = GetBookmarkModelUsedForSync();
     const bookmarks::BookmarkNode* bookmark =
-        bookmarks::GetBookmarkNodeByID(GetBookmarkModelUsedForSync(), id);
+        bookmarks::GetBookmarkNodeByID(model, id);
 
     std::unique_ptr<power_bookmarks::PowerBookmarkMeta> meta =
-        power_bookmarks::GetNodePowerBookmarkMeta(GetBookmarkModelUsedForSync(),
-                                                  bookmark);
+        power_bookmarks::GetNodePowerBookmarkMeta(model, bookmark);
 
-    if (!meta || !meta->has_shopping_specifics())
+    if (!meta || !meta->has_shopping_specifics()) {
       continue;
+    }
 
-    if (!bookmark)
+    CHECK(bookmark);
+
+    if (model->IsLocalOnlyNode(*bookmark)) {
       continue;
+    }
 
     urls.push_back(bookmark->url());
     url_to_id_map[bookmark->url().spec()] = id;
diff --git a/components/commerce/core/shopping_service_test_base.cc b/components/commerce/core/shopping_service_test_base.cc
index 6a79172..a8803fdb 100644
--- a/components/commerce/core/shopping_service_test_base.cc
+++ b/components/commerce/core/shopping_service_test_base.cc
@@ -439,6 +439,11 @@
   RegisterPrefs(pref_service_->registry());
   pref_service_->registry()->RegisterBooleanPref(
       unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, false);
+  // These tests use a dedicated BookmarkModel instance for account bookmarks.
+  // Let BookmarkModel know about it, so it can return the correct value in
+  // BookmarkModel::IsLocalOnlyNode().
+  account_bookmark_model_
+      ->SetLoadedAccountBookmarksFileAsLocalOrSyncableBookmarksForTest();
 }
 
 ShoppingServiceTestBase::~ShoppingServiceTestBase() = default;
diff --git a/components/commerce/core/shopping_service_unittest.cc b/components/commerce/core/shopping_service_unittest.cc
index db43de3..a72661a0 100644
--- a/components/commerce/core/shopping_service_unittest.cc
+++ b/components/commerce/core/shopping_service_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/values.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_node.h"
+#include "components/bookmarks/test/test_bookmark_client.h"
 #include "components/commerce/core/commerce_feature_list.h"
 #include "components/commerce/core/feature_utils.h"
 #include "components/commerce/core/mock_account_checker.h"
@@ -98,6 +99,13 @@
         ShouldEnableReplaceSyncPromosWithSignInPromos());
     sync_service_->SetHasSyncConsent(
         !ShouldEnableReplaceSyncPromosWithSignInPromos());
+    // Mimic not only the sync ConsentLevel being set but also bookmarks
+    // being specifically on.
+    static_cast<bookmarks::TestBookmarkClient*>(
+        local_or_syncable_bookmark_model_->client())
+        ->SetIsSyncFeatureEnabledIncludingBookmarks(
+            !ShouldEnableReplaceSyncPromosWithSignInPromos());
+
     ShoppingServiceTestBase::SetUp();
   }
 
diff --git a/components/commerce/core/webui/shopping_service_handler_unittest.cc b/components/commerce/core/webui/shopping_service_handler_unittest.cc
index 97c4b10..fa7de71 100644
--- a/components/commerce/core/webui/shopping_service_handler_unittest.cc
+++ b/components/commerce/core/webui/shopping_service_handler_unittest.cc
@@ -121,7 +121,10 @@
 
  protected:
   void SetUp() override {
-    bookmark_model_ = bookmarks::TestBookmarkClient::CreateModel();
+    auto client = std::make_unique<bookmarks::TestBookmarkClient>();
+    client->SetIsSyncFeatureEnabledIncludingBookmarks(true);
+    bookmark_model_ =
+        bookmarks::TestBookmarkClient::CreateModelWithClient(std::move(client));
     shopping_service_ = std::make_unique<MockShoppingService>();
     pref_service_ = std::make_unique<TestingPrefServiceSimple>();
     RegisterPrefs(pref_service_->registry());
@@ -750,4 +753,4 @@
 }
 
 }  // namespace
-}  // namespace commerce
\ No newline at end of file
+}  // namespace commerce
diff --git a/components/compose/core/browser/compose_features.cc b/components/compose/core/browser/compose_features.cc
index 5befc6a..e004d19 100644
--- a/components/compose/core/browser/compose_features.cc
+++ b/components/compose/core/browser/compose_features.cc
@@ -43,7 +43,7 @@
 
 BASE_FEATURE(kEnableComposeSavedStateNotification,
              "EnableComposeSavedStateNotification",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kComposeUiParams,
              "ComposeUiParams",
diff --git a/components/content_settings/renderer/content_settings_agent_impl.cc b/components/content_settings/renderer/content_settings_agent_impl.cc
index d78c86f..0ff900e7 100644
--- a/components/content_settings/renderer/content_settings_agent_impl.cc
+++ b/components/content_settings/renderer/content_settings_agent_impl.cc
@@ -55,6 +55,11 @@
 
 ContentSettingsAgentImpl::Delegate::~Delegate() = default;
 
+bool ContentSettingsAgentImpl::Delegate::IsFrameAllowlistedForStorageAccess(
+    blink::WebFrame* frame) const {
+  return false;
+}
+
 bool ContentSettingsAgentImpl::Delegate::IsSchemeAllowlisted(
     const std::string& scheme) {
   return false;
@@ -230,6 +235,11 @@
     StorageType storage_type,
     base::OnceCallback<void(bool)> callback) {
   WebLocalFrame* frame = render_frame()->GetWebFrame();
+  if (delegate_->IsFrameAllowlistedForStorageAccess(frame)) {
+    std::move(callback).Run(true);
+    return;
+  }
+
   if (IsFrameWithOpaqueOrigin(frame)) {
     std::move(callback).Run(false);
     return;
@@ -263,8 +273,13 @@
 bool ContentSettingsAgentImpl::AllowStorageAccessSync(
     StorageType storage_type) {
   WebLocalFrame* frame = render_frame()->GetWebFrame();
-  if (IsFrameWithOpaqueOrigin(frame))
+  if (delegate_->IsFrameAllowlistedForStorageAccess(frame)) {
+    return true;
+  }
+
+  if (IsFrameWithOpaqueOrigin(frame)) {
     return false;
+  }
 
   StoragePermissionsKey key(url::Origin(frame->GetSecurityOrigin()),
                             storage_type);
diff --git a/components/content_settings/renderer/content_settings_agent_impl.h b/components/content_settings/renderer/content_settings_agent_impl.h
index 3c6fc85..82189582 100644
--- a/components/content_settings/renderer/content_settings_agent_impl.h
+++ b/components/content_settings/renderer/content_settings_agent_impl.h
@@ -26,6 +26,7 @@
 #include "url/origin.h"
 
 namespace blink {
+class WebFrame;
 class WebURL;
 }  // namespace blink
 
@@ -45,6 +46,10 @@
    public:
     virtual ~Delegate();
 
+    // Return true if this frame should be allowlisted for accessing storage.
+    virtual bool IsFrameAllowlistedForStorageAccess(
+        blink::WebFrame* frame) const;
+
     // Return true if this scheme should be allowlisted for content settings.
     virtual bool IsSchemeAllowlisted(const std::string& scheme);
 
diff --git a/components/contextual_search/OWNERS b/components/contextual_search/OWNERS
index 5e22a75..6ad6111 100644
--- a/components/contextual_search/OWNERS
+++ b/components/contextual_search/OWNERS
@@ -1,7 +1,6 @@
 gangwu@chromium.org
 
 # Secondary
-donnd@chromium.org
 twellington@chromium.org
 
 per-file *.mojom=set noparent
diff --git a/components/crash/android/BUILD.gn b/components/crash/android/BUILD.gn
index 9105dfc..92e0269 100644
--- a/components/crash/android/BUILD.gn
+++ b/components/crash/android/BUILD.gn
@@ -9,7 +9,6 @@
   "java/src/org/chromium/components/crash/CrashKeys.java",
   "java/src/org/chromium/components/crash/PureJavaExceptionHandler.java",
   "java/src/org/chromium/components/crash/browser/ChildProcessCrashObserver.java",
-  "java/src/org/chromium/components/crash/browser/PackagePaths.java",
   "java/src/org/chromium/components/crash/browser/ProcessExitReasonFromSystem.java",
 ]
 
@@ -17,6 +16,11 @@
   sources = _jni_sources
 }
 
+generate_jni("package_paths_jni") {
+  sources =
+      [ "java/src/org/chromium/components/crash/browser/PackagePaths.java" ]
+}
+
 java_cpp_enum("java_enums_srcjar") {
   sources = [ "crash_keys_android.h" ]
 }
@@ -42,6 +46,7 @@
     "java/src/org/chromium/components/crash/MinidumpLogcatPrepender.java",
     "java/src/org/chromium/components/crash/NativeAndJavaSmartExceptionReporter.java",
     "java/src/org/chromium/components/crash/PureJavaExceptionReporter.java",
+    "java/src/org/chromium/components/crash/browser/PackagePaths.java",
   ]
   sources += _jni_sources
 }
diff --git a/components/crash/core/app/BUILD.gn b/components/crash/core/app/BUILD.gn
index 24e5f6d..d921b487 100644
--- a/components/crash/core/app/BUILD.gn
+++ b/components/crash/core/app/BUILD.gn
@@ -108,7 +108,7 @@
     sources += [ "crashpad_android.cc" ]
     deps += [
       ":crashpad_handler_main",
-      "//components/crash/android:jni_headers",
+      "//components/crash/android:package_paths_jni",
       "//third_party/crashpad/crashpad/handler",
       "//third_party/crashpad/crashpad/snapshot",
     ]
diff --git a/components/crash/core/app/DEPS b/components/crash/core/app/DEPS
index b99bb69..1483de04 100644
--- a/components/crash/core/app/DEPS
+++ b/components/crash/core/app/DEPS
@@ -2,7 +2,7 @@
   "+sandbox",
   "+third_party/breakpad",
   "+components/allocation_recorder/crash_handler",
-  "+components/crash/android/jni_headers",
+  "+components/crash/android/package_paths_jni",
   "+components/gwp_asan/buildflags/buildflags.h",
   "+components/gwp_asan/crash_handler/crash_handler.h",
   "+components/stability_report",
diff --git a/components/crash/core/app/crashpad_android.cc b/components/crash/core/app/crashpad_android.cc
index ac665b20..25f39d9 100644
--- a/components/crash/core/app/crashpad_android.cc
+++ b/components/crash/core/app/crashpad_android.cc
@@ -33,7 +33,7 @@
 #include "base/synchronization/lock.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
-#include "components/crash/android/jni_headers/PackagePaths_jni.h"
+#include "components/crash/android/package_paths_jni/PackagePaths_jni.h"
 #include "components/crash/core/app/crash_reporter_client.h"
 #include "content/public/common/content_descriptors.h"
 #include "sandbox/linux/services/syscall_wrappers.h"
diff --git a/components/download/public/common/download_item.h b/components/download/public/common/download_item.h
index c77cd21..31d82f6 100644
--- a/components/download/public/common/download_item.h
+++ b/components/download/public/common/download_item.h
@@ -471,7 +471,7 @@
   // final name.
   virtual bool AllDataSaved() const = 0;
 
-  // Total number of expected bytes. Returns -1 if the total size is unknown.
+  // Total number of expected bytes. Returns 0 if the total size is unknown.
   virtual int64_t GetTotalBytes() const = 0;
 
   // Total number of bytes that have been received and written to the download
diff --git a/components/feature_engagement/public/feature_configurations.cc b/components/feature_engagement/public/feature_configurations.cc
index 0e79af1..c98cdbc 100644
--- a/components/feature_engagement/public/feature_configurations.cc
+++ b/components/feature_engagement/public/feature_configurations.cc
@@ -592,22 +592,6 @@
     return config;
   }
 
-  if (kIPHComposeMenuNewBadgeFeature.name == feature->name) {
-    // A config that allows the new badge attached to the Compose feature
-    // entrypoint in the right-click menu to be shown at most 10 times in a
-    // 10-day window and only while the user has opened the Compose feature less
-    // than 3 times.
-    std::optional<FeatureConfig> config = FeatureConfig();
-    config->valid = true;
-    config->availability = Comparator(ANY, 0);
-    config->session_rate = Comparator(ANY, 0);
-    config->trigger = EventConfig("compose_menu_new_badge_triggered",
-                                  Comparator(LESS_THAN, 10), 10, 360);
-    config->used = EventConfig("compose_menu_item_activated",
-                               Comparator(LESS_THAN, 3), 360, 360);
-    return config;
-  }
-
   if (kIPHComposeNewBadgeFeature.name == feature->name) {
     // A config that allows the new badge displayed in the Compose feature nudge
     // to be shown at most 4 times in a 10-day window and only while the user
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index ba47ac7..3d5a034 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -28,9 +28,6 @@
 BASE_FEATURE(kIPHCompanionSidePanelRegionSearchFeature,
              "IPH_CompanionSidePanelRegionSearch",
              base::FEATURE_ENABLED_BY_DEFAULT);
-BASE_FEATURE(kIPHComposeMenuNewBadgeFeature,
-             "IPH_ComposeMenuNewBadgeFeature",
-             base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHComposeMSBBSettingsFeature,
              "IPH_ComposeMSBBSettingsFeature",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index 89f64b2..f54bcb9 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -24,7 +24,6 @@
 BASE_DECLARE_FEATURE(kIPHBatterySaverModeFeature);
 BASE_DECLARE_FEATURE(kIPHCompanionSidePanelFeature);
 BASE_DECLARE_FEATURE(kIPHCompanionSidePanelRegionSearchFeature);
-BASE_DECLARE_FEATURE(kIPHComposeMenuNewBadgeFeature);
 BASE_DECLARE_FEATURE(kIPHComposeMSBBSettingsFeature);
 BASE_DECLARE_FEATURE(kIPHComposeNewBadgeFeature);
 BASE_DECLARE_FEATURE(kIPHDeepScanPromptRemovalFeature);
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index 62d5c28..4626d98 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -151,7 +151,6 @@
     &kIPHBatterySaverModeFeature,
     &kIPHCompanionSidePanelFeature,
     &kIPHCompanionSidePanelRegionSearchFeature,
-    &kIPHComposeMenuNewBadgeFeature,
     &kIPHComposeMSBBSettingsFeature,
     &kIPHComposeNewBadgeFeature,
     &kIPHDeepScanPromptRemovalFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 782a675a..7969eaca 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -271,8 +271,6 @@
 DEFINE_VARIATION_PARAM(kIPHCompanionSidePanelFeature, "IPH_CompanionSidePanel");
 DEFINE_VARIATION_PARAM(kIPHCompanionSidePanelRegionSearchFeature,
                        "IPH_CompanionSidePanelRegionSearch");
-DEFINE_VARIATION_PARAM(kIPHComposeMenuNewBadgeFeature,
-                       "IPH_ComposeMenuNewBadgeFeature");
 DEFINE_VARIATION_PARAM(kIPHComposeNewBadgeFeature,
                        "IPH_ComposeNewBadgeFeature");
 DEFINE_VARIATION_PARAM(kIPHComposeMSBBSettingsFeature,
@@ -604,7 +602,6 @@
         VARIATION_ENTRY(kIPHBatterySaverModeFeature),
         VARIATION_ENTRY(kIPHCompanionSidePanelFeature),
         VARIATION_ENTRY(kIPHCompanionSidePanelRegionSearchFeature),
-        VARIATION_ENTRY(kIPHComposeMenuNewBadgeFeature),
         VARIATION_ENTRY(kIPHComposeMSBBSettingsFeature),
         VARIATION_ENTRY(kIPHComposeNewBadgeFeature),
         VARIATION_ENTRY(kIPHDeepScanPromptRemovalFeature),
diff --git a/components/feedback/feedback_common.cc b/components/feedback/feedback_common.cc
index 4abbb8b..a771ff2 100644
--- a/components/feedback/feedback_common.cc
+++ b/components/feedback/feedback_common.cc
@@ -30,6 +30,7 @@
 constexpr int kChromeOSProductId = 208;
 #endif
 constexpr int kChromeBrowserProductId = 237;
+constexpr int kMahiProductId = 5329991;
 
 // The below thresholds were chosen arbitrarily to conveniently show small data
 // as part of the report itself without having to look into the system_logs.zip
@@ -247,6 +248,11 @@
   return kChromeBrowserProductId;
 }
 
+// static
+int FeedbackCommon::GetMahiProductId() {
+  return kMahiProductId;
+}
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // static
 int FeedbackCommon::GetChromeOSProductId() {
diff --git a/components/feedback/feedback_common.h b/components/feedback/feedback_common.h
index 5035067..6c06cc4 100644
--- a/components/feedback/feedback_common.h
+++ b/components/feedback/feedback_common.h
@@ -64,6 +64,9 @@
 
   static int GetChromeBrowserProductId();
 
+  // Mahi feature has the dedicated product id.
+  static int GetMahiProductId();
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   static int GetChromeOSProductId();
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/components/history_embeddings/history_embeddings_service.cc b/components/history_embeddings/history_embeddings_service.cc
index 005f6d0..de80b2d 100644
--- a/components/history_embeddings/history_embeddings_service.cc
+++ b/components/history_embeddings/history_embeddings_service.cc
@@ -52,11 +52,18 @@
   std::move(callback).Run(std::move(url_passages));
 }
 
+Embedding StubComputeQueryEmbedding(const std::string& query) {
+  // TODO(b/328114635): Synchronous inference to compute vector embedding?
+  Embedding embedding({1.0f, 2.0f, 3.0f, 4.0f});
+  embedding.Normalize();
+  return embedding;
+}
+
 std::vector<Embedding> StubComputePassagesEmbeddings(
     const UrlPassages& url_passages) {
   // TODO(b/328114635): Synchronous inference to compute vector embeddings?
   return std::vector<Embedding>(url_passages.passages.passages_size(),
-                                Embedding({1.0f, 2.0f, 3.0f, 4.0f}));
+                                StubComputeQueryEmbedding(""));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -106,6 +113,14 @@
           nullptr));
 }
 
+void HistoryEmbeddingsService::Search(std::string query,
+                                      size_t count,
+                                      SearchResultCallback callback) {
+  storage_.AsyncCall(&Storage::Search)
+      .WithArgs(std::move(query), count)
+      .Then(std::move(callback));
+}
+
 void HistoryEmbeddingsService::Shutdown() {
   storage_.Reset();
 }
@@ -131,6 +146,25 @@
   sql_database.InsertOrReplacePassages(url_passages);
 }
 
+std::vector<ScoredUrl> HistoryEmbeddingsService::Storage::Search(
+    std::string query,
+    size_t count) {
+  std::vector<ScoredUrl> scored_urls =
+      sql_database.FindNearest(count, StubComputeQueryEmbedding(query));
+
+  // Populate source passages.
+  for (ScoredUrl& scored_url : scored_urls) {
+    std::optional<proto::PassagesValue> value =
+        sql_database.GetPassages(scored_url.url_id);
+    if (value &&
+        scored_url.index < static_cast<size_t>(value.value().passages_size())) {
+      scored_url.passage = value.value().passages(scored_url.index);
+    }
+  }
+
+  return scored_urls;
+}
+
 void HistoryEmbeddingsService::OnPassagesRetrieved(PassagesCallback callback,
                                                    UrlPassages url_passages) {
   storage_.AsyncCall(&Storage::ProcessAndStorePassages)
diff --git a/components/history_embeddings/history_embeddings_service.h b/components/history_embeddings/history_embeddings_service.h
index 0916733..bd30ecb 100644
--- a/components/history_embeddings/history_embeddings_service.h
+++ b/components/history_embeddings/history_embeddings_service.h
@@ -24,6 +24,7 @@
 using PassagesCallback = base::OnceCallback<void(UrlPassages)>;
 using ComputeEmbeddingsCallback =
     base::OnceCallback<std::vector<Embedding>(const UrlPassages&)>;
+using SearchResultCallback = base::OnceCallback<void(std::vector<ScoredUrl>)>;
 
 class HistoryEmbeddingsService : public KeyedService,
                                  public history::HistoryServiceObserver {
@@ -42,6 +43,10 @@
   void RetrievePassages(content::RenderFrameHost& host,
                         PassagesCallback callback);
 
+  // Find top `count` URL visit info entries nearest given `query`. Pass
+  // results to given `callback` when search completes.
+  void Search(std::string query, size_t count, SearchResultCallback callback);
+
   // KeyedService:
   void Shutdown() override;
 
@@ -63,6 +68,9 @@
     void ProcessAndStorePassages(ComputeEmbeddingsCallback compute_embeddings,
                                  UrlPassages url_passages);
 
+    // Runs search on worker sequence.
+    std::vector<ScoredUrl> Search(std::string query, size_t count);
+
     // A VectorDatabase implementation that holds data in memory.
     VectorDatabaseInMemory vector_database;
 
diff --git a/components/history_embeddings/vector_database.cc b/components/history_embeddings/vector_database.cc
index 024b124..77c3262 100644
--- a/components/history_embeddings/vector_database.cc
+++ b/components/history_embeddings/vector_database.cc
@@ -82,12 +82,18 @@
 UrlEmbeddings& UrlEmbeddings::operator=(UrlEmbeddings&) = default;
 bool UrlEmbeddings::operator==(const UrlEmbeddings&) const = default;
 
-float UrlEmbeddings::BestScoreWith(const Embedding& query) const {
+std::pair<float, size_t> UrlEmbeddings::BestScoreWith(
+    const Embedding& query) const {
+  size_t index = 0;
   float best = std::numeric_limits<float>::min();
-  for (const Embedding& embedding : embeddings) {
-    best = std::max(best, query.ScoreWith(embedding));
+  for (size_t i = 0; i < embeddings.size(); i++) {
+    float score = query.ScoreWith(embeddings[i]);
+    if (score > best) {
+      best = score;
+      index = i;
+    }
   }
-  return best;
+  return {best, index};
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -121,9 +127,13 @@
     while (q.size() > count) {
       q.pop();
     }
+    const auto [score, score_index] = item->BestScoreWith(query);
     q.push(ScoredUrl{
         .url_id = item->url_id,
-        .score = item->BestScoreWith(query),
+        .visit_id = item->visit_id,
+        .visit_time = item->visit_time,
+        .score = score,
+        .index = score_index,
     });
   }
 
diff --git a/components/history_embeddings/vector_database.h b/components/history_embeddings/vector_database.h
index 1afb096..c9d6b55 100644
--- a/components/history_embeddings/vector_database.h
+++ b/components/history_embeddings/vector_database.h
@@ -72,7 +72,8 @@
   UrlEmbeddings& operator=(UrlEmbeddings&);
   bool operator==(const UrlEmbeddings&) const;
 
-  float BestScoreWith(const Embedding& query) const;
+  // Finds score of embedding nearest to query, and also outputs its index.
+  std::pair<float, size_t> BestScoreWith(const Embedding& query) const;
 
   history::URLID url_id;
   history::VisitID visit_id;
@@ -81,8 +82,21 @@
 };
 
 struct ScoredUrl {
+  // Basic data about the found URL/visit.
   history::URLID url_id;
+  history::VisitID visit_id;
+  base::Time visit_time;
+
+  // A measure of how closely the query matched the found data.
   float score;
+
+  // Index of the embedding, which also corresponds to the index of the source
+  // passage used to compute the embedding.
+  size_t index;
+
+  // Source passage; may not be populated during search, but kept in this
+  // struct for convenience when passing finished results to service callers.
+  std::string passage;
 };
 
 // This base class decouples storage classes and inverts the dependency so that
diff --git a/components/history_strings.grdp b/components/history_strings.grdp
index ccc2231..befa63f 100644
--- a/components/history_strings.grdp
+++ b/components/history_strings.grdp
@@ -131,4 +131,16 @@
   <message name="IDS_HISTORY_EMBEDDINGS_SUGGESTION_3" desc="On the history page, the text inside of a suggestion chip that a user can click to update their search to show results from last month" translateable="false">
     Last month
   </message>
+  <message name="IDS_HISTORY_EMBEDDINGS_HEADING" desc="Heading for history embeddings results." translateable="false">
+    Results for "<ph name="SEARCH_QUERY"><ex>pizza</ex>$1</ph>"
+  </message>
+  <message name="IDS_HISTORY_EMBEDDINGS_FOOTER" desc="Footer text for history embeddings." translateable="false">
+    Some text explaining the feature.
+  </message>
+  <message name="IDS_HISTORY_EMBEDDINGS_THUMBS_UP" desc="Alternative text to the thumbs up icon under history embeddings results." translateable="false">
+    Thumbs up
+  </message>
+  <message name="IDS_HISTORY_EMBEDDINGS_THUMBS_DOWN" desc="Alternative text to the thumbs up icon under history embeddings results." translateable="false">
+    Thumbs down
+  </message>
 </grit-part>
diff --git a/components/optimization_guide/core/model_execution/model_execution_manager.cc b/components/optimization_guide/core/model_execution/model_execution_manager.cc
index eb16867..2607f20d 100644
--- a/components/optimization_guide/core/model_execution/model_execution_manager.cc
+++ b/components/optimization_guide/core/model_execution/model_execution_manager.cc
@@ -90,6 +90,19 @@
       result);
 }
 
+void NoOpExecuteRemoteFn(
+    proto::ModelExecutionFeature feature,
+    const google::protobuf::MessageLite& request,
+    std::unique_ptr<proto::LogAiDataRequest> log_request,
+    OptimizationGuideModelExecutionResultStreamingCallback callback) {
+  OptimizationGuideModelStreamingExecutionResult streaming_result;
+  streaming_result.response = base::unexpected(
+      OptimizationGuideModelExecutionError::FromModelExecutionError(
+          OptimizationGuideModelExecutionError::ModelExecutionError::
+              kGenericFailure));
+  std::move(callback).Run(std::move(streaming_result));
+}
+
 }  // namespace
 
 using ModelExecutionError =
@@ -241,9 +254,14 @@
 ModelExecutionManager::StartSession(
     proto::ModelExecutionFeature feature,
     const std::optional<SessionConfigParams>& config_params) {
+  bool disable_server_fallback =
+      config_params && config_params->disable_server_fallback;
   ExecuteRemoteFn execute_fn =
-      base::BindRepeating(&ModelExecutionManager::ExecuteModelWithStreaming,
-                          base::Unretained(this));
+      disable_server_fallback
+          ? base::BindRepeating(&NoOpExecuteRemoteFn)
+          : base::BindRepeating(
+                &ModelExecutionManager::ExecuteModelWithStreaming,
+                base::Unretained(this));
   if (on_device_model_service_controller_) {
     auto session = on_device_model_service_controller_->CreateSession(
         feature, execute_fn, optimization_guide_logger_.get(),
@@ -254,7 +272,7 @@
     }
   }
 
-  if (config_params && config_params->disable_server_fallback) {
+  if (disable_server_fallback) {
     return nullptr;
   }
 
diff --git a/components/password_manager/core/browser/form_parsing/form_data_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_data_parser_unittest.cc
index 30a9bef..0227b149 100644
--- a/components/password_manager/core/browser/form_parsing/form_data_parser_unittest.cc
+++ b/components/password_manager/core/browser/form_parsing/form_data_parser_unittest.cc
@@ -78,6 +78,7 @@
   const char* autocomplete_attribute = nullptr;
   const std::u16string value = kNonimportantValue;
   const std::u16string user_input = u"";
+  const base::StringPiece16 id_attribute = kNonimportantValue;
   const base::StringPiece16 name = kNonimportantValue;
   FormControlType form_control_type = FormControlType::kInputText;
   PasswordFieldPrediction prediction = {.type = autofill::MAX_VALID_FIELD_TYPE};
@@ -292,7 +293,11 @@
       FormFieldData field;
       const autofill::FieldRendererId renderer_id = GetUniqueId();
       field.renderer_id = renderer_id;
-      field.id_attribute = StampUniqueSuffix(u"html_id");
+      if (field_description.id_attribute == kNonimportantValue) {
+        field.id_attribute = StampUniqueSuffix(u"html_id");
+      } else {
+        field.id_attribute = std::u16string(field_description.id_attribute);
+      }
       if (field_description.name == kNonimportantValue) {
         field.name = StampUniqueSuffix(u"html_name");
       } else {
@@ -2952,6 +2957,17 @@
                    .prediction = {.type = autofill::SINGLE_USERNAME}},
               },
       },
+      {
+          .description_for_logging = "Nameless field",
+          .fields =
+              {
+                  {.role = ElementRole::NONE,
+                   .id_attribute = u"",
+                   .name = u"",
+                   .form_control_type = FormControlType::kInputText,
+                   .prediction = {.type = autofill::SINGLE_USERNAME}},
+              },
+      },
   });
 }
 
diff --git a/components/password_manager/core/common/password_manager_util.cc b/components/password_manager/core/common/password_manager_util.cc
index 3f6f75b5..634f1766 100644
--- a/components/password_manager/core/common/password_manager_util.cc
+++ b/components/password_manager/core/common/password_manager_util.cc
@@ -11,6 +11,10 @@
 
 namespace password_manager::util {
 
+// The minimum length of the input name that allows considering it as potential
+// single username field.
+const size_t kMinInputNameLengthForSingleUsername = 2;
+
 bool IsRendererRecognizedCredentialForm(const autofill::FormData& form) {
   // TODO(crbug.com/1465793): Consolidate with the parsing logic in
   // form_autofill_util.cc.
@@ -29,6 +33,14 @@
 bool CanBeConsideredAsSingleUsername(const std::u16string& name,
                                      const std::u16string& id,
                                      const std::u16string& label) {
+  // Do not consider fields with very short names/ids to avoid aggregating
+  // multiple unrelated fields on the server. (crbug.com/1209143)
+  if (name.length() < kMinInputNameLengthForSingleUsername &&
+      id.length() < kMinInputNameLengthForSingleUsername) {
+    return false;
+  }
+  // Do not consider fields if their HTML attributes indicate they
+  // are search fields.
   return (name.find(password_manager::constants::kSearch) ==
           std::u16string::npos) &&
          (id.find(password_manager::constants::kSearch) ==
diff --git a/components/pdf/renderer/pdf_accessibility_tree.cc b/components/pdf/renderer/pdf_accessibility_tree.cc
index 3b501cf3..8e569d1 100644
--- a/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -2548,6 +2548,10 @@
           std::erase((*parent_node_iter)->child_ids, ocr_request.image_node_id);
       CHECK_EQ(num_erased, 1);
       (*parent_node_iter)->child_ids.push_back(extracted_text_root_node_id);
+      // Because we now have OCR results, the parenting node can no longer be a
+      // paragraph as OCR's tree contains its own paragraph. A generic
+      // container is equivalent to a div.
+      (*parent_node_iter)->role = ax::mojom::Role::kGenericContainer;
       // Need to keep iterating the rest of `tree_updates`.
       continue;
     }
diff --git a/components/policy/resources/templates/policy_definitions/ContentSettings/GetDisplayMediaSetSelectAllScreensAllowedForUrls.yaml b/components/policy/resources/templates/policy_definitions/ContentSettings/GetDisplayMediaSetSelectAllScreensAllowedForUrls.yaml
index b86e2ff4..f1fa0e9c5 100644
--- a/components/policy/resources/templates/policy_definitions/ContentSettings/GetDisplayMediaSetSelectAllScreensAllowedForUrls.yaml
+++ b/components/policy/resources/templates/policy_definitions/ContentSettings/GetDisplayMediaSetSelectAllScreensAllowedForUrls.yaml
@@ -21,6 +21,6 @@
   type: array
 supported_on:
 - chrome_os:102-
-- chrome.linux:111-
+- chrome.linux:111-123
 tags: []
 type: list
diff --git a/components/policy/resources/templates/policy_definitions/PrivacySandbox/PrivacySandboxIpProtectionEnabled.yaml b/components/policy/resources/templates/policy_definitions/PrivacySandbox/PrivacySandboxIpProtectionEnabled.yaml
index 9a9658f..423f5d4 100644
--- a/components/policy/resources/templates/policy_definitions/PrivacySandbox/PrivacySandboxIpProtectionEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/PrivacySandbox/PrivacySandboxIpProtectionEnabled.yaml
@@ -14,6 +14,9 @@
   per_profile: true
 future_on:
  - fuchsia
+ - chrome.*
+ - chrome_os
+ - android
 items:
 - caption: Disable the <ph name="PRIVACY_SANDBOX_NAME">Privacy Sandbox</ph> IP Protection feature.
   value: false
@@ -26,9 +29,5 @@
 - file://components/ip_protection/OWNERS
 schema:
   type: boolean
-supported_on:
- - chrome.*:124-
- - chrome_os:124-
- - android:124-
 tags: []
 type: main
diff --git a/components/policy/test/data/pref_mapping/GetDisplayMediaSetSelectAllScreensAllowedForUrls.json b/components/policy/test/data/pref_mapping/GetDisplayMediaSetSelectAllScreensAllowedForUrls.json
index 1a7087f6..dd0c8ee 100644
--- a/components/policy/test/data/pref_mapping/GetDisplayMediaSetSelectAllScreensAllowedForUrls.json
+++ b/components/policy/test/data/pref_mapping/GetDisplayMediaSetSelectAllScreensAllowedForUrls.json
@@ -1,8 +1,7 @@
 [
   {
     "os": [
-      "chromeos_ash",
-      "linux"
+      "chromeos_ash"
     ],
     "policy_pref_mapping_tests": [
       {
diff --git a/components/security_interstitials/core/unsafe_resource.cc b/components/security_interstitials/core/unsafe_resource.cc
index 8fed24f..056662d 100644
--- a/components/security_interstitials/core/unsafe_resource.cc
+++ b/components/security_interstitials/core/unsafe_resource.cc
@@ -26,7 +26,7 @@
 UnsafeResource::UnsafeResource()
     : is_subresource(false),
       is_subframe(false),
-      threat_type(safe_browsing::SB_THREAT_TYPE_SAFE),
+      threat_type(safe_browsing::SBThreatType::SB_THREAT_TYPE_SAFE),
       request_destination(network::mojom::RequestDestination::kDocument),
       is_delayed_warning(false),
       should_send_reports(true),
@@ -37,6 +37,7 @@
 UnsafeResource::~UnsafeResource() = default;
 
 bool UnsafeResource::IsMainPageLoadPendingWithSyncCheck() const {
+  using enum safe_browsing::SBThreatType;
   // Subresource hits cannot happen until after main page load is committed.
   if (is_subresource)
     return false;
@@ -44,26 +45,26 @@
   switch (threat_type) {
     // Client-side phishing detection interstitials never block the main
     // frame load, since they happen after the page is finished loading.
-    case safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
+    case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
     // Malicious ad activity reporting happens in the background.
-    case safe_browsing::SB_THREAT_TYPE_BLOCKED_AD_POPUP:
-    case safe_browsing::SB_THREAT_TYPE_BLOCKED_AD_REDIRECT:
+    case SB_THREAT_TYPE_BLOCKED_AD_POPUP:
+    case SB_THREAT_TYPE_BLOCKED_AD_REDIRECT:
     // Ad sampling happens in the background.
-    case safe_browsing::SB_THREAT_TYPE_AD_SAMPLE:
+    case SB_THREAT_TYPE_AD_SAMPLE:
     // Chrome SAVED password reuse warning happens after the page is finished
     // loading.
-    case safe_browsing::SB_THREAT_TYPE_SAVED_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_SAVED_PASSWORD_REUSE:
     // Chrome GAIA signed in and syncing password reuse warning happens after
     // the page is finished loading.
-    case safe_browsing::SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE:
     // Chrome GAIA signed in and non-syncing password reuse warning happens
     // after the page is finished loading.
-    case safe_browsing::SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE:
     // Enterprise password reuse warning happens after the page is finished
     // loading.
-    case safe_browsing::SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE:
     // Suspicious site collection happens in the background
-    case safe_browsing::SB_THREAT_TYPE_SUSPICIOUS_SITE:
+    case SB_THREAT_TYPE_SUSPICIOUS_SITE:
       return false;
 
     default:
diff --git a/components/security_state/ios/security_state_utils.mm b/components/security_state/ios/security_state_utils.mm
index dfa8007..614f8cb 100644
--- a/components/security_state/ios/security_state_utils.mm
+++ b/components/security_state/ios/security_state_utils.mm
@@ -19,6 +19,8 @@
 
 MaliciousContentStatus GetMaliciousContentStatus(
     const web::WebState* web_state) {
+  using enum safe_browsing::SBThreatType;
+
   // There is no known malicious content if there is no allow list or no visible
   // item.
   const SafeBrowsingUrlAllowList* allow_list =
@@ -42,38 +44,37 @@
   // threat type.
   DCHECK(!threats.empty());
   switch (*threats.begin()) {
-    case safe_browsing::SB_THREAT_TYPE_UNUSED:
-    case safe_browsing::SB_THREAT_TYPE_SAFE:
-    case safe_browsing::SB_THREAT_TYPE_URL_PHISHING:
-    case safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
+    case SB_THREAT_TYPE_UNUSED:
+    case SB_THREAT_TYPE_SAFE:
+    case SB_THREAT_TYPE_URL_PHISHING:
+    case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
       return security_state::MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING;
-    case safe_browsing::SB_THREAT_TYPE_URL_MALWARE:
+    case SB_THREAT_TYPE_URL_MALWARE:
       return security_state::MALICIOUS_CONTENT_STATUS_MALWARE;
-    case safe_browsing::SB_THREAT_TYPE_URL_UNWANTED:
+    case SB_THREAT_TYPE_URL_UNWANTED:
       return security_state::MALICIOUS_CONTENT_STATUS_UNWANTED_SOFTWARE;
-    case safe_browsing::SB_THREAT_TYPE_SAVED_PASSWORD_REUSE:
-    case safe_browsing::SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE:
-    case safe_browsing::SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE:
-    case safe_browsing::SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE:
-    case safe_browsing::SB_THREAT_TYPE_BILLING:
+    case SB_THREAT_TYPE_SAVED_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_BILLING:
       return security_state::MALICIOUS_CONTENT_STATUS_BILLING;
-    case safe_browsing::
-        DEPRECATED_SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING:
-    case safe_browsing::DEPRECATED_SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
-    case safe_browsing::SB_THREAT_TYPE_URL_BINARY_MALWARE:
-    case safe_browsing::SB_THREAT_TYPE_EXTENSION:
-    case safe_browsing::SB_THREAT_TYPE_BLOCKLISTED_RESOURCE:
-    case safe_browsing::SB_THREAT_TYPE_API_ABUSE:
-    case safe_browsing::SB_THREAT_TYPE_SUBRESOURCE_FILTER:
-    case safe_browsing::SB_THREAT_TYPE_CSD_ALLOWLIST:
-    case safe_browsing::SB_THREAT_TYPE_AD_SAMPLE:
-    case safe_browsing::SB_THREAT_TYPE_BLOCKED_AD_POPUP:
-    case safe_browsing::SB_THREAT_TYPE_BLOCKED_AD_REDIRECT:
-    case safe_browsing::SB_THREAT_TYPE_SUSPICIOUS_SITE:
-    case safe_browsing::SB_THREAT_TYPE_APK_DOWNLOAD:
-    case safe_browsing::SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST:
-    case safe_browsing::SB_THREAT_TYPE_MANAGED_POLICY_WARN:
-    case safe_browsing::SB_THREAT_TYPE_MANAGED_POLICY_BLOCK:
+    case DEPRECATED_SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING:
+    case DEPRECATED_SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
+    case SB_THREAT_TYPE_URL_BINARY_MALWARE:
+    case SB_THREAT_TYPE_EXTENSION:
+    case SB_THREAT_TYPE_BLOCKLISTED_RESOURCE:
+    case SB_THREAT_TYPE_API_ABUSE:
+    case SB_THREAT_TYPE_SUBRESOURCE_FILTER:
+    case SB_THREAT_TYPE_CSD_ALLOWLIST:
+    case SB_THREAT_TYPE_AD_SAMPLE:
+    case SB_THREAT_TYPE_BLOCKED_AD_POPUP:
+    case SB_THREAT_TYPE_BLOCKED_AD_REDIRECT:
+    case SB_THREAT_TYPE_SUSPICIOUS_SITE:
+    case SB_THREAT_TYPE_APK_DOWNLOAD:
+    case SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST:
+    case SB_THREAT_TYPE_MANAGED_POLICY_WARN:
+    case SB_THREAT_TYPE_MANAGED_POLICY_BLOCK:
       // These threat types are not currently associated with
       // interstitials, and thus resources with these threat types are
       // not ever whitelisted or pending whitelisting.
diff --git a/components/security_state/ios/security_state_utils_unittest.mm b/components/security_state/ios/security_state_utils_unittest.mm
index ea14ef3..ea129d2b 100644
--- a/components/security_state/ios/security_state_utils_unittest.mm
+++ b/components/security_state/ios/security_state_utils_unittest.mm
@@ -27,26 +27,26 @@
 
 // Verifies GetMaliciousContentStatus() return values.
 TEST_F(SecurityStateUtilsTest, GetMaliciousContentStatus) {
+  using enum safe_browsing::SBThreatType;
+
   SafeBrowsingUrlAllowList* allow_list =
       SafeBrowsingUrlAllowList::FromWebState(web_state());
   std::map<security_state::MaliciousContentStatus,
            std::vector<safe_browsing::SBThreatType>>
       threats_types_for_content_statuses = {
           {security_state::MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING,
-           {safe_browsing::SB_THREAT_TYPE_UNUSED,
-            safe_browsing::SB_THREAT_TYPE_SAFE,
-            safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
-            safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING}},
+           {SB_THREAT_TYPE_UNUSED, SB_THREAT_TYPE_SAFE,
+            SB_THREAT_TYPE_URL_PHISHING,
+            SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING}},
           {security_state::MALICIOUS_CONTENT_STATUS_MALWARE,
-           {safe_browsing::SB_THREAT_TYPE_URL_MALWARE}},
+           {SB_THREAT_TYPE_URL_MALWARE}},
           {security_state::MALICIOUS_CONTENT_STATUS_UNWANTED_SOFTWARE,
-           {safe_browsing::SB_THREAT_TYPE_URL_UNWANTED}},
+           {SB_THREAT_TYPE_URL_UNWANTED}},
           {security_state::MALICIOUS_CONTENT_STATUS_BILLING,
-           {safe_browsing::SB_THREAT_TYPE_SAVED_PASSWORD_REUSE,
-            safe_browsing::SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE,
-            safe_browsing::SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE,
-            safe_browsing::SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE,
-            safe_browsing::SB_THREAT_TYPE_BILLING}}};
+           {SB_THREAT_TYPE_SAVED_PASSWORD_REUSE,
+            SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE,
+            SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE,
+            SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE, SB_THREAT_TYPE_BILLING}}};
 
   for (auto& pair : threats_types_for_content_statuses) {
     security_state::MaliciousContentStatus status = pair.first;
@@ -54,7 +54,8 @@
       allow_list->RemovePendingUnsafeNavigationDecisions(url_);
       allow_list->AddPendingUnsafeNavigationDecision(url_, threat);
       EXPECT_EQ(status, security_state::GetMaliciousContentStatus(web_state()))
-          << "Unexpected MaliciousContentStatus for SBThreatType: " << threat;
+          << "Unexpected MaliciousContentStatus for SBThreatType: "
+          << static_cast<int>(threat);
     }
   }
 }
@@ -68,7 +69,7 @@
   SafeBrowsingUrlAllowList* allow_list =
       SafeBrowsingUrlAllowList::FromWebState(web_state());
   allow_list->AddPendingUnsafeNavigationDecision(
-      url_, safe_browsing::SB_THREAT_TYPE_URL_MALWARE);
+      url_, safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_MALWARE);
   EXPECT_EQ(security_state::DANGEROUS,
             security_state::GetSecurityLevelForWebState(web_state()));
 }
diff --git a/components/signin/public/identity_manager/access_token_constants.cc b/components/signin/public/identity_manager/access_token_constants.cc
index a91b39b..63f4a2c 100644
--- a/components/signin/public/identity_manager/access_token_constants.cc
+++ b/components/signin/public/identity_manager/access_token_constants.cc
@@ -90,6 +90,9 @@
       // Required by Omnibox / DocumentSuggestionsService.
       GaiaConstants::kCloudSearchQueryOAuth2Scope,
 
+      // Used internally by the identity system.
+      GaiaConstants::kOAuth1LoginScope,
+
     // Required by ChromeOS only.
 #if BUILDFLAG(IS_CHROMEOS_ASH)
       GaiaConstants::kAssistantOAuth2Scope,
@@ -104,7 +107,6 @@
       GaiaConstants::kCloudPlatformProjectsOAuth2Scope,
       GaiaConstants::kNearbyShareOAuth2Scope,
       GaiaConstants::kNearbyPresenceOAuth2Scope,
-      GaiaConstants::kOAuth1LoginScope,
       GaiaConstants::kPeopleApiReadOnlyOAuth2Scope,
       GaiaConstants::kPhotosOAuth2Scope,
       GaiaConstants::kTachyonOAuthScope,
diff --git a/components/soda/constants.cc b/components/soda/constants.cc
index e8ff4f2..545753c 100644
--- a/components/soda/constants.cc
+++ b/components/soda/constants.cc
@@ -22,8 +22,10 @@
 #include "ui/base/l10n/l10n_util.h"
 
 namespace speech {
-const constexpr char* const kDefaultEnabledLanguages[] = {"fr-FR", "it-IT",
-                                                          "de-DE", "en-US"};
+const constexpr char* const kDefaultEnabledLanguages[] = {
+    "en-US", "fr-FR", "it-IT", "de-DE",       "es-ES",
+    "ja-JP", "hi-IN", "pt-BR", "ko-KR",       "pl-PL",
+    "th-TH", "tr-TR", "id-ID", "cmn-Hans-CN", "cmn-Hant-TW"};
 
 const char kUsEnglishLocale[] = "en-US";
 
diff --git a/components/sync_bookmarks/bookmark_sync_service.cc b/components/sync_bookmarks/bookmark_sync_service.cc
index 8023470..cc48c7f 100644
--- a/components/sync_bookmarks/bookmark_sync_service.cc
+++ b/components/sync_bookmarks/bookmark_sync_service.cc
@@ -42,13 +42,18 @@
 }
 
 bool BookmarkSyncService::IsTrackingMetadata() const {
-  return bookmark_model_type_processor_.IsTrackingMetadata();
+  return bookmark_model_type_processor_.IsTrackingMetadata() ||
+         is_tracking_metadata_for_testing_;
 }
 
 sync_bookmarks::BookmarkModelView* BookmarkSyncService::bookmark_model_view() {
   return bookmark_model_view_.get();
 }
 
+void BookmarkSyncService::SetIsTrackingMetadataForTesting() {
+  is_tracking_metadata_for_testing_ = true;
+}
+
 void BookmarkSyncService::SetBookmarksLimitForTesting(size_t limit) {
   bookmark_model_type_processor_
       .SetMaxBookmarksTillSyncEnabledForTest(  // IN-TEST
diff --git a/components/sync_bookmarks/bookmark_sync_service.h b/components/sync_bookmarks/bookmark_sync_service.h
index 2a42bb3..9ebfd114 100644
--- a/components/sync_bookmarks/bookmark_sync_service.h
+++ b/components/sync_bookmarks/bookmark_sync_service.h
@@ -80,6 +80,7 @@
   sync_bookmarks::BookmarkModelView* bookmark_model_view();
 
   // For integration tests.
+  void SetIsTrackingMetadataForTesting();
   void SetBookmarksLimitForTesting(size_t limit);
 
  private:
@@ -87,6 +88,7 @@
   // BookmarkModelTypeProcessor handles communications between sync engine and
   // BookmarkModel/HistoryService.
   BookmarkModelTypeProcessor bookmark_model_type_processor_;
+  bool is_tracking_metadata_for_testing_ = false;
 };
 
 }  // namespace sync_bookmarks
diff --git a/components/user_education/common/BUILD.gn b/components/user_education/common/BUILD.gn
index e72a81f..31815d0 100644
--- a/components/user_education/common/BUILD.gn
+++ b/components/user_education/common/BUILD.gn
@@ -46,8 +46,6 @@
     "new_badge_specification.h",
     "product_messaging_controller.cc",
     "product_messaging_controller.h",
-    "scoped_new_badge_tracker_base.cc",
-    "scoped_new_badge_tracker_base.h",
     "tutorial.cc",
     "tutorial.h",
     "tutorial_description.cc",
diff --git a/components/user_education/common/new_badge_controller.cc b/components/user_education/common/new_badge_controller.cc
index 6fe7226..fe6bb79a 100644
--- a/components/user_education/common/new_badge_controller.cc
+++ b/components/user_education/common/new_badge_controller.cc
@@ -9,13 +9,18 @@
 
 namespace user_education {
 
+// static
+bool NewBadgeController::disable_new_badges_ = false;
+
 NewBadgeController::NewBadgeController(
     NewBadgeRegistry& registry,
     FeaturePromoStorageService& storage_service,
     std::unique_ptr<NewBadgePolicy> policy)
     : registry_(registry),
       storage_service_(storage_service),
-      policy_(std::move(policy)) {
+      policy_(std::move(policy)) {}
+
+void NewBadgeController::InitData() {
   // Ensure that all registered New Badge features that are enabled have their
   // `feature_enabled_time` set.
   for (const auto& [feature, spec] : registry_->feature_data()) {
@@ -32,6 +37,10 @@
 NewBadgeController::~NewBadgeController() = default;
 
 bool NewBadgeController::MaybeShowNewBadge(const base::Feature& feature) {
+  if (disable_new_badges_) {
+    return false;
+  }
+
   if (!CheckPrerequisites(feature, /*allow_not_registered=*/false)) {
     return false;
   }
@@ -96,4 +105,9 @@
   return true;
 }
 
+// static
+NewBadgeController::TestLock NewBadgeController::DisableNewBadgesForTesting() {
+  return std::make_unique<base::AutoReset<bool>>(&disable_new_badges_, true);
+}
+
 }  // namespace user_education
diff --git a/components/user_education/common/new_badge_controller.h b/components/user_education/common/new_badge_controller.h
index 29ece892..aece445 100644
--- a/components/user_education/common/new_badge_controller.h
+++ b/components/user_education/common/new_badge_controller.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/auto_reset.h"
 #include "base/feature_list.h"
 #include "base/memory/raw_ref.h"
 #include "components/user_education/common/feature_promo_data.h"
@@ -26,6 +27,10 @@
   void operator=(const NewBadgeController&) = delete;
   virtual ~NewBadgeController();
 
+  // Called after registration of "New" Badges to ensure that all data is
+  // consistent.
+  void InitData();
+
   // Call when a UI element that could have a "New" Badge will be shown to the
   // user. Returns true if the badge should be shown. Note that successfully
   // calling this method a number of times will permanently disable the badge,
@@ -41,6 +46,14 @@
   // feature or it is not enabled, does not generate an error.
   void NotifyFeatureUsedIfValid(const base::Feature& feature);
 
+  // Disables "New" Badges for tests - specifically pixel tests, where the
+  // presence of a badge could disrupt the expected image.
+  //
+  // Be sure to store the resulting lock and then release it at the end of the
+  // test; badges are only disabled while the returned object is alive.
+  using TestLock = std::unique_ptr<base::AutoReset<bool>>;
+  [[nodiscard]] static TestLock DisableNewBadgesForTesting();
+
  private:
   void NotifyFeatureUsedImpl(const base::Feature& feature,
                              bool allow_not_registered);
@@ -54,6 +67,7 @@
   const raw_ref<NewBadgeRegistry> registry_;
   const raw_ref<FeaturePromoStorageService> storage_service_;
   const std::unique_ptr<NewBadgePolicy> policy_;
+  static bool disable_new_badges_;
 };
 
 }  // namespace user_education
diff --git a/components/user_education/common/new_badge_controller_unittest.cc b/components/user_education/common/new_badge_controller_unittest.cc
index e162ba6..c655c6c 100644
--- a/components/user_education/common/new_badge_controller_unittest.cc
+++ b/components/user_education/common/new_badge_controller_unittest.cc
@@ -80,6 +80,7 @@
         std::make_unique<TestNewBadgePolicy>(kMaxShows, kMaxUsed, kShowWindow);
     controller_ = std::make_unique<NewBadgeController>(
         registry_, storage_service_, std::move(policy));
+    controller_->InitData();
   }
 
   void CreateWithMockPolicy(bool enable_feature = true) {
@@ -90,6 +91,7 @@
     mock_policy_ = policy.get();
     controller_ = std::make_unique<NewBadgeController>(
         registry_, storage_service_, std::move(policy));
+    controller_->InitData();
   }
 
   void CheckData(const base::Feature& feature, const NewBadgeData& expected) {
@@ -127,6 +129,13 @@
   CheckData(kNewBadgeTestFeature, NewBadgeData{0, 0});
 }
 
+TEST_F(NewBadgeControllerTest, NewBadgesDisabledForTesting) {
+  auto lock = NewBadgeController::DisableNewBadgesForTesting();
+  CreateWithMockPolicy();
+  EXPECT_FALSE(controller_->MaybeShowNewBadge(kNewBadgeTestFeature));
+  CheckData(kNewBadgeTestFeature, NewBadgeData{0, 0});
+}
+
 TEST_F(NewBadgeControllerTest, NotifyFeatureUsed) {
   CreateWithMockPolicy();
   controller_->NotifyFeatureUsed(kNewBadgeTestFeature);
diff --git a/components/user_education/common/scoped_new_badge_tracker_base.cc b/components/user_education/common/scoped_new_badge_tracker_base.cc
deleted file mode 100644
index 20f02974..0000000
--- a/components/user_education/common/scoped_new_badge_tracker_base.cc
+++ /dev/null
@@ -1,58 +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 "components/user_education/common/scoped_new_badge_tracker_base.h"
-
-#include "base/containers/contains.h"
-#include "base/feature_list.h"
-#include "components/feature_engagement/public/tracker.h"
-
-namespace user_education {
-
-ScopedNewBadgeTrackerBase::ScopedNewBadgeTrackerBase(
-    feature_engagement::Tracker* tracker)
-    : tracker_(tracker) {}
-
-// TODO(crbug.com/1258216): When we have the ability to do concurrent FE promos,
-// dismiss all of the badge promos here instead of in TryShowNewBadge().
-ScopedNewBadgeTrackerBase::~ScopedNewBadgeTrackerBase() = default;
-
-bool ScopedNewBadgeTrackerBase::TryShowNewBadge(
-    const base::Feature& badge_feature,
-    const base::Feature* promoted_feature) {
-  // In the event of a submenu that the user could open multiple times while
-  // navigating the same top-level menu, and we don't want to count those as
-  // separate times the user sees the New Badge:
-  if (base::Contains(active_badge_features_, &badge_feature))
-    return true;
-
-  // If there is no tracker available or the feature being promoted is disabled,
-  // do not show the New Badge.
-  if (!tracker_)
-    return false;
-  if (promoted_feature && !base::FeatureList::IsEnabled(*promoted_feature))
-    return false;
-
-  const bool result = tracker_->ShouldTriggerHelpUI(badge_feature);
-  if (result) {
-    active_badge_features_.insert(&badge_feature);
-    // TODO(crbug.com/1258216): Immediately dismiss to work around an issue
-    // where the FE backend disallows concurrent promos; move the call to
-    // Dismiss() to the destructor when concurrency is added.
-    //
-    // Note that "Dismiss" in this case does not dismiss the UI. It's telling
-    // the FE backend that the promo is done so that other promos can run. A
-    // badge showing in a menu should not block e.g. other badges from
-    // displaying (never mind help bubbles).
-    tracker_->Dismissed(badge_feature);
-  }
-  return result;
-}
-
-void ScopedNewBadgeTrackerBase::ActionPerformed(const char* event_name) {
-  if (tracker_)
-    tracker_->NotifyEvent(event_name);
-}
-
-}  // namespace user_education
diff --git a/components/user_education/common/scoped_new_badge_tracker_base.h b/components/user_education/common/scoped_new_badge_tracker_base.h
deleted file mode 100644
index a3815eb29..0000000
--- a/components/user_education/common/scoped_new_badge_tracker_base.h
+++ /dev/null
@@ -1,132 +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 COMPONENTS_USER_EDUCATION_COMMON_SCOPED_NEW_BADGE_TRACKER_BASE_H_
-#define COMPONENTS_USER_EDUCATION_COMMON_SCOPED_NEW_BADGE_TRACKER_BASE_H_
-
-#include <set>
-
-#include "base/feature_list.h"
-#include "base/memory/raw_ptr.h"
-
-namespace feature_engagement {
-class Tracker;
-}
-
-namespace user_education {
-
-// Works with the Feature Engagement system to determine when/how many times a
-// New Badge is displayed to the user. Wraps a feature_engagement::Tracker so
-// the correct calls are made to the Feature Engagement backend.
-//
-// Derive a subclass for a more convenient constructor (e.g. one that takes a
-// Browser or Profile rather than a raw feature_engagement::Tracker).
-//
-// The lifespan of a ScopedNewBadgeTracker should match the time the dialog or
-// menu containing the New Badge is visible to the user.
-//
-// You may use a single tracker for New Badges on multiple features in the same
-// menu or dialog, but make sure the feature flags and event names are distinct.
-//
-// Usage:
-//
-// * Menus
-//
-// Below is an example of using a ScopedNewBadgeTracker to add a New Badge to a
-// menu where the object implementing ui::SimpleMenuModel::Delegate is created
-// each time the menu is shown (e.g. AppMenuModel, TabContextMenuContents,
-// etc.) The case where the delegate object is persistent will be discussed
-// later.
-//
-//   // In OnMenuWillShow(menu):
-//   menu->SetIsNewFeatureAt(
-//       menu->GetIndexOfCommandId(IDC_MY_FEATURE).value(),
-//       new_badge_tracker_.TryShowNewBadge(
-//           feature_engagement::kIPHMyFeatureNewBadge,
-//           &ui_features::kMyFeature));
-//
-//   // In ExecuteCommand():
-//   case IDC_MY_FEATURE:
-//     new_badge_tracker_.EventPerformed("my_feature_activated");
-//     ...
-//     break;
-//
-// If the New Badge is in the top-level menu, you can move the call to
-// SetIsNewFeatureAt() to immediately after the menu model is initialized
-// (typically in the constructor or "Init" method) and you will not have to
-// override OnMenuWillBeShown().
-//
-// If you are handling multiple New Badges for different features, you will
-// want to check the result of GetIndexOfCommand() to make sure the menu being
-// shown is the one that contains the item receiving the new badge.
-//
-// If the ui::SimpleMenuModel::Delegate is a persistent object and is not
-// created each time the menu is displayed, you will need to move the tracker
-// down into the ui::SimpleMenuModel for your menu, and move your code from
-// OnMenuWillShow() to MenuWillShow() and from ExecuteCommand() to
-// ActivatedAt(int, int). Be sure to invoke base class behavior when overriding
-// these methods!
-//
-// * Dialogs
-//
-// Add a ScopedNewBadgeTracker member variable to your DialogDelegateView.
-// Dialogs are typically not re-usable; we create a new DialogDelegateView for
-// each time we show them. If you are following this pattern, include this in
-// your constructor or "Init" function after creating the NewBadgeLabel (but
-// before showing the dialog):
-//
-//   new_badge_label_->SetDisplayNewBadge(
-//       new_badge_tracker_.TryShowNewBadge(
-//           feature_engagement::kIPHMyFeatureNewBadge,
-//           &ui_features::kMyFeature));
-//
-// Then in the callback for the button that activates the feature being
-// promoted, call:
-//
-//   new_badge_tracker_.EventPerformed("my_feature_activated");
-//
-// If for some reason you are re-using a dialog delegate, you must dynamically
-// create and destroy the tracker when the dialog is shown and hidden.
-class ScopedNewBadgeTrackerBase {
- public:
-  // Constructs a scoped tracker for browser with |profile|.
-  explicit ScopedNewBadgeTrackerBase(feature_engagement::Tracker* tracker);
-
-  // This object should be destructed when the New Badge is going away, such as
-  // when a menu with a New Badge or a dialog with a NewBadgeLabel is closing.
-  // If TryShowNewBadge() returned true, the tracker will be informed that the
-  // promo has ended.
-  ~ScopedNewBadgeTrackerBase();
-
-  ScopedNewBadgeTrackerBase(const ScopedNewBadgeTrackerBase& other) = delete;
-  void operator=(const ScopedNewBadgeTrackerBase& other) = delete;
-
-  // Returns whether the New Badge should be shown.
-  //
-  // |badge_feature| is the feature flag for the New Badge itself.
-  //
-  // |promoted_feature|, if specified, is the flag for the feature the New Badge
-  // is promoting. You generally want to specify this feature even if the two
-  // flags are controlled by the same Finch study, because the user could
-  // override one but not the other. This parameter is optional because a New
-  // Badge promo could be shown for a feature without a flag, or for a feature
-  // that has already rolled to 100% and whose flag has been removed.
-  bool TryShowNewBadge(const base::Feature& badge_feature,
-                       const base::Feature* promoted_feature = nullptr);
-
-  // Indicates that the user performed a promoted action. |action_event_name|
-  // should be the value referenced in the "event_used" parameter of your field
-  // trial configuration.
-  // Note: this is a wrapper around feature_engagement::Tracker::NotifyEvent().
-  void ActionPerformed(const char* action_event_name);
-
- private:
-  const raw_ptr<feature_engagement::Tracker> tracker_;
-  std::set<raw_ptr<const base::Feature, SetExperimental>>
-      active_badge_features_;
-};
-
-}  // namespace user_education
-
-#endif  // COMPONENTS_USER_EDUCATION_COMMON_SCOPED_NEW_BADGE_TRACKER_BASE_H_
diff --git a/components/user_education/common/user_education_features.cc b/components/user_education/common/user_education_features.cc
index e7327b6..0a586e2 100644
--- a/components/user_education/common/user_education_features.cc
+++ b/components/user_education/common/user_education_features.cc
@@ -58,6 +58,10 @@
              "UserEducationExperienceVersion2",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kNewBadgeTestFeature,
+             "NewBadgeTestFeature",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 bool IsUserEducationV2() {
   return base::FeatureList::IsEnabled(kUserEducationExperienceVersion2);
 }
diff --git a/components/user_education/common/user_education_features.h b/components/user_education/common/user_education_features.h
index 4c3b6127..6d334a1a 100644
--- a/components/user_education/common/user_education_features.h
+++ b/components/user_education/common/user_education_features.h
@@ -12,6 +12,7 @@
 namespace user_education::features {
 
 BASE_DECLARE_FEATURE(kUserEducationExperienceVersion2);
+BASE_DECLARE_FEATURE(kNewBadgeTestFeature);
 
 // Returns whether User Education Version 2 policies are enabled.
 extern bool IsUserEducationV2();
diff --git a/components/variations/antishuffle_unittest.cc b/components/variations/antishuffle_unittest.cc
index 80cd592b..2b384c7 100644
--- a/components/variations/antishuffle_unittest.cc
+++ b/components/variations/antishuffle_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/variations/entropy_provider.h"
+#include "components/variations/proto/layer.pb.h"
 #include "components/variations/proto/study.pb.h"
 #include "components/variations/proto/variations_seed.pb.h"
 #include "components/variations/variations_layers.h"
@@ -22,68 +23,100 @@
 namespace variations {
 namespace {
 
-// Creates a seed with a variety of permanent consistency studies, with many
-// groups to ensure that they are sensitive to any changes in the group
-// assignment algorithms, which should not change between releases.
-std::unique_ptr<VariationsSeed> ConstructSeed() {
-  auto seed = std::make_unique<VariationsSeed>();
-  {
-    Study* study = seed->add_study();
-    study->set_name("Study_NoSalt");
-    study->set_consistency(Study_Consistency_PERMANENT);
-    for (int i = 0; i < 100; i++) {
-      Study::Experiment* experiment = study->add_experiment();
-      experiment->set_name(base::StringPrintf("group%02d", i));
-      experiment->set_probability_weight(1);
-    }
-  }
+struct TestStudyConfig {
+  std::string_view name;
+  bool add_salt = false;
+  bool add_google_web_experiment_id = false;
+  bool use_limited_entropy = false;
+};
 
-  {
-    Study* study = seed->add_study();
-    study->set_name("Study_WithSalt");
+const int kLayerId = 1;
+const int kLayerMemberId = 101;
+
+const TestStudyConfig kTestStudyConfigs[] = {
+    {.name = "Study_NoSalt"},
+    {.name = "Study_WithSalt", .add_salt = true},
+    {.name = "Study_NoSalt_LowEntropy", .add_google_web_experiment_id = true},
+    {.name = "Study_WithSalt_LowEntropy",
+     .add_salt = true,
+     .add_google_web_experiment_id = true},
+
+    // Limited entropy test configs:
+    {.name = "Study_NoSalt_LimitedEntropy", .use_limited_entropy = true},
+    {.name = "Study_WithSalt_LimitedEntropy",
+     .add_salt = true,
+     .use_limited_entropy = true},
+    {.name = "Study_NoSalt_WithGoogleId_LimitedEntropy",
+     .add_google_web_experiment_id = true,
+     .use_limited_entropy = true},
+    {.name = "Study_WithSalt_WithGoogleId_LimitedEntropy",
+     .add_salt = true,
+     .add_google_web_experiment_id = true,
+     .use_limited_entropy = true},
+};
+
+// Adds a permanently consistent study with many groups to the seed. This
+// ensures that they are sensitive to any changes in the group assignment
+// algorithms, which should not change between releases. The function also
+// applies any settings from `config`.
+void SetupStudy(VariationsSeed* seed, const TestStudyConfig& config) {
+  Study* study = seed->add_study();
+  study->set_name(std::string(config.name));
+  study->set_consistency(Study_Consistency_PERMANENT);
+  if (config.add_salt) {
     study->set_randomization_seed(0x1234);
-    study->set_consistency(Study_Consistency_PERMANENT);
-    for (int i = 0; i < 100; i++) {
-      Study::Experiment* experiment = study->add_experiment();
-      experiment->set_name(base::StringPrintf("group%02d", i));
-      experiment->set_probability_weight(1);
+  }
+
+  for (int i = 0; i < 100; i++) {
+    Study::Experiment* experiment = study->add_experiment();
+    experiment->set_name(base::StringPrintf("group%02d", i));
+    experiment->set_probability_weight(1);
+    if (config.add_google_web_experiment_id) {
+      experiment->set_google_web_experiment_id(i);
     }
   }
 
-  {
-    Study* study = seed->add_study();
-    study->set_name("Study_NoSalt_LowEntropy");
-    study->set_consistency(Study_Consistency_PERMANENT);
-    for (int i = 0; i < 100; i++) {
-      Study::Experiment* experiment = study->add_experiment();
-      experiment->set_name(base::StringPrintf("group%02d", i));
-      experiment->set_google_web_experiment_id(i);
-      experiment->set_probability_weight(1);
-    }
+  if (config.use_limited_entropy) {
+    LayerMemberReference* reference = study->mutable_layer();
+    reference->set_layer_id(kLayerId);
+    reference->set_layer_member_id(kLayerMemberId);
   }
+}
 
-  {
-    Study* study = seed->add_study();
-    study->set_name("Study_WithSalt_LowEntropy");
-    study->set_randomization_seed(0x1234);
-    study->set_consistency(Study_Consistency_PERMANENT);
-    for (int i = 0; i < 100; i++) {
-      Study::Experiment* experiment = study->add_experiment();
-      experiment->set_name(base::StringPrintf("group%02d", i));
-      experiment->set_google_web_experiment_id(i);
-      experiment->set_probability_weight(1);
-    }
+void SetupLimitedLayer(VariationsSeed* seed) {
+  Layer* layer = seed->add_layers();
+  layer->set_id(1);
+  layer->set_num_slots(100);
+  layer->set_entropy_mode(Layer::LIMITED);
+
+  Layer_LayerMember* layer_member = layer->add_members();
+  layer_member->set_id(101);
+  Layer_LayerMember_SlotRange* slot = layer_member->add_slots();
+  slot->set_start(0);
+  slot->set_end(99);
+}
+
+void SetupSeed(VariationsSeed* seed) {
+  bool add_limited_layer = false;
+  for (const TestStudyConfig& config : kTestStudyConfigs) {
+    SetupStudy(seed, config);
+    add_limited_layer |= config.use_limited_entropy;
   }
-  return seed;
+  // This ensures there is only one limited layer in the seed. It is referenced
+  // by all test studies.
+  if (add_limited_layer) {
+    SetupLimitedLayer(seed);
+  }
 }
 
 void ProcessSeed(EntropyProviders&& entropy_providers) {
-  auto seed = ConstructSeed();
+  VariationsSeed seed;
+  SetupSeed(&seed);
   auto client_state = CreateDummyClientFilterableState();
   base::FeatureList feature_list;
-  VariationsLayers layers(*seed, entropy_providers);
+  VariationsLayers layers(seed, entropy_providers);
   VariationsSeedProcessor().CreateTrialsFromSeed(
-      *seed, *client_state, base::BindRepeating(NoopUIStringOverrideCallback),
+      seed, *client_state, base::BindRepeating(NoopUIStringOverrideCallback),
       entropy_providers, layers, &feature_list);
 }
 
@@ -97,13 +130,10 @@
 // behavior clients that disable high entropy randomization, such as clients
 // not opted in to UMA, and clients on platforms like webview where high entropy
 // randomization is not supported.
-// TODO(crbug.com/1518401): add anti-shuffle test for studies constrained to a
-// layer with LIMTIED entropy mode after the randomization logic lands.
 
 TEST(VariationsAntishuffleTest, HighEntropyNil_LowEntropy0) {
-  ProcessSeed(EntropyProviders(
-      "", {0, 8000},
-      /*limited_entropy_randomization_source=*/std::string_view()));
+  ProcessSeed(EntropyProviders("", {0, 8000},
+                               /*limited_entropy_value=*/"not_used"));
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt"), "group87");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_WithSalt"), "group22");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt_LowEntropy"),
@@ -113,9 +143,8 @@
 }
 
 TEST(VariationsAntishuffleTest, HighEntropyId0_LowEntropy0) {
-  ProcessSeed(EntropyProviders(
-      "clientid_0", {0, 8000},
-      /*limited_entropy_randomization_source=*/std::string_view()));
+  ProcessSeed(EntropyProviders("clientid_0", {0, 8000},
+                               /*limited_entropy_value=*/"not_used"));
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt"), "group64");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_WithSalt"), "group15");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt_LowEntropy"),
@@ -125,9 +154,8 @@
 }
 
 TEST(VariationsAntishuffleTest, HighEntropyId1_LowEntropy0) {
-  ProcessSeed(EntropyProviders(
-      "clientid_1", {0, 8000},
-      /*limited_entropy_randomization_source=*/std::string_view()));
+  ProcessSeed(EntropyProviders("clientid_1", {0, 8000},
+                               /*limited_entropy_value=*/"not_used"));
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt"), "group02");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_WithSalt"), "group40");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt_LowEntropy"),
@@ -137,9 +165,8 @@
 }
 
 TEST(VariationsAntishuffleTest, HighEntropyNil_LowEntropy7999) {
-  ProcessSeed(EntropyProviders(
-      "", {7999, 8000},
-      /*limited_entropy_randomization_source=*/std::string_view()));
+  ProcessSeed(EntropyProviders("", {7999, 8000},
+                               /*limited_entropy_value=*/"not_used"));
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt"), "group43");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_WithSalt"), "group48");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt_LowEntropy"),
@@ -149,9 +176,8 @@
 }
 
 TEST(VariationsAntishuffleTest, HighEntropyId0_LowEntropy7999) {
-  ProcessSeed(EntropyProviders(
-      "clientid_0", {7999, 8000},
-      /*limited_entropy_randomization_source=*/std::string_view()));
+  ProcessSeed(EntropyProviders("clientid_0", {7999, 8000},
+                               /*limited_entropy_value=*/"not_used"));
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt"), "group64");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_WithSalt"), "group15");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt_LowEntropy"),
@@ -161,9 +187,8 @@
 }
 
 TEST(VariationsAntishuffleTest, HighEntropyId1_LowEntropy7999) {
-  ProcessSeed(EntropyProviders(
-      "clientid_1", {7999, 8000},
-      /*limited_entropy_randomization_source=*/std::string_view()));
+  ProcessSeed(EntropyProviders("clientid_1", {7999, 8000},
+                               /*limited_entropy_value=*/"not_used"));
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt"), "group02");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_WithSalt"), "group40");
   EXPECT_EQ(base::FieldTrialList::FindFullName("Study_NoSalt_LowEntropy"),
@@ -172,4 +197,59 @@
             "group48");
 }
 
+TEST(VariationsAntishuffleTest, LimitedEntropyNil) {
+  ProcessSeed(EntropyProviders("not_used", {0, 8000},
+                               /*limited_entropy_value=*/std::string_view()));
+  EXPECT_FALSE(
+      base::FieldTrialList::TrialExists("Study_NoSalt_LimitedEntropy"));
+  EXPECT_FALSE(
+      base::FieldTrialList::TrialExists("Study_WithSalt_LimitedEntropy"));
+  EXPECT_FALSE(base::FieldTrialList::TrialExists(
+      "Study_NoSalt_WithGoogleId_LimitedEntropy"));
+  EXPECT_FALSE(base::FieldTrialList::TrialExists(
+      "Study_WithSalt_WithGoogleId_LimitedEntropy"));
+
+  EXPECT_TRUE(base::FieldTrialList::TrialExists("Study_NoSalt"));
+  EXPECT_TRUE(base::FieldTrialList::TrialExists("Study_WithSalt"));
+  EXPECT_TRUE(base::FieldTrialList::TrialExists("Study_NoSalt_LowEntropy"));
+  EXPECT_TRUE(base::FieldTrialList::TrialExists("Study_WithSalt_LowEntropy"));
+}
+
+TEST(VariationsAntishuffleTest, LimitedEntropyRandomizationSource) {
+  struct RandomizationConfig {
+    std::string limited_entropy_value;
+    struct Expectation {
+      std::string study;
+      std::string group;
+    };
+    std::vector<Expectation> expectations;
+  } test_cases[] = {
+      // Group selections of "Study_WithSalt_LimitedEntropy" and
+      // "Study_WithSalt_WithGoogleId_LimitedEntropy" are the same because they
+      // are randomized from the same entropy provider, entropy value, and study
+      // salt.
+      {"limited_0",
+       {{"Study_NoSalt_LimitedEntropy", "group03"},
+        {"Study_WithSalt_LimitedEntropy", "group02"},
+        {"Study_NoSalt_WithGoogleId_LimitedEntropy", "group19"},
+        {"Study_WithSalt_WithGoogleId_LimitedEntropy", "group02"}}},
+
+      {"limited_1",
+       {{"Study_NoSalt_LimitedEntropy", "group63"},
+        {"Study_WithSalt_LimitedEntropy", "group17"},
+        {"Study_NoSalt_WithGoogleId_LimitedEntropy", "group57"},
+        {"Study_WithSalt_WithGoogleId_LimitedEntropy", "group17"}}}};
+
+  for (const auto& test_case : test_cases) {
+    base::test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.InitWithEmptyFeatureAndFieldTrialLists();
+    ProcessSeed(EntropyProviders("not_used", {0, 8000},
+                                 test_case.limited_entropy_value));
+    for (const auto& expectation : test_case.expectations) {
+      EXPECT_EQ(base::FieldTrialList::FindFullName(expectation.study),
+                expectation.group);
+    }
+  }
+}
+
 }  // namespace variations
diff --git a/components/viz/host/gpu_host_impl.cc b/components/viz/host/gpu_host_impl.cc
index 2912ffce..903507a 100644
--- a/components/viz/host/gpu_host_impl.cc
+++ b/components/viz/host/gpu_host_impl.cc
@@ -454,7 +454,8 @@
         }
         break;
       }
-      case gpu::GpuDiskCacheType::kDawnWebGPU: {
+      case gpu::GpuDiskCacheType::kDawnWebGPU:
+      case gpu::GpuDiskCacheType::kDawnGraphite: {
         gpu_service_remote_->LoadedBlob(handle, key, data);
         break;
       }
@@ -689,7 +690,8 @@
         cache->Cache(base::StrCat({prefix, ":", key}), blob);
         break;
       }
-      case gpu::GpuDiskCacheType::kDawnWebGPU: {
+      case gpu::GpuDiskCacheType::kDawnWebGPU:
+      case gpu::GpuDiskCacheType::kDawnGraphite: {
         cache->Cache(key, blob);
         break;
       }
diff --git a/components/webapps/browser/android/add_to_homescreen_mediator.cc b/components/webapps/browser/android/add_to_homescreen_mediator.cc
index a81d5e8..7888134 100644
--- a/components/webapps/browser/android/add_to_homescreen_mediator.cc
+++ b/components/webapps/browser/android/add_to_homescreen_mediator.cc
@@ -13,6 +13,7 @@
 #include "components/webapps/browser/android/app_banner_manager_android.h"
 #include "components/webapps/browser/android/webapps_jni_headers/AddToHomescreenMediator_jni.h"
 #include "components/webapps/browser/banners/app_banner_metrics.h"
+#include "components/webapps/browser/features.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "components/webapps/browser/webapps_client.h"
 #include "content/public/browser/web_contents.h"
@@ -121,6 +122,13 @@
         base::android::ConvertJavaStringToUTF16(env, j_user_title);
     params_->shortcut_info->has_custom_title = true;
   }
+
+  // Shortcuts always open in a browser tab.
+  if (base::FeatureList::IsEnabled(features::kPwaUniversalInstallUi) &&
+      params_->app_type == AppType::SHORTCUT) {
+    params_->shortcut_info->display = blink::mojom::DisplayMode::kBrowser;
+  }
+
   if (params_->app_type == AppType::WEBAPK ||
       params_->app_type == AppType::WEBAPK_DIY) {
     AppBannerManagerAndroid* app_banner_manager =
diff --git a/components/webapps/browser/features.cc b/components/webapps/browser/features.cc
index 1f769810..4126818 100644
--- a/components/webapps/browser/features.cc
+++ b/components/webapps/browser/features.cc
@@ -34,6 +34,10 @@
     kInstallPromptGlobalGuardrails_IgnorePeriod{&kInstallPromptGlobalGuardrails,
                                                 "ignore_period", base::Days(3)};
 
+BASE_FEATURE(kPwaUniversalInstallUi,
+             "PwaUniversalInstallUi",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enables WebAPK Install Failure Notification.
 BASE_FEATURE(kWebApkInstallFailureNotification,
              "WebApkInstallFailureNotification",
diff --git a/components/webapps/browser/features.h b/components/webapps/browser/features.h
index a7ee4b7..dccdc1c 100644
--- a/components/webapps/browser/features.h
+++ b/components/webapps/browser/features.h
@@ -39,6 +39,9 @@
 extern const base::FeatureParam<int> kInstallPromptGlobalGuardrails_IgnoreCount;
 extern const base::FeatureParam<base::TimeDelta>
     kInstallPromptGlobalGuardrails_IgnorePeriod;
+
+BASE_DECLARE_FEATURE(kPwaUniversalInstallUi);
+
 BASE_DECLARE_FEATURE(kWebApkInstallFailureNotification);
 #endif  // BUILDFLAG(IS_ANDROID)
 
diff --git a/content/browser/accessibility/accessibility_tree_snapshot_combiner.cc b/content/browser/accessibility/accessibility_tree_snapshot_combiner.cc
index f4685a8..21160fb5 100644
--- a/content/browser/accessibility/accessibility_tree_snapshot_combiner.cc
+++ b/content/browser/accessibility/accessibility_tree_snapshot_combiner.cc
@@ -10,17 +10,18 @@
 AccessibilityTreeSnapshotCombiner::AccessibilityTreeSnapshotCombiner(
     base::OnceCallback<void(const ui::AXTreeUpdate&)> callback,
     mojom::SnapshotAccessibilityTreeParamsPtr params)
-    : callback_(std::move(callback)),
-      params_(std::move(params)),
-      weak_ptr_factory_(this) {}
+    : callback_(std::move(callback)), params_(std::move(params)) {}
 
 void AccessibilityTreeSnapshotCombiner::RequestSnapshotOnRenderFrameHost(
     RenderFrameHostImpl* rfhi) {
+  // The callback creates a reference to this, which is needed to keep the
+  // object alive using base::RefCounted. Once all frames have received their
+  // responses from the renderer and run their respective callbacks, all
+  // references to this will be removed and the destructor will be called.
   rfhi->RequestAXTreeSnapshot(
       base::BindOnce(&AccessibilityTreeSnapshotCombiner::
                          ReceiveSnapshotFromRenderFrameHost,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     rfhi->AccessibilityIsRootFrame()),
+                     this, rfhi->AccessibilityIsRootFrame()),
       params_.Clone());
 }
 
diff --git a/content/browser/accessibility/accessibility_tree_snapshot_combiner.h b/content/browser/accessibility/accessibility_tree_snapshot_combiner.h
index 6e8f0fc2..5108407 100644
--- a/content/browser/accessibility/accessibility_tree_snapshot_combiner.h
+++ b/content/browser/accessibility/accessibility_tree_snapshot_combiner.h
@@ -42,8 +42,6 @@
   ui::AXTreeCombiner combiner_;
   base::OnceCallback<void(const ui::AXTreeUpdate&)> callback_;
   mojom::SnapshotAccessibilityTreeParamsPtr params_;
-
-  base::WeakPtrFactory<AccessibilityTreeSnapshotCombiner> weak_ptr_factory_;
 };
 
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 262f226..b452a65 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -346,7 +346,6 @@
     case ui::AXEventGenerator::Event::CARET_BOUNDS_CHANGED:
     case ui::AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::CHILDREN_CHANGED:
-    case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED:
     case ui::AXEventGenerator::Event::COLLAPSED:
     case ui::AXEventGenerator::Event::CONTROLS_CHANGED:
     case ui::AXEventGenerator::Event::DETAILS_CHANGED:
@@ -626,4 +625,9 @@
   return wcax->GenerateAccessibilityNodeInfoString(unique_id);
 }
 
+std::vector<std::string>
+BrowserAccessibilityManagerAndroid::GetMetadataForTree() const {
+  return GetTreeData().metadata;
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.h b/content/browser/accessibility/browser_accessibility_manager_android.h
index b64291fd..b0227f5e 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.h
+++ b/content/browser/accessibility/browser_accessibility_manager_android.h
@@ -127,6 +127,8 @@
 
   std::u16string GenerateAccessibilityNodeInfoString(int32_t unique_id);
 
+  std::vector<std::string> GetMetadataForTree() const;
+
  private:
   // AXTreeObserver overrides.
   void OnAtomicUpdateFinished(
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
index 165b5a3..465ae28 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
@@ -356,7 +356,6 @@
     case ui::AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::CHILDREN_CHANGED:
     case ui::AXEventGenerator::Event::CONTROLS_CHANGED:
-    case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED:
     case ui::AXEventGenerator::Event::DETAILS_CHANGED:
     case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED:
     case ui::AXEventGenerator::Event::DROPEFFECT_CHANGED:
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index 8a2227aa..e9e46e9 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -378,7 +378,6 @@
     case ui::AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::CHILDREN_CHANGED:
     case ui::AXEventGenerator::Event::CONTROLS_CHANGED:
-    case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED:
     case ui::AXEventGenerator::Event::DETAILS_CHANGED:
     case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED:
     case ui::AXEventGenerator::Event::DESCRIPTION_CHANGED:
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc
index c2c057b7..5da6f968 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -227,9 +227,6 @@
       }
       break;
     }
-    case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED:
-      FireUiaPropertyChangedEvent(UIA_ClassNamePropertyId, wrapper);
-      break;
     case ui::AXEventGenerator::Event::COLLAPSED:
     case ui::AXEventGenerator::Event::EXPANDED:
       FireUiaPropertyChangedEvent(
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index e2d3379..33baf0e 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -705,214 +705,6 @@
   return count;
 }
 
-// -----------------------------------------------
-// [EXPERIMENTAL] These methods should only be called when the JNI experiment is
-// running. We unfortunately need to keep these separate since the JNI method
-// signatures do not align.
-
-void WebContentsAccessibilityAndroid::UpdateAccessibilityNodeInfoBoundsRect_exp(
-    JNIEnv* env,
-    const ScopedJavaLocalRef<jobject>& obj,
-    jint unique_id,
-    BrowserAccessibilityAndroid* node) {
-  BrowserAccessibilityManagerAndroid* root_manager =
-      GetRootBrowserAccessibilityManager();
-  if (!root_manager) {
-    return;
-  }
-
-  ui::AXOffscreenResult offscreen_result = ui::AXOffscreenResult::kOnscreen;
-  float dip_scale = 1 / root_manager->device_scale_factor();
-  gfx::Rect absolute_rect = gfx::ScaleToEnclosingRect(
-      node->GetUnclippedRootFrameBoundsRect(&offscreen_result), dip_scale,
-      dip_scale);
-  gfx::Rect parent_relative_rect = absolute_rect;
-  if (node->PlatformGetParent()) {
-    gfx::Rect parent_rect = gfx::ScaleToEnclosingRect(
-        node->PlatformGetParent()->GetUnclippedRootFrameBoundsRect(), dip_scale,
-        dip_scale);
-    parent_relative_rect.Offset(-parent_rect.OffsetFromOrigin());
-  }
-  bool is_offscreen = offscreen_result == ui::AXOffscreenResult::kOffscreen;
-
-  Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoLocation_exp(
-      env, obj, unique_id, absolute_rect.x(), absolute_rect.y(),
-      parent_relative_rect.x(), parent_relative_rect.y(), absolute_rect.width(),
-      absolute_rect.height(), is_offscreen);
-}
-
-jboolean WebContentsAccessibilityAndroid::UpdateCachedAccessibilityNodeInfo_exp(
-    JNIEnv* env,
-    jint unique_id) {
-  BrowserAccessibilityManagerAndroid* root_manager =
-      GetRootBrowserAccessibilityManager();
-  if (!root_manager) {
-    return false;
-  }
-
-  BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
-  if (!node) {
-    return false;
-  }
-
-  ScopedJavaLocalRef<jobject> obj = java_anib_ref_.get(env);
-  if (obj.is_null()) {
-    return false;
-  }
-
-  // Update cached nodes by providing new enclosing Rects
-  UpdateAccessibilityNodeInfoBoundsRect_exp(env, obj, unique_id, node);
-
-  return true;
-}
-
-jboolean WebContentsAccessibilityAndroid::PopulateAccessibilityNodeInfo_exp(
-    JNIEnv* env,
-    jint unique_id) {
-  if (!GetRootBrowserAccessibilityManager()) {
-    return false;
-  }
-
-  BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
-  if (!node) {
-    return false;
-  }
-
-  ScopedJavaLocalRef<jobject> obj = java_anib_ref_.get(env);
-  if (obj.is_null()) {
-    return false;
-  }
-
-  int parent_id = ui::kAXAndroidInvalidViewId;
-  auto* parent_node =
-      static_cast<BrowserAccessibilityAndroid*>(node->PlatformGetParent());
-  if (parent_node) {
-    parent_id = parent_node->unique_id();
-  }
-
-  // Build a vector of child ids
-  std::vector<int> child_ids;
-  for (const auto& child : node->PlatformChildren()) {
-    const auto& android_node =
-        static_cast<const BrowserAccessibilityAndroid&>(child);
-    child_ids.push_back(android_node.unique_id());
-  }
-  if (child_ids.size()) {
-    Java_AccessibilityNodeInfoBuilder_addAccessibilityNodeInfoChildren_exp(
-        env, obj, unique_id, base::android::ToJavaIntArray(env, child_ids));
-  }
-
-  Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoBooleanAttributes_exp(
-      env, obj, unique_id, node->IsReportingCheckable(), node->IsChecked(),
-      node->IsClickable(), node->IsContentInvalid(), node->IsEnabled(),
-      node->IsFocusable(), node->IsFocused(), node->HasImage(),
-      node->IsPasswordField(), node->IsScrollable(), node->IsSelected(),
-      node->IsVisibleToUser(), node->HasCharacterLocations());
-
-  Java_AccessibilityNodeInfoBuilder_addAccessibilityNodeInfoActions_exp(
-      env, obj, unique_id, node->CanScrollForward(), node->CanScrollBackward(),
-      node->CanScrollUp(), node->CanScrollDown(), node->CanScrollLeft(),
-      node->CanScrollRight(), node->IsClickable(), node->IsTextField(),
-      node->IsEnabled(), node->IsFocusable(), node->IsFocused(),
-      node->IsCollapsed(), node->IsExpanded(), node->HasNonEmptyValue(),
-      !node->GetTextContentUTF16().empty(), node->IsSeekControl(),
-      node->IsFormDescendant());
-
-  Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoBaseAttributes_exp(
-      env, obj, unique_id, parent_id,
-      GetCanonicalJNIString(env, node->GetClassName()),
-      GetCanonicalJNIString(env, node->GetRoleString()),
-      GetCanonicalJNIString(env, node->GetRoleDescription()),
-      base::android::ConvertUTF16ToJavaString(env, node->GetHint()),
-      base::android::ConvertUTF16ToJavaString(env, node->GetTargetUrl()),
-      node->CanOpenPopup(), node->IsMultiLine(), node->AndroidInputType(),
-      node->AndroidLiveRegionType(),
-      GetCanonicalJNIString(env, node->GetContentInvalidErrorMessage()),
-      node->ClickableScore(), GetCanonicalJNIString(env, node->GetCSSDisplay()),
-      base::android::ConvertUTF16ToJavaString(env, node->GetBrailleLabel()),
-      GetCanonicalJNIString(env, node->GetBrailleRoleDescription()));
-
-  ScopedJavaLocalRef<jintArray> suggestion_starts_java;
-  ScopedJavaLocalRef<jintArray> suggestion_ends_java;
-  ScopedJavaLocalRef<jobjectArray> suggestion_text_java;
-  std::vector<int> suggestion_starts;
-  std::vector<int> suggestion_ends;
-  node->GetSuggestions(&suggestion_starts, &suggestion_ends);
-  if (suggestion_starts.size() && suggestion_ends.size()) {
-    suggestion_starts_java = base::android::ToJavaIntArray(
-        env, suggestion_starts.data(), suggestion_starts.size());
-    suggestion_ends_java = base::android::ToJavaIntArray(
-        env, suggestion_ends.data(), suggestion_ends.size());
-
-    // Currently we don't retrieve the text of each suggestion, so
-    // store a blank string for now.
-    std::vector<std::string> suggestion_text(suggestion_starts.size());
-    suggestion_text_java =
-        base::android::ToJavaArrayOfStrings(env, suggestion_text);
-  }
-
-  bool is_link = ui::IsLink(node->GetRole());
-  Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoText_exp(
-      env, obj, unique_id,
-      base::android::ConvertUTF16ToJavaString(env, node->GetTextContentUTF16()),
-      is_link
-          ? base::android::ConvertUTF16ToJavaString(env, node->GetTargetUrl())
-          : base::android::ConvertUTF16ToJavaString(env, std::u16string()),
-      is_link, node->IsTextField(),
-      GetCanonicalJNIString(env, node->GetInheritedString16Attribute(
-                                     ax::mojom::StringAttribute::kLanguage)),
-      suggestion_starts_java, suggestion_ends_java, suggestion_text_java,
-      base::android::ConvertUTF16ToJavaString(env,
-                                              node->GetStateDescription()));
-
-  std::u16string element_id;
-  if (node->GetHtmlAttribute("id", &element_id)) {
-    Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoViewIdResourceName_exp(
-        env, obj, unique_id,
-        base::android::ConvertUTF16ToJavaString(env, element_id));
-  }
-
-  UpdateAccessibilityNodeInfoBoundsRect_exp(env, obj, unique_id, node);
-
-  if (node->IsCollection()) {
-    Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoCollectionInfo_exp(
-        env, obj, unique_id, node->RowCount(), node->ColumnCount(),
-        node->IsHierarchical());
-  }
-  if (node->IsCollectionItem() || node->IsTableHeader()) {
-    Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoCollectionItemInfo_exp(
-        env, obj, unique_id, node->RowIndex(), node->RowSpan(),
-        node->ColumnIndex(), node->ColumnSpan(), node->IsTableHeader());
-  }
-
-  // For sliders that are numeric, use the AccessibilityNodeInfo.RangeInfo
-  // object as expected. But for non-numeric ranges (e.g. "small", "medium",
-  // "large"), do not set the RangeInfo object and instead rely on announcing
-  // the aria-valuetext value, which will be included in the node's text value.
-  if (node->IsRangeControlWithoutAriaValueText()) {
-    Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoRangeInfo_exp(
-        env, obj, unique_id, node->AndroidRangeType(), node->RangeMin(),
-        node->RangeMax(), node->RangeCurrentValue());
-  }
-
-  if (ui::IsDialog(node->GetRole())) {
-    Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoPaneTitle_exp(
-        env, obj, unique_id,
-        base::android::ConvertUTF16ToJavaString(
-            env, node->GetDialogModalMessageText()));
-  }
-
-  if (node->IsTextField()) {
-    Java_AccessibilityNodeInfoBuilder_setAccessibilityNodeInfoSelectionAttrs_exp(
-        env, obj, unique_id, node->GetSelectionStart(),
-        node->GetSelectionEnd());
-  }
-
-  return true;
-}
-
-// -----------------------------------------------
-
 void WebContentsAccessibilityAndroid::UpdateAccessibilityNodeInfoBoundsRect(
     JNIEnv* env,
     const ScopedJavaLocalRef<jobject>& obj,
@@ -1740,17 +1532,12 @@
   ScopedJavaLocalRef<jobject> obj = java_adb_ref_.get(env);
   CHECK(obj);
 
-  // Construct a root manager without a delegate if one does not already exist.
-  // In some situations (e.g. unit tests) the full accessibility engine may
-  // already be running and we do not need to create a new manager.
-  if (!GetRootBrowserAccessibilityManager()) {
-    snapshot_root_manager_ =
-        std::make_unique<BrowserAccessibilityManagerAndroid>(
-            result, GetWeakPtr(), /* delegate= */ nullptr);
-  }
+  // Construct a root manager without a delegate using the snapshot result.
+  snapshot_root_manager_ = std::make_unique<BrowserAccessibilityManagerAndroid>(
+      result, GetWeakPtr(), /* delegate= */ nullptr);
 
   auto* root = static_cast<BrowserAccessibilityAndroid*>(
-      GetRootBrowserAccessibilityManager()->GetBrowserAccessibilityRoot());
+      snapshot_root_manager_->GetBrowserAccessibilityRoot());
   CHECK(root);
 
   // Construct the Java-side tree, use the JNI builder `java_adb_ref_` to
@@ -1760,8 +1547,13 @@
                                        /* is_root= */ true);
 
   // Add tree-level (root only) data to Java-side tree (e.g. HTML metadata).
-  Java_AssistDataBuilder_populateHTMLMetadataProperties(env, obj,
-                                                        view_structure_root);
+  const auto& metadata_strings =
+      GetRootBrowserAccessibilityManager()->GetMetadataForTree();
+  if (!metadata_strings.empty()) {
+    Java_AssistDataBuilder_populateHTMLMetadataProperties(
+        env, obj, view_structure_root,
+        base::android::ToJavaArrayOfStrings(env, metadata_strings));
+  }
 
   // We have fulfilled the request for an accessibility tree snapshot, so we can
   // now call the provided Java-side callback to inform original client that the
diff --git a/content/browser/accessibility/web_contents_accessibility_android.h b/content/browser/accessibility/web_contents_accessibility_android.h
index ed56e2b3..5d95a191 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.h
+++ b/content/browser/accessibility/web_contents_accessibility_android.h
@@ -150,15 +150,6 @@
       JNIEnv* env,
       jint unique_id);
 
-  // This block of methods is experimental.
-  jboolean UpdateCachedAccessibilityNodeInfo_exp(JNIEnv* env, jint id);
-  jboolean PopulateAccessibilityNodeInfo_exp(JNIEnv* env, jint id);
-  void UpdateAccessibilityNodeInfoBoundsRect_exp(
-      JNIEnv* env,
-      const base::android::ScopedJavaLocalRef<jobject>& obj,
-      jint id,
-      BrowserAccessibilityAndroid* node);
-
   // Populate Java accessibility data structures with info about a node.
   jboolean UpdateCachedAccessibilityNodeInfo(
       JNIEnv* env,
diff --git a/content/browser/android/content_feature_map.cc b/content/browser/android/content_feature_map.cc
index 3fd4243..b1cb698b 100644
--- a/content/browser/android/content_feature_map.cc
+++ b/content/browser/android/content_feature_map.cc
@@ -22,7 +22,6 @@
     &blink::features::kStylusPointerAdjustment,
     &blink::features::kStylusRichGestures,
     &features::kAccessibilityIncludeLongClickAction,
-    &features::kAccessibilityJNIOptimizations,
     &features::kAccessibilityPageZoom,
     &features::kAccessibilityPageZoomEnhancements,
     &features::kAccessibilityUnifiedSnapshots,
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index c0a1a61..82fff11 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -35,6 +35,7 @@
 #include "base/values.h"
 #include "components/attribution_reporting/features.h"
 #include "components/attribution_reporting/os_registration.h"
+#include "components/attribution_reporting/os_registration_error.mojom.h"
 #include "components/attribution_reporting/registrar.h"
 #include "components/attribution_reporting/registrar_info.h"
 #include "components/attribution_reporting/registration_eligibility.mojom.h"
@@ -89,6 +90,7 @@
 using ::attribution_reporting::IssueType;
 using ::attribution_reporting::Registrar;
 using ::attribution_reporting::SuitableOrigin;
+using ::attribution_reporting::mojom::OsRegistrationError;
 using ::attribution_reporting::mojom::RegistrationEligibility;
 using ::attribution_reporting::mojom::RegistrationType;
 using ::attribution_reporting::mojom::SourceRegistrationError;
@@ -182,8 +184,9 @@
 }
 
 bool BackgroundRegistrationsEnabled() {
-  return base::FeatureList::IsEnabled(
-             blink::features::kKeepAliveInBrowserMigration) &&
+  return (base::FeatureList::IsEnabled(
+              blink::features::kKeepAliveInBrowserMigration) ||
+          base::FeatureList::IsEnabled(blink::features::kFetchLaterAPI)) &&
          base::FeatureList::IsEnabled(
              blink::features::kAttributionReportingInBrowserMigration);
 }
@@ -2044,23 +2047,28 @@
 
   DCHECK(!registrations->pending_os_decodes().empty());
   {
-    std::vector<attribution_reporting::OsRegistrationItem> registration_items;
-    if (result.has_value()) {
-      registration_items =
-          attribution_reporting::ParseOsSourceOrTriggerHeader(*result);
-    }
+    auto registration_items =
+        [&]() -> base::expected<
+                  std::vector<attribution_reporting::OsRegistrationItem>,
+                  OsRegistrationError> {
+      if (!result.has_value()) {
+        return base::unexpected(OsRegistrationError::kInvalidList);
+      }
 
-    if (!registration_items.empty()) {
+      return attribution_reporting::ParseOsSourceOrTriggerHeader(*result);
+    }();
+
+    if (registration_items.has_value()) {
       if (registrations->navigation_id().has_value()) {
         RecordRegistrationMethod(
             registrations->context().registration_method());
         MaybeBufferOsRegistrations(*registrations->navigation_id(),
-                                   std::move(registration_items),
+                                   std::move(registration_items.value()),
                                    registrations->context());
       } else {
         RecordRegistrationMethod(
             registrations->context().registration_method());
-        SubmitOsRegistrations(std::move(registration_items),
+        SubmitOsRegistrations(std::move(registration_items.value()),
                               registrations->context(), registration_type);
       }
     } else {
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index 95a94cc..0ffb4ad 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -1830,7 +1830,7 @@
   // Wait for parsing to finish.
   task_environment_.FastForwardBy(base::TimeDelta());
 
-  histograms.ExpectUniqueSample("Conversions.SourceRegistrationError12",
+  histograms.ExpectUniqueSample("Conversions.SourceRegistrationError13",
                                 SourceRegistrationError::kInvalidJson, 1);
 }
 
@@ -4274,7 +4274,7 @@
     data_host_manager_.NotifyBackgroundRegistrationCompleted(kBackgroundId);
 
     task_environment_.FastForwardBy(base::TimeDelta());
-    histograms.ExpectUniqueSample("Conversions.TriggerRegistrationError10",
+    histograms.ExpectUniqueSample("Conversions.TriggerRegistrationError11",
                                   TriggerRegistrationError::kInvalidJson, 1);
   }
 }
diff --git a/content/browser/attribution_reporting/attribution_interop_parser.cc b/content/browser/attribution_reporting/attribution_interop_parser.cc
index 3f1d69d..603fa316 100644
--- a/content/browser/attribution_reporting/attribution_interop_parser.cc
+++ b/content/browser/attribution_reporting/attribution_interop_parser.cc
@@ -14,9 +14,12 @@
 #include <utility>
 #include <vector>
 
+#include "base/check.h"
 #include "base/functional/function_ref.h"
 #include "base/functional/overloaded.h"
+#include "base/json/json_writer.h"
 #include "base/memory/raw_ref.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
@@ -28,8 +31,8 @@
 #include "components/attribution_reporting/suitable_origin.h"
 #include "components/attribution_reporting/test_utils.h"
 #include "content/browser/attribution_reporting/attribution_config.h"
-#include "content/browser/attribution_reporting/attribution_constants.h"
-#include "content/browser/attribution_reporting/attribution_reporting.mojom.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_version.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace content {
@@ -37,7 +40,6 @@
 namespace {
 
 using ::attribution_reporting::SuitableOrigin;
-using ::attribution_reporting::mojom::RegistrationType;
 using ::attribution_reporting::mojom::SourceType;
 
 constexpr char kAttributionSrcUrlKey[] = "attribution_src_url";
@@ -49,12 +51,6 @@
 constexpr char kResponseKey[] = "response";
 constexpr char kResponsesKey[] = "responses";
 constexpr char kSourceTypeKey[] = "source_type";
-constexpr char kTimeKey[] = "time";
-constexpr char kTypeKey[] = "type";
-constexpr char kUnparsableRegistrationsKey[] = "unparsable_registrations";
-
-constexpr char kSource[] = "source";
-constexpr char kTrigger[] = "trigger";
 
 using Context = absl::variant<base::StringPiece, size_t>;
 using ContextPath = std::vector<Context>;
@@ -163,16 +159,6 @@
                        });
     }
 
-    {
-      std::optional<base::Value> regs =
-          dict.Extract(kUnparsableRegistrationsKey);
-      auto context = PushContext(kUnparsableRegistrationsKey);
-      ParseListOfDicts(base::OptionalToPtr(regs), [&](base::Value::Dict reg) {
-        ParseUnparsableRegistration(std::move(reg),
-                                    output.unparsable_registrations);
-      });
-    }
-
     CheckUnknown(dict);
 
     if (has_error_) {
@@ -348,8 +334,6 @@
       return;
     }
 
-    const char* source_type_error = nullptr;
-
     {
       auto context = PushContext(kResponsesKey);
       ParseListOfDicts(
@@ -365,63 +349,34 @@
 
             ParseDict(
                 response, kResponseKey, [&](base::Value::Dict response_dict) {
-                  std::optional<base::Value> source = response_dict.Extract(
-                      kAttributionReportingRegisterSourceHeader);
+                  net::HttpResponseHeaders::Builder builder(
+                      net::HttpVersion(1, 1),
+                      /*status=*/"200 OK");
 
-                  std::optional<base::Value> trigger = response_dict.Extract(
-                      kAttributionReportingRegisterTriggerHeader);
-
-                  if (source.has_value() == trigger.has_value()) {
-                    *Error() << "must contain either source or trigger";
-                    return;
-                  }
-
-                  if (source.has_value() && !source_type.has_value()) {
-                    source_type_error =
-                        "must be present for source registration";
-                    return;
-                  }
-
-                  if (trigger.has_value() && source_type.has_value()) {
-                    source_type_error =
-                        "must not be present for trigger registration";
-                    return;
-                  }
-
-                  std::string info_header;
-                  std::optional<base::Value> info_value =
-                      response_dict.Extract(kAttributionReportingInfoHeader);
-                  if (info_value.has_value()) {
-                    if (std::string* info_str = info_value->GetIfString()) {
-                      info_header = std::move(*info_str);
+                  for (auto [header, value] : response_dict) {
+                    if (const std::string* str = value.GetIfString()) {
+                      builder.AddHeader(header, *str);
                     } else {
-                      auto infoContext =
-                          PushContext(kAttributionReportingInfoHeader);
-                      *Error() << "must be a string";
-                      return;
+                      std::optional<std::string> json = base::WriteJson(value);
+                      CHECK(json.has_value());
+                      // The string must outlive the call to
+                      // `net::HttpResponseHeaders::Build()`, so put it back in
+                      // the dict.
+                      value = base::Value(std::move(*json));
+                      builder.AddHeader(header, value.GetString());
                     }
                   }
 
                   auto& event = events.emplace_back(
                       std::move(*reporting_origin), std::move(*context_origin));
                   event.source_type = source_type;
-                  event.registration = source.has_value() ? std::move(*source)
-                                                          : std::move(*trigger);
+                  event.response_headers = builder.Build();
                   event.time = time;
                   event.debug_permission = debug_permission;
-                  event.info_header = std::move(info_header);
                 });
           },
           /*expected_size=*/1);
     }
-
-    if (source_type_error) {
-      auto outer = PushContext(kRegistrationRequestKey);
-      {
-        auto inner = PushContext(kSourceTypeKey);
-        *Error() << source_type_error;
-      }
-    }
   }
 
   void ParseReport(base::Value::Dict dict,
@@ -458,47 +413,6 @@
     }
   }
 
-  void ParseUnparsableRegistration(
-      base::Value::Dict dict,
-      std::vector<AttributionInteropOutput::UnparsableRegistration>&
-          unparsable_registrations) {
-    AttributionInteropOutput::UnparsableRegistration reg;
-
-    reg.time = ParseTime(dict, kTimeKey,
-                         /*previous_time=*/unparsable_registrations.empty()
-                             ? base::Time::Min()
-                             : unparsable_registrations.back().time,
-                         /*strictly_greater=*/false);
-    dict.Remove(kTimeKey);
-
-    {
-      std::optional<base::Value> type = dict.Extract(kTypeKey);
-      bool ok = false;
-
-      if (const std::string* str = type ? type->GetIfString() : nullptr) {
-        if (*str == kSource) {
-          reg.type = RegistrationType::kSource;
-          ok = true;
-        } else if (*str == kTrigger) {
-          reg.type = RegistrationType::kTrigger;
-          ok = true;
-        }
-      }
-
-      if (!ok) {
-        auto context = PushContext(kTypeKey);
-        *Error() << "must be either \"" << kSource << "\" or \"" << kTrigger
-                 << "\"";
-      }
-    }
-
-    CheckUnknown(dict);
-
-    if (!has_error_) {
-      unparsable_registrations.push_back(std::move(reg));
-    }
-  }
-
   void CheckUnknown(const base::Value::Dict& dict) {
     for (auto [key, value] : dict) {
       auto context = PushContext(key);
@@ -765,38 +679,13 @@
       .Set(kPayloadKey, payload.Clone());
 }
 
-base::Value::Dict AttributionInteropOutput::UnparsableRegistration::ToJson()
-    const {
-  const char* type_str;
-  switch (type) {
-    case RegistrationType::kSource:
-      type_str = kSource;
-      break;
-    case RegistrationType::kTrigger:
-      type_str = kTrigger;
-      break;
-  }
-
-  return base::Value::Dict()
-      .Set(kTimeKey, TimeAsUnixMillisecondString(time))
-      .Set(kTypeKey, type_str);
-}
-
 base::Value::Dict AttributionInteropOutput::ToJson() const {
   base::Value::List report_list;
   for (const auto& report : reports) {
     report_list.Append(report.ToJson());
   }
 
-  base::Value::List unparsable_registration_list;
-  for (const auto& reg : unparsable_registrations) {
-    unparsable_registration_list.Append(reg.ToJson());
-  }
-
-  return base::Value::Dict()
-      .Set(kReportsKey, std::move(report_list))
-      .Set(kUnparsableRegistrationsKey,
-           std::move(unparsable_registration_list));
+  return base::Value::Dict().Set(kReportsKey, std::move(report_list));
 }
 
 AttributionInteropOutput::Report& AttributionInteropOutput::Report::operator=(
@@ -812,12 +701,6 @@
   return out << report.ToJson();
 }
 
-std::ostream& operator<<(
-    std::ostream& out,
-    const AttributionInteropOutput::UnparsableRegistration& reg) {
-  return out << reg.ToJson();
-}
-
 std::ostream& operator<<(std::ostream& out,
                          const AttributionInteropOutput& output) {
   return out << output.ToJson();
diff --git a/content/browser/attribution_reporting/attribution_interop_parser.h b/content/browser/attribution_reporting/attribution_interop_parser.h
index b45bfc4..273f42e 100644
--- a/content/browser/attribution_reporting/attribution_interop_parser.h
+++ b/content/browser/attribution_reporting/attribution_interop_parser.h
@@ -10,15 +10,19 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
 #include "base/types/expected.h"
 #include "base/values.h"
 #include "components/attribution_reporting/source_type.mojom-forward.h"
 #include "components/attribution_reporting/suitable_origin.h"
 #include "content/browser/attribution_reporting/attribution_config.h"
-#include "content/browser/attribution_reporting/attribution_reporting.mojom-forward.h"
 #include "url/gurl.h"
 
+namespace net {
+class HttpResponseHeaders;
+}  // namespace net
+
 namespace content {
 
 struct AttributionSimulationEvent {
@@ -26,10 +30,9 @@
   attribution_reporting::SuitableOrigin context_origin;
   // If null, the event represents a trigger. Otherwise, represents a source.
   std::optional<attribution_reporting::mojom::SourceType> source_type;
-  base::Value registration;
+  scoped_refptr<net::HttpResponseHeaders> response_headers;
   base::Time time;
   bool debug_permission = false;
-  std::string info_header;
 
   AttributionSimulationEvent(
       attribution_reporting::SuitableOrigin reporting_origin,
@@ -90,18 +93,7 @@
     friend bool operator==(const Report&, const Report&) = default;
   };
 
-  struct UnparsableRegistration {
-    base::Time time;
-    attribution_reporting::mojom::RegistrationType type;
-
-    base::Value::Dict ToJson() const;
-
-    friend bool operator==(const UnparsableRegistration&,
-                           const UnparsableRegistration&) = default;
-  };
-
   std::vector<Report> reports;
-  std::vector<UnparsableRegistration> unparsable_registrations;
 
   AttributionInteropOutput();
   ~AttributionInteropOutput();
@@ -121,10 +113,6 @@
 std::ostream& operator<<(std::ostream&,
                          const AttributionInteropOutput::Report&);
 
-std::ostream& operator<<(
-    std::ostream&,
-    const AttributionInteropOutput::UnparsableRegistration&);
-
 std::ostream& operator<<(std::ostream&, const AttributionInteropOutput&);
 
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_interop_parser_unittest.cc b/content/browser/attribution_reporting/attribution_interop_parser_unittest.cc
index fbd5b59..b491140 100644
--- a/content/browser/attribution_reporting/attribution_interop_parser_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_interop_parser_unittest.cc
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/functional/overloaded.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/strings/strcat.h"
 #include "base/test/gmock_expected_support.h"
 #include "base/test/values_test_util.h"
@@ -21,8 +22,8 @@
 #include "components/attribution_reporting/source_type.mojom.h"
 #include "components/attribution_reporting/suitable_origin.h"
 #include "content/browser/attribution_reporting/attribution_config.h"
-#include "content/browser/attribution_reporting/attribution_reporting.mojom.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
+#include "net/http/http_response_headers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
@@ -72,7 +73,7 @@
         "url": "https://a.r.test",
         "debug_permission": true,
         "response": {
-          "Attribution-Reporting-Register-Source": 123
+          "Attribution-Reporting-Register-Source": {"a": "b"}
         }
       }]
     },
@@ -86,7 +87,7 @@
       "responses": [{
         "url": "https://b.r.test",
         "response": {
-          "Attribution-Reporting-Register-Source": 456
+          "Attribution-Reporting-Register-Source": "!!!"
         }
       }]
     }
@@ -106,7 +107,8 @@
             *SuitableOrigin::Deserialize("https://a.r.test"));
   EXPECT_EQ(result.front().context_origin,
             *SuitableOrigin::Deserialize("https://a.s.test"));
-  EXPECT_EQ(result.front().registration, base::Value(123));
+  EXPECT_TRUE(result.front().response_headers->HasHeaderValue(
+      "Attribution-Reporting-Register-Source", R"({"a":"b"})"));
   EXPECT_TRUE(result.front().debug_permission);
 
   EXPECT_EQ(result.back().time,
@@ -117,7 +119,8 @@
             *SuitableOrigin::Deserialize("https://b.r.test"));
   EXPECT_EQ(result.back().context_origin,
             *SuitableOrigin::Deserialize("https://b.s.test"));
-  EXPECT_EQ(result.back().registration, base::Value(456));
+  EXPECT_TRUE(result.back().response_headers->HasHeaderValue(
+      "Attribution-Reporting-Register-Source", "!!!"));
   EXPECT_FALSE(result.back().debug_permission);
 }
 
@@ -133,7 +136,7 @@
         "url": "https://a.r.test",
         "debug_permission": true,
         "response": {
-          "Attribution-Reporting-Register-Trigger": 789
+          "Attribution-Reporting-Register-Trigger": {"a": "b" }
         }
       }]
     }
@@ -152,53 +155,11 @@
   EXPECT_EQ(result.front().context_origin,
             *SuitableOrigin::Deserialize("https://b.d.test"));
   EXPECT_EQ(result.front().source_type, std::nullopt);
-  EXPECT_EQ(result.front().registration, base::Value(789));
+  EXPECT_TRUE(result.front().response_headers->HasHeaderValue(
+      "Attribution-Reporting-Register-Trigger", R"({"a":"b"})"));
   EXPECT_TRUE(result.front().debug_permission);
 }
 
-TEST(AttributionInteropParserTest, ValidInfoParses) {
-  constexpr char kJson[] = R"json({"registrations": [
-    {
-      "timestamp": "1643235573123",
-      "registration_request": {
-        "source_type": "navigation",
-        "attribution_src_url": "https://a.r.test",
-        "context_origin": "https://a.s.test"
-      },
-      "responses": [{
-        "url": "https://a.r.test",
-        "response": {
-          "Attribution-Reporting-Register-Source": 123,
-          "Attribution-Reporting-Info": "foo"
-        }
-      }]
-    },
-    {
-      "timestamp": "1643235575123",
-      "registration_request": {
-        "attribution_src_url": "https://a.r.test",
-        "context_origin": " https://b.d.test",
-      },
-      "responses": [{
-        "url": "https://a.r.test",
-        "response": {
-          "Attribution-Reporting-Register-Trigger": 789,
-          "Attribution-Reporting-Info": "bar"
-        }
-      }]
-    }
-  ]})json";
-
-  base::Value::Dict value = base::test::ParseJsonDict(kJson);
-
-  ASSERT_OK_AND_ASSIGN(
-      auto result, ParseAttributionInteropInput(std::move(value), kOffsetTime));
-  ASSERT_EQ(result.size(), 2u);
-
-  EXPECT_EQ(result.front().info_header, "foo");
-  EXPECT_EQ(result.back().info_header, "bar");
-}
-
 struct ParseErrorTestCase {
   const char* expected_failure_substr;
   const char* json;
@@ -272,7 +233,6 @@
         R"json({"registrations": [{
           "timestamp": "1643235574000",
           "registration_request": {
-            "source_type": "navigation",
             "attribution_src_url": "https://a.r.test",
             "context_origin": "https://a.s.test"
           }
@@ -283,7 +243,6 @@
         R"json({"registrations": [{
           "timestamp": "1643235574000",
           "registration_request": {
-            "source_type": "navigation",
             "attribution_src_url": "https://a.r.test",
             "context_origin": "https://a.s.test"
           },
@@ -295,7 +254,6 @@
         R"json({"registrations": [{
           "timestamp": "1643235574000",
           "registration_request": {
-            "source_type": "navigation",
             "attribution_src_url": "https://a.r.test",
             "context_origin": "https://a.s.test"
           },
@@ -307,7 +265,6 @@
         R"json({"registrations": [{
           "timestamp": "1643235574000",
           "registration_request": {
-            "source_type": "navigation",
             "attribution_src_url": "https://a.r.test",
             "context_origin": "https://a.s.test"
           },
@@ -319,7 +276,6 @@
         R"json({"registrations": [{
           "timestamp": "1643235574000",
           "registration_request": {
-            "source_type": "navigation",
             "attribution_src_url": "https://a.r.test",
             "context_origin": "https://a.s.test"
           },
@@ -331,7 +287,6 @@
         R"json({"registrations": [{
           "timestamp": "1643235574000",
           "registration_request": {
-            "source_type": "navigation",
             "attribution_src_url": "https://a.r.test",
             "context_origin": "https://a.s.test"
           },
@@ -345,7 +300,6 @@
         R"json({"registrations": [{
           "timestamp": "1643235574000",
           "registration_request": {
-            "source_type": "navigation",
             "attribution_src_url": "https://a.r.test",
             "context_origin": "https://a.s.test"
           },
@@ -359,7 +313,6 @@
         R"json({"registrations": [{
           "timestamp": "1643235574000",
           "registration_request": {
-            "source_type": "navigation",
             "attribution_src_url": "https://a.r.test",
             "context_origin": "https://a.s.test"
           },
@@ -370,39 +323,6 @@
         }]})json",
     },
     {
-        R"(["registrations"][0]["responses"][0]["response"]: must contain either source or trigger)",
-        R"json({"registrations": [{
-          "timestamp": "1643235574000",
-          "registration_request": {
-            "source_type": "navigation",
-            "attribution_src_url": "https://a.r.test",
-            "context_origin": "https://a.s.test"
-          },
-          "responses": [{
-            "url": "https://a.r.test",
-            "response": {}
-          }]
-        }]})json",
-    },
-    {
-        R"(["registrations"][0]["responses"][0]["response"]: must contain either source or trigger)",
-        R"json({"registrations": [{
-          "timestamp": "1643235574000",
-          "registration_request": {
-            "source_type": "navigation",
-            "attribution_src_url": "https://a.r.test",
-            "context_origin": "https://a.s.test"
-          },
-          "responses": [{
-            "url": "https://a.r.test",
-            "response": {
-              "Attribution-Reporting-Register-Source": {},
-              "Attribution-Reporting-Register-Trigger": {}
-            }
-          }]
-        }]})json",
-    },
-    {
         R"(["registrations"][0]["registration_request"]["source_type"]: must be either)",
         R"json({"registrations": [{
           "timestamp": "1643235574000",
@@ -412,39 +332,6 @@
         }]})json",
     },
     {
-        R"(["registrations"][0]["registration_request"]["source_type"]: must be present)",
-        R"json({"registrations": [{
-          "timestamp": "1643235574000",
-          "registration_request": {
-            "attribution_src_url": "https://a.r.test",
-            "context_origin": "https://a.s.test"
-          },
-          "responses": [{
-            "url": "https://a.r.test",
-            "response": {
-              "Attribution-Reporting-Register-Source": {}
-            }
-          }]
-        }]})json",
-    },
-    {
-        R"(["registrations"][0]["registration_request"]["source_type"]: must not be present)",
-        R"json({"registrations": [{
-          "timestamp": "1643235574000",
-          "registration_request": {
-            "source_type": "navigation",
-            "attribution_src_url": "https://a.r.test",
-            "context_origin": "https://a.s.test"
-          },
-          "responses": [{
-            "url": "https://a.r.test",
-            "response": {
-              "Attribution-Reporting-Register-Trigger": {}
-            }
-          }]
-        }]})json",
-    },
-    {
         R"(["registrations"][1]["timestamp"]: must be greater than previous time)",
         R"json({"registrations": [
           {
@@ -475,24 +362,6 @@
           },
         ]})json",
     },
-    {
-        R"(["registrations"][0]["responses"][0]["response"]["Attribution-Reporting-Info"]: must be a string)",
-        R"json({"registrations": [{
-          "timestamp": "1643235574000",
-          "registration_request": {
-            "source_type": "navigation",
-            "attribution_src_url": "https://a.r.test",
-            "context_origin": "https://a.s.test"
-          },
-          "responses": [{
-            "url": "https://a.r.test",
-            "response": {
-              "Attribution-Reporting-Register-Source": {},
-              "Attribution-Reporting-Info": {}
-            }
-          }]
-        }]})json",
-    },
 };
 
 INSTANTIATE_TEST_SUITE_P(,
@@ -792,28 +661,19 @@
       {
           "top_level_errors",
           R"json({"foo": []})json",
-          ErrorIs(AllOf(
-              HasSubstr(R"(["reports"]: must be present)"),
-              HasSubstr(R"(["unparsable_registrations"]: must be present)"),
-              HasSubstr(R"(["foo"]: unknown field)"))),
+          ErrorIs(AllOf(HasSubstr(R"(["reports"]: must be present)"),
+                        HasSubstr(R"(["foo"]: unknown field)"))),
       },
       {
           "second_level_errors",
           R"json({
-            "reports": [{"foo": null}],
-            "unparsable_registrations": [{"bar": 123}]
+            "reports": [{"foo": null}]
           })json",
           ErrorIs(AllOf(
               HasSubstr(R"(["reports"][0]["report_time"]: must be an integer)"),
               HasSubstr(R"(["reports"][0]["report_url"]: must be a valid URL)"),
               HasSubstr(R"(["reports"][0]["payload"]: required)"),
-              HasSubstr(R"(["reports"][0]["foo"]: unknown field)"),
-              HasSubstr(
-                  R"(["unparsable_registrations"][0]["time"]: must be an integer)"),
-              HasSubstr(
-                  R"(["unparsable_registrations"][0]["type"]: must be either)"),
-              HasSubstr(
-                  R"(["unparsable_registrations"][0]["bar"]: unknown field)"))),
+              HasSubstr(R"(["reports"][0]["foo"]: unknown field)"))),
       },
       {
           "unsorted_reports",
@@ -829,59 +689,31 @@
                 "report_url": "https://a.test/y",
                 "payload": "def"
               }
-             ],
-            "unparsable_registrations": []
+             ]
           })json",
           ErrorIs(HasSubstr(
               R"(["reports"][1]["report_time"]: must be greater than or equal)")),
       },
       {
-          "unsorted_unparsable_registrations",
-          R"json({
-            "unparsable_registrations": [
-              {"time": "4", "type": "source"},
-              {"time": "3", "type": "trigger"}
-             ],
-             "reports": []
-          })json",
-          ErrorIs(HasSubstr(
-              R"(["unparsable_registrations"][1]["time"]: must be greater than or equal)")),
-      },
-      {
           "ok",
           R"json({
             "reports": [{
               "report_time": "123",
               "report_url": "https://a.test/x",
               "payload": "abc"
-            }],
-            "unparsable_registrations": [{
-              "time": "456",
-              "type": "trigger"
             }]
           })json",
-          ValueIs(AllOf(
-              Field(
-                  &AttributionInteropOutput::reports,
-                  ElementsAre(AllOf(
-                      Field(&AttributionInteropOutput::Report::time,
-                            base::Time::UnixEpoch() + base::Milliseconds(123)),
-                      Field(&AttributionInteropOutput::Report::url,
-                            GURL("https://a.test/x")),
-                      Field(&AttributionInteropOutput::Report::payload,
-                            // `std::ref` needed because `base::Value` isn't
-                            // copyable
-                            Eq(std::ref(kExpectedPayload)))))),
-              Field(
-                  &AttributionInteropOutput::unparsable_registrations,
-                  ElementsAre(AllOf(
-                      Field(&AttributionInteropOutput::UnparsableRegistration::
-                                time,
-                            base::Time::UnixEpoch() + base::Milliseconds(456)),
-                      Field(&AttributionInteropOutput::UnparsableRegistration::
-                                type,
-                            attribution_reporting::mojom::RegistrationType::
-                                kTrigger)))))),
+          ValueIs(Field(
+              &AttributionInteropOutput::reports,
+              ElementsAre(AllOf(
+                  Field(&AttributionInteropOutput::Report::time,
+                        base::Time::UnixEpoch() + base::Milliseconds(123)),
+                  Field(&AttributionInteropOutput::Report::url,
+                        GURL("https://a.test/x")),
+                  Field(&AttributionInteropOutput::Report::payload,
+                        // `std::ref` needed because `base::Value` isn't
+                        // copyable
+                        Eq(std::ref(kExpectedPayload))))))),
       },
   };
 
diff --git a/content/browser/attribution_reporting/attribution_interop_runner.cc b/content/browser/attribution_reporting/attribution_interop_runner.cc
index e5802e65..5963800 100644
--- a/content/browser/attribution_reporting/attribution_interop_runner.cc
+++ b/content/browser/attribution_reporting/attribution_interop_runner.cc
@@ -20,7 +20,6 @@
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
 #include "base/memory/raw_ref.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/numerics/safe_conversions.h"
@@ -41,9 +40,7 @@
 #include "components/aggregation_service/features.h"
 #include "components/attribution_reporting/parsing_utils.h"
 #include "components/attribution_reporting/registration_eligibility.mojom.h"
-#include "components/attribution_reporting/source_registration.h"
 #include "components/attribution_reporting/source_type.mojom.h"
-#include "components/attribution_reporting/trigger_registration.h"
 #include "components/cbor/reader.h"
 #include "components/cbor/values.h"
 #include "content/browser/aggregation_service/aggregatable_report.h"
@@ -52,7 +49,6 @@
 #include "content/browser/aggregation_service/aggregation_service_test_utils.h"
 #include "content/browser/attribution_reporting/aggregatable_attribution_utils.h"
 #include "content/browser/attribution_reporting/attribution_background_registrations_id.h"
-#include "content/browser/attribution_reporting/attribution_constants.h"
 #include "content/browser/attribution_reporting/attribution_cookie_checker.h"
 #include "content/browser/attribution_reporting/attribution_data_host_manager.h"
 #include "content/browser/attribution_reporting/attribution_interop_parser.h"
@@ -68,8 +64,6 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_browser_context.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_version.h"
 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "services/network/public/cpp/attribution_reporting_runtime_features.h"
 #include "services/network/public/cpp/trigger_verification.h"
@@ -248,17 +242,14 @@
   bool debug_cookie_set_ = false;
 };
 
-// Registers sources and triggers in the `AttributionManagerImpl` and records
-// unparsable registrations.
+// Registers sources and triggers in the `AttributionManagerImpl`.
 class AttributionEventHandler {
  public:
   AttributionEventHandler(std::unique_ptr<AttributionManagerImpl> manager,
-                          FakeCookieChecker* fake_cookie_checker,
-                          base::Time time_origin)
+                          FakeCookieChecker* fake_cookie_checker)
       : manager_(std::move(manager)),
         fake_cookie_checker_(
-            raw_ref<FakeCookieChecker>::from_ptr(fake_cookie_checker)),
-        time_offset_(TimeOffset(time_origin)) {
+            raw_ref<FakeCookieChecker>::from_ptr(fake_cookie_checker)) {
     DCHECK(manager_);
   }
 
@@ -267,47 +258,26 @@
 
     const BackgroundRegistrationsId id(unique_id_counter_++);
 
-    std::string registration_str;
-    if (std::string* str = event.registration.GetIfString()) {
-      registration_str = std::move(*str);
-    } else {
-      CHECK(base::JSONWriter::Write(event.registration, &registration_str));
-    }
-
     auto* attribution_data_host_manager = manager_->GetDataHostManager();
 
-    const bool is_source = event.source_type.has_value();
     std::optional<blink::AttributionSrcToken> attribution_src_token;
 
-    if (is_source) {
-      if (auto registration = attribution_reporting::SourceRegistration::Parse(
-              registration_str, *event.source_type);
-          !registration.has_value()) {
-        AddUnparsableRegistration(event);
-      }
-
-      if (event.source_type ==
-          attribution_reporting::mojom::SourceType::kNavigation) {
-        attribution_src_token.emplace();
-        attribution_data_host_manager
-            ->NotifyNavigationWithBackgroundRegistrationsWillStart(
-                attribution_src_token.value(),
-                /*background_registrations_count=*/1);
-        attribution_data_host_manager->NotifyNavigationRegistrationStarted(
-            AttributionSuitableContext::CreateForTesting(
-                event.context_origin,
-                /*is_nested_within_fenced_frame=*/false, kFrameId,
-                /*last_navigation_id=*/kNavigationId),
-            attribution_src_token.value(), kNavigationId,
-            /*devtools_request_id=*/"");
-        attribution_data_host_manager->NotifyNavigationRegistrationCompleted(
-            attribution_src_token.value());
-      }
-    } else if (auto registration =
-                   attribution_reporting::TriggerRegistration::Parse(
-                       registration_str);
-               !registration.has_value()) {
-      AddUnparsableRegistration(event);
+    if (event.source_type ==
+        attribution_reporting::mojom::SourceType::kNavigation) {
+      attribution_src_token.emplace();
+      attribution_data_host_manager
+          ->NotifyNavigationWithBackgroundRegistrationsWillStart(
+              attribution_src_token.value(),
+              /*background_registrations_count=*/1);
+      attribution_data_host_manager->NotifyNavigationRegistrationStarted(
+          AttributionSuitableContext::CreateForTesting(
+              event.context_origin,
+              /*is_nested_within_fenced_frame=*/false, kFrameId,
+              /*last_navigation_id=*/kNavigationId),
+          attribution_src_token.value(), kNavigationId,
+          /*devtools_request_id=*/"");
+      attribution_data_host_manager->NotifyNavigationRegistrationCompleted(
+          attribution_src_token.value());
     }
 
     attribution_data_host_manager->NotifyBackgroundRegistrationStarted(
@@ -315,31 +285,18 @@
         AttributionSuitableContext::CreateForTesting(
             event.context_origin,
             /*is_nested_within_fenced_frame=*/false, kFrameId, kNavigationId),
-        is_source ? RegistrationEligibility::kSource
-                  : RegistrationEligibility::kTrigger,
+        event.source_type.has_value() ? RegistrationEligibility::kSource
+                                      : RegistrationEligibility::kTrigger,
         attribution_src_token,
         /*devtools_request_id=*/"");
 
-    scoped_refptr<net::HttpResponseHeaders> headers =
-        net::HttpResponseHeaders::Builder(/*version=*/{1, 1},
-                                          /*status=*/"200 OK")
-            .AddHeader(is_source ? kAttributionReportingRegisterSourceHeader
-                                 : kAttributionReportingRegisterTriggerHeader,
-                       registration_str)
-            .AddHeader(kAttributionReportingInfoHeader, event.info_header)
-            .Build();
     attribution_data_host_manager->NotifyBackgroundRegistrationData(
-        id, headers.get(), event.reporting_origin->GetURL(),
+        id, event.response_headers.get(), event.reporting_origin->GetURL(),
         network::AttributionReportingRuntimeFeatures(),
         /*trigger_verification=*/{});
     attribution_data_host_manager->NotifyBackgroundRegistrationCompleted(id);
   }
 
-  std::vector<AttributionInteropOutput::UnparsableRegistration>
-  TakeUnparsable() && {
-    return std::move(unparsable_);
-  }
-
   void FastForwardUntilReportsConsumed(
       BrowserTaskEnvironment& task_environment) {
     while (true) {
@@ -368,23 +325,10 @@
   }
 
  private:
-  // TODO(linnan): Consider removing `unparsable_registrations`.
-  void AddUnparsableRegistration(const AttributionSimulationEvent& event) {
-    auto& registration = unparsable_.emplace_back();
-    registration.time = event.time - time_offset_;
-    registration.type = event.source_type.has_value()
-                            ? RegistrationType::kSource
-                            : RegistrationType::kTrigger;
-  }
-
   const std::unique_ptr<AttributionManagerImpl> manager_;
   const raw_ref<FakeCookieChecker> fake_cookie_checker_;
 
-  const base::TimeDelta time_offset_;
-
   int64_t unique_id_counter_ = 0;
-
-  std::vector<AttributionInteropOutput::UnparsableRegistration> unparsable_;
 };
 
 }  // namespace
@@ -471,8 +415,7 @@
            base::TaskShutdownBehavior::BLOCK_SHUTDOWN,
            base::ThreadPolicy::MUST_USE_FOREGROUND}));
 
-  AttributionEventHandler handler(std::move(manager), raw_fake_cookie_checker,
-                                  time_origin);
+  AttributionEventHandler handler(std::move(manager), raw_fake_cookie_checker);
 
   static_cast<AggregationServiceImpl*>(
       storage_partition->GetAggregationService())
@@ -497,7 +440,6 @@
 
   handler.FastForwardUntilReportsConsumed(task_environment);
 
-  output.unparsable_registrations = std::move(handler).TakeUnparsable();
   return output;
 }
 
diff --git a/content/browser/attribution_reporting/attribution_interop_unittest.cc b/content/browser/attribution_reporting/attribution_interop_unittest.cc
index 0c6ca497..ec49429e 100644
--- a/content/browser/attribution_reporting/attribution_interop_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_interop_unittest.cc
@@ -157,11 +157,8 @@
   PreProcessOutput(actual_output);
 
   EXPECT_THAT(actual_output,
-              AllOf(Field(&AttributionInteropOutput::reports,
-                          UnorderedElementsAreArray(expected_output.reports)),
-                    Field(&AttributionInteropOutput::unparsable_registrations,
-                          UnorderedElementsAreArray(
-                              expected_output.unparsable_registrations))));
+              Field(&AttributionInteropOutput::reports,
+                    UnorderedElementsAreArray(expected_output.reports)));
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/content/browser/attribution_reporting/attribution_storage_delegate.h b/content/browser/attribution_reporting/attribution_storage_delegate.h
index 7806b76..4b5d071c 100644
--- a/content/browser/attribution_reporting/attribution_storage_delegate.h
+++ b/content/browser/attribution_reporting/attribution_storage_delegate.h
@@ -157,8 +157,7 @@
       attribution_reporting::mojom::SourceType,
       const attribution_reporting::TriggerSpecs&,
       attribution_reporting::MaxEventLevelReports,
-      attribution_reporting::EventLevelEpsilon,
-      base::Time source_time) const = 0;
+      attribution_reporting::EventLevelEpsilon) const = 0;
 
   int GetMaxAggregatableReportsPerSource() const;
 
diff --git a/content/browser/attribution_reporting/attribution_storage_delegate_impl.cc b/content/browser/attribution_reporting/attribution_storage_delegate_impl.cc
index 46737a71..5eb1c23f 100644
--- a/content/browser/attribution_reporting/attribution_storage_delegate_impl.cc
+++ b/content/browser/attribution_reporting/attribution_storage_delegate_impl.cc
@@ -211,8 +211,7 @@
     SourceType source_type,
     const attribution_reporting::TriggerSpecs& trigger_specs,
     attribution_reporting::MaxEventLevelReports max_event_level_reports,
-    attribution_reporting::EventLevelEpsilon epsilon,
-    base::Time source_time) const {
+    attribution_reporting::EventLevelEpsilon epsilon) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   attribution_reporting::RandomizedResponseData response =
       attribution_reporting::DoRandomizedResponse(
diff --git a/content/browser/attribution_reporting/attribution_storage_delegate_impl.h b/content/browser/attribution_reporting/attribution_storage_delegate_impl.h
index 54e418a36..9fd22f8 100644
--- a/content/browser/attribution_reporting/attribution_storage_delegate_impl.h
+++ b/content/browser/attribution_reporting/attribution_storage_delegate_impl.h
@@ -93,8 +93,7 @@
       attribution_reporting::mojom::SourceType,
       const attribution_reporting::TriggerSpecs&,
       attribution_reporting::MaxEventLevelReports,
-      attribution_reporting::EventLevelEpsilon,
-      base::Time source_time) const override;
+      attribution_reporting::EventLevelEpsilon) const override;
   std::vector<NullAggregatableReport> GetNullAggregatableReports(
       const AttributionTrigger&,
       base::Time trigger_time,
diff --git a/content/browser/attribution_reporting/attribution_storage_delegate_impl_unittest.cc b/content/browser/attribution_reporting/attribution_storage_delegate_impl_unittest.cc
index 07c7e05..ff74ac3 100644
--- a/content/browser/attribution_reporting/attribution_storage_delegate_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_delegate_impl_unittest.cc
@@ -80,8 +80,7 @@
                       .GetRandomizedResponse(source.common_info().source_type(),
                                              source.trigger_specs(),
                                              source.max_event_level_reports(),
-                                             source.event_level_epsilon(),
-                                             source.source_time());
+                                             source.event_level_epsilon());
     ASSERT_TRUE(result.has_value());
     ASSERT_GT(result->rate(), 0);
     ASSERT_EQ(result->response(), std::nullopt);
@@ -133,8 +132,7 @@
 
     auto result = delegate->GetRandomizedResponse(
         test_case.source_type, source.trigger_specs(),
-        source.max_event_level_reports(), source.event_level_epsilon(),
-        source.source_time());
+        source.max_event_level_reports(), source.event_level_epsilon());
 
     EXPECT_EQ(result.has_value(), test_case.expected_ok);
   }
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index db44b3d..42e540e 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -607,14 +607,13 @@
   const base::Time aggregatable_report_window_time =
       source_time + reg.aggregatable_report_window;
 
-  ASSIGN_OR_RETURN(
-      const auto randomized_response_data,
-      delegate_->GetRandomizedResponse(
-          common_info.source_type(), reg.trigger_specs,
-          reg.max_event_level_reports, reg.event_level_epsilon, source_time),
-      [](auto) -> StoreSourceResult {
-        return StoreSourceResult::ExceedsMaxChannelCapacity();
-      });
+  ASSIGN_OR_RETURN(const auto randomized_response_data,
+                   delegate_->GetRandomizedResponse(
+                       common_info.source_type(), reg.trigger_specs,
+                       reg.max_event_level_reports, reg.event_level_epsilon),
+                   [](auto) -> StoreSourceResult {
+                     return StoreSourceResult::ExceedsMaxChannelCapacity();
+                   });
 
   int num_conversions = 0;
   auto attribution_logic = StoredSource::AttributionLogic::kTruthfully;
diff --git a/content/browser/attribution_reporting/test/configurable_storage_delegate.cc b/content/browser/attribution_reporting/test/configurable_storage_delegate.cc
index 5b85b50a..fccdeb3a 100644
--- a/content/browser/attribution_reporting/test/configurable_storage_delegate.cc
+++ b/content/browser/attribution_reporting/test/configurable_storage_delegate.cc
@@ -133,8 +133,7 @@
     attribution_reporting::mojom::SourceType,
     const attribution_reporting::TriggerSpecs&,
     attribution_reporting::MaxEventLevelReports,
-    attribution_reporting::EventLevelEpsilon,
-    base::Time source_time) const {
+    attribution_reporting::EventLevelEpsilon) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (exceeds_channel_capacity_limit_) {
     return base::unexpected(ExceedsChannelCapacityLimit());
diff --git a/content/browser/attribution_reporting/test/configurable_storage_delegate.h b/content/browser/attribution_reporting/test/configurable_storage_delegate.h
index 5707a1c..04c9984 100644
--- a/content/browser/attribution_reporting/test/configurable_storage_delegate.h
+++ b/content/browser/attribution_reporting/test/configurable_storage_delegate.h
@@ -44,8 +44,7 @@
       attribution_reporting::mojom::SourceType,
       const attribution_reporting::TriggerSpecs&,
       attribution_reporting::MaxEventLevelReports,
-      attribution_reporting::EventLevelEpsilon,
-      base::Time source_time) const override;
+      attribution_reporting::EventLevelEpsilon) const override;
   std::vector<NullAggregatableReport> GetNullAggregatableReports(
       const AttributionTrigger&,
       base::Time trigger_time,
diff --git a/content/browser/child_process_launcher_helper.cc b/content/browser/child_process_launcher_helper.cc
index 05ddb8af..725d296 100644
--- a/content/browser/child_process_launcher_helper.cc
+++ b/content/browser/child_process_launcher_helper.cc
@@ -390,12 +390,13 @@
           std::move(process)));
 }
 
+#if !BUILDFLAG(IS_WIN)
 void ChildProcessLauncherHelper::PassLoggingSwitches(
     base::LaunchOptions* launch_options,
     base::CommandLine* cmd_line) {
   const base::CommandLine& browser_command_line =
       *base::CommandLine::ForCurrentProcess();
-  static const char* const kForwardSwitches[] = {
+  constexpr const char* kForwardSwitches[] = {
       switches::kDisableLogging,
       switches::kEnableLogging,
       switches::kLogFile,
@@ -405,6 +406,7 @@
   };
   cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches);
 }
+#endif  // !BUILDFLAG(IS_WIN)
 
 }  // namespace internal
 
diff --git a/content/browser/child_process_launcher_helper.h b/content/browser/child_process_launcher_helper.h
index c641d6f..e6f3e32 100644
--- a/content/browser/child_process_launcher_helper.h
+++ b/content/browser/child_process_launcher_helper.h
@@ -35,6 +35,7 @@
 #endif
 
 #if BUILDFLAG(IS_WIN)
+#include "base/win/scoped_handle.h"
 #include "base/win/windows_types.h"
 #include "content/public/common/prefetch_type_win.h"
 #include "sandbox/win/src/sandbox_types.h"
@@ -341,6 +342,11 @@
   std::unique_ptr<sandbox::policy::SandboxPolicyFuchsia> sandbox_policy_;
 #endif
 
+#if BUILDFLAG(IS_WIN)
+  // Only valid if the host process has logging enabled.
+  base::win::ScopedHandle log_handle_;
+#endif
+
   // Histogram shared memory region metadata.
   base::UnsafeSharedMemoryRegion histogram_memory_region_;
 };
diff --git a/content/browser/child_process_launcher_helper_win.cc b/content/browser/child_process_launcher_helper_win.cc
index 1d6bc65..220eab3 100644
--- a/content/browser/child_process_launcher_helper_win.cc
+++ b/content/browser/child_process_launcher_helper_win.cc
@@ -2,27 +2,42 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "content/browser/child_process_launcher_helper.h"
+
+#include "base/base_switches.h"
 #include "base/files/file_path.h"
-#include "base/path_service.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
 #include "base/process/process.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
+#include "build/build_config.h"
 #include "content/browser/child_process_launcher.h"
-#include "content/browser/child_process_launcher_helper.h"
 #include "content/public/browser/child_process_launcher_utils.h"
 #include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/sandbox_init_win.h"
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
-#include "sandbox/policy/win/sandbox_win.h"
 #include "sandbox/win/src/sandbox_types.h"
 
 namespace {
 
+// Helper to avoid marking the log file as non-executable every time we launch a
+// process.
+bool ShouldMarkLogfileAsNonExecute() {
+  static bool first_time = true;
+  if (!first_time) {
+    return false;
+  }
+  first_time = false;
+  return true;
+}
+
 // /prefetch:# arguments to use when launching various process types. It has
 // been observed that when file reads are consistent for 3 process launches with
 // the same /prefetch:# argument, the Windows prefetcher starts issuing reads in
@@ -93,6 +108,7 @@
 
 std::unique_ptr<FileMappedForLaunch>
 ChildProcessLauncherHelper::GetFilesToMap() {
+  // Windows uses LaunchOptions to pass filehandles to children.
   return nullptr;
 }
 
@@ -176,6 +192,60 @@
   }
 }
 
+void ChildProcessLauncherHelper::PassLoggingSwitches(
+    base::LaunchOptions* launch_options,
+    base::CommandLine* cmd_line) {
+  const base::CommandLine& browser_command_line =
+      *base::CommandLine::ForCurrentProcess();
+  // Sandboxed processes on Windows cannot open files, and can't always figure
+  // out default paths, so we directly pass a handle if logging is enabled.
+  if (logging::IsLoggingToFileEnabled()) {
+    // Make sure we're in charge of these flags.
+    CHECK(!cmd_line->HasSwitch(switches::kEnableLogging));
+    CHECK(!cmd_line->HasSwitch(switches::kLogFile));
+
+    // Make best efforts attempt to mark the logfile as no-execute the first
+    // time a process is started.
+    if (ShouldMarkLogfileAsNonExecute()) {
+      // Failure here means we pass in a writeable handle to a file that could
+      // be marked executable and chained into a sandbox escape - but failure
+      // should be rare and providing a logfile is already optional.
+      std::ignore = base::PreventExecuteMappingUnchecked(
+          base::FilePath(logging::GetLogFileFullPath()),
+          base::PreventExecuteMappingClasses::GetPassKey());
+    }
+
+    log_handle_.Set(logging::DuplicateLogFileHandle());
+    if (log_handle_.is_valid()) {
+      // Override `--enable-logging --log-file=` switches so the child can log.
+      cmd_line->AppendSwitchASCII(switches::kEnableLogging, "handle");
+      auto handle_str =
+          base::NumberToString(base::win::HandleToUint32(log_handle_.get()));
+      cmd_line->AppendSwitchASCII(switches::kLogFile, handle_str);
+
+      launch_options->handles_to_inherit.push_back(log_handle_.get());
+    }
+  }
+#if !defined(OFFICIAL_BUILD)
+  // Official builds do not send std handles to children so there is no point
+  // in passing --enable-logging by itself. Debug builds might need to know if
+  // stderr is being forced or not.
+  else if (browser_command_line.HasSwitch(switches::kEnableLogging)) {
+    std::string logging_destination =
+        browser_command_line.GetSwitchValueASCII(switches::kEnableLogging);
+    cmd_line->AppendSwitchASCII(switches::kEnableLogging, logging_destination);
+  }
+#endif
+  // Forward other switches like other platforms.
+  constexpr const char* kForwardSwitches[] = {
+      switches::kDisableLogging,
+      switches::kLoggingLevel,
+      switches::kV,
+      switches::kVModule,
+  };
+  cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches);
+}
+
 bool ChildProcessLauncherHelper::IsUsingLaunchOptions() {
   return true;
 }
diff --git a/content/browser/find_request_manager_browsertest.cc b/content/browser/find_request_manager_browsertest.cc
index 50187a7..4a06d531 100644
--- a/content/browser/find_request_manager_browsertest.cc
+++ b/content/browser/find_request_manager_browsertest.cc
@@ -92,6 +92,12 @@
     EXPECT_TRUE(
         NavigateToURL(shell(), embedded_test_server()->GetURL("a.com", url)));
     ASSERT_TRUE(navigation_observer.last_navigation_succeeded());
+
+    // crbug.com/330147459: Ensure a frame has been produced in the renderer so
+    // the active match is set correctly.
+    ASSERT_TRUE(
+        EvalJsAfterLifecycleUpdate(contents()->GetPrimaryMainFrame(), "", "")
+            .error.empty());
   }
 
   // Loads a multi-frame page. The page will have a full binary frame tree of
@@ -633,7 +639,14 @@
 }
 
 // Tests that new matches can be found in dynamically added text.
-IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(FindNewMatches)) {
+// TODO(crbug.com/330194342): Deflake and re-enable.
+#if BUILDFLAG(IS_ANDROID) || \
+    (BUILDFLAG(IS_LINUX) && !defined(UNDEFINED_SANITIZER))
+#define MAYBE_FindNewMatches DISABLED_FindNewMatches
+#else
+#define MAYBE_FindNewMatches FindNewMatches
+#endif
+IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE_FindNewMatches) {
   LoadAndWait("/find_in_dynamic_page.html");
 
   auto options = blink::mojom::FindOptions::New();
diff --git a/content/browser/loader/keep_alive_url_loader_service.cc b/content/browser/loader/keep_alive_url_loader_service.cc
index bf522a0..9de06ef 100644
--- a/content/browser/loader/keep_alive_url_loader_service.cc
+++ b/content/browser/loader/keep_alive_url_loader_service.cc
@@ -168,10 +168,7 @@
         base::BindRepeating(&KeepAliveURLLoaderFactoriesBase::CreateThrottles,
                             base::Unretained(this), resource_request),
         base::PassKey<KeepAliveURLLoaderService>(),
-        // TODO(https://crbug.com/1519211): Determine whether to integrate ARA
-        // in fetch later requests.
-        (!resource_request.is_fetch_later_api &&
-         context->attribution_context.has_value())
+        context->attribution_context.has_value()
             ? KeepAliveAttributionRequestHelper::CreateIfNeeded(
                   resource_request.attribution_reporting_eligibility,
                   resource_request.url,
diff --git a/content/browser/loader/keep_alive_url_loader_service_unittest.cc b/content/browser/loader/keep_alive_url_loader_service_unittest.cc
index ac0fc62..8a370b9 100644
--- a/content/browser/loader/keep_alive_url_loader_service_unittest.cc
+++ b/content/browser/loader/keep_alive_url_loader_service_unittest.cc
@@ -1215,7 +1215,10 @@
     : public KeepAliveURLLoaderServiceTestBase {
  protected:
   void SetUp() override {
-    feature_list().InitAndEnableFeature(blink::features::kFetchLaterAPI);
+    feature_list().InitWithFeatures(
+        {blink::features::kFetchLaterAPI,
+         blink::features::kAttributionReportingInBrowserMigration},
+        {});
     KeepAliveURLLoaderServiceTestBase::SetUp();
   }
 
@@ -1377,4 +1380,61 @@
   EXPECT_EQ(network_url_loader_factory().NumPending(), 1);
 }
 
+TEST_F(FetchLaterKeepAliveURLLoaderServiceTest,
+       ForwardRedirectsAndResponseToAttributionRequestHelper) {
+  // The Attribution Manager uses the DataDecoder service, which, when an
+  // InProcessDataDecoer object exists, will route to an internal in-process
+  // instance.
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
+
+  // Set up the Attribution Manager.
+  test_web_contents()->NavigateAndCommit(GURL("https://secure_impression.com"));
+  auto mock_manager = std::make_unique<MockAttributionManager>();
+  mock_manager->SetDataHostManager(
+      std::make_unique<AttributionDataHostManagerImpl>(mock_manager.get()));
+  MockAttributionManager* mock_attribution_manager = mock_manager.get();
+  static_cast<StoragePartitionImpl*>(
+      browser_context()->GetDefaultStoragePartition())
+      ->OverrideAttributionManagerForTesting(std::move(mock_manager));
+
+  // Loads FetchLater request (which is also keepalive request):
+  FakeRemoteFetchLaterLoaderFactory renderer_loader_factory;
+  BindFetchLaterLoaderFactory(renderer_loader_factory);
+  network::ResourceRequest request =
+      CreateFetchLaterResourceRequest(GURL(kTestRequestUrl));
+  request.attribution_reporting_eligibility =
+      network::mojom::AttributionReportingEligibility::kEventSourceOrTrigger;
+  renderer_loader_factory.CreateLoader(std::move(request));
+  EXPECT_EQ(loader_service().NumLoadersForTesting(), 1u);
+  // As the request is deferred, the pending URLoader in network is 0.
+  EXPECT_EQ(network_url_loader_factory().NumPending(), 0);
+  // Simulate a shutdown to start the pending request.
+  loader_service().Shutdown();
+  // The pending loader should still exist.
+  EXPECT_EQ(loader_service().NumLoadersForTesting(), 1u);
+  // There should be no disconnected loader.
+  EXPECT_EQ(loader_service().NumDisconnectedLoadersForTesting(), 0u);
+  // The network should now have created pending URLLoader.
+  EXPECT_EQ(network_url_loader_factory().NumPending(), 1);
+
+  // Simluates receiving a redirect in the network service.
+  EXPECT_CALL(*mock_attribution_manager, HandleTrigger).Times(1);
+  constexpr char kRegisterTriggerJson[] = R"json({ })json";
+  GetLastPendingRequest()->client->OnReceiveRedirect(
+      CreateRedirectInfo(GURL(kTestRedirectRequestUrl)),
+      CreateResponseHead({{kAttributionReportingRegisterTriggerHeader,
+                           kRegisterTriggerJson}}));
+
+  // Simluates receiving response in the network service.
+  EXPECT_CALL(*mock_attribution_manager, HandleSource).Times(1);
+  constexpr char kRegisterSourceJson[] =
+      R"json({"destination":"https://destination.example"})json";
+  GetLastPendingRequest()->client->OnReceiveResponse(
+      CreateResponseHead(
+          {{kAttributionReportingRegisterSourceHeader, kRegisterSourceJson}}),
+      /*body=*/{}, /*cached_metadata=*/absl::nullopt);
+
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace content
diff --git a/content/browser/media/encrypted_media_browsertest.cc b/content/browser/media/encrypted_media_browsertest.cc
index de42355..023e67512 100644
--- a/content/browser/media/encrypted_media_browsertest.cc
+++ b/content/browser/media/encrypted_media_browsertest.cc
@@ -391,7 +391,15 @@
 
 // Strictly speaking this is not an "encrypted" media test. Keep it here for
 // completeness.
-IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, ConfigChangeVideo_ClearToClear) {
+// TODO(crbug.com/330190697): Flaky on Fuchsia, deflake and re-enable the test.
+#if BUILDFLAG(IS_FUCHSIA)
+#define MAYBE_ConfigChangeVideo_ClearToClear \
+  DISABLED_ConfigChangeVideo_ClearToClear
+#else
+#define MAYBE_ConfigChangeVideo_ClearToClear ConfigChangeVideo_ClearToClear
+#endif
+IN_PROC_BROWSER_TEST_P(EncryptedMediaTest,
+                       MAYBE_ConfigChangeVideo_ClearToClear) {
   TestConfigChange(ConfigChangeType::CLEAR_TO_CLEAR);
 }
 
diff --git a/content/browser/preloading/prefetch/prefetch_container.cc b/content/browser/preloading/prefetch/prefetch_container.cc
index 2a39822..2b6ecc4 100644
--- a/content/browser/preloading/prefetch/prefetch_container.cc
+++ b/content/browser/preloading/prefetch/prefetch_container.cc
@@ -300,25 +300,37 @@
 }
 
 void RecordWasBlockedUntilHeadWhenServingHistogram(
-    const blink::mojom::SpeculationEagerness& eagerness,
+    const PrefetchType& prefetch_type,
     bool blocked_until_head) {
-  base::UmaHistogramBoolean(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s",
-          GetPrefetchEagernessHistogramSuffix(eagerness).c_str()),
-      blocked_until_head);
+  if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
+    base::UmaHistogramBoolean(
+        base::StringPrintf(
+            "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%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 blink::mojom::SpeculationEagerness& eagerness,
+    const PrefetchType& prefetch_type,
     const base::TimeDelta& block_until_head_duration,
     bool served) {
-  base::UmaHistogramTimes(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration.%s.%s",
-          served ? "Served" : "NotServed",
-          GetPrefetchEagernessHistogramSuffix(eagerness).c_str()),
-      block_until_head_duration);
+  if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
+    base::UmaHistogramTimes(
+        base::StringPrintf(
+            "PrefetchProxy.AfterClick.BlockUntilHeadDuration.%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(
@@ -1093,7 +1105,7 @@
   // streaming URL loader and head/failure/redirect hasn't been received yet.
   if (streaming_loader_ && !redirect_chain_.empty() &&
       redirect_chain_.back()->response_reader_->IsWaitingForResponse() &&
-      PrefetchShouldBlockUntilHead(prefetch_type_.GetEagerness())) {
+      PrefetchShouldBlockUntilHead(prefetch_type_)) {
     return ServableState::kShouldBlockUntilHeadReceived;
   }
 
@@ -1204,7 +1216,7 @@
   // will already be set. Only record in the histogram when the
   // `blocked_until_head_start_time_` is not set yet.
   if (!blocked_until_head_start_time_) {
-    RecordWasBlockedUntilHeadWhenServingHistogram(prefetch_type_.GetEagerness(),
+    RecordWasBlockedUntilHeadWhenServingHistogram(prefetch_type_,
                                                   blocked_until_head);
   }
   if (blocked_until_head) {
@@ -1221,7 +1233,7 @@
 
   if (blocked_until_head_start_time_.has_value()) {
     RecordBlockUntilHeadDurationHistogram(
-        prefetch_type_.GetEagerness(),
+        prefetch_type_,
         base::TimeTicks::Now() - blocked_until_head_start_time_.value(),
         served);
   }
diff --git a/content/browser/preloading/prefetch/prefetch_params.cc b/content/browser/preloading/prefetch/prefetch_params.cc
index 601c05b..e5870a0d 100644
--- a/content/browser/preloading/prefetch/prefetch_params.cc
+++ b/content/browser/preloading/prefetch/prefetch_params.cc
@@ -11,6 +11,7 @@
 #include "base/no_destructor.h"
 #include "base/rand_util.h"
 #include "content/browser/preloading/prefetch/prefetch_features.h"
+#include "content/browser/preloading/preloading_trigger_type_impl.h"
 #include "content/common/features.h"
 #include "content/public/browser/prefetch_service_delegate.h"
 #include "content/public/common/content_features.h"
@@ -209,43 +210,54 @@
       features::kPrefetchUseContentRefactor, "canary_check_retries", 1);
 }
 
-bool PrefetchShouldBlockUntilHead(
-    blink::mojom::SpeculationEagerness prefetch_eagerness) {
-  switch (prefetch_eagerness) {
-    case blink::mojom::SpeculationEagerness::kEager:
-      return base::GetFieldTrialParamByFeatureAsBool(
-          features::kPrefetchUseContentRefactor,
-          "block_until_head_eager_prefetch", true);
-    case blink::mojom::SpeculationEagerness::kModerate:
-      return base::GetFieldTrialParamByFeatureAsBool(
-          features::kPrefetchUseContentRefactor,
-          "block_until_head_moderate_prefetch", true);
-    case blink::mojom::SpeculationEagerness::kConservative:
-      return base::GetFieldTrialParamByFeatureAsBool(
-          features::kPrefetchUseContentRefactor,
-          "block_until_head_conservative_prefetch", true);
+bool PrefetchShouldBlockUntilHead(const PrefetchType& prefetch_type) {
+  if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
+    switch (prefetch_type.GetEagerness()) {
+      case blink::mojom::SpeculationEagerness::kEager:
+        return base::GetFieldTrialParamByFeatureAsBool(
+            features::kPrefetchUseContentRefactor,
+            "block_until_head_eager_prefetch", true);
+      case blink::mojom::SpeculationEagerness::kModerate:
+        return base::GetFieldTrialParamByFeatureAsBool(
+            features::kPrefetchUseContentRefactor,
+            "block_until_head_moderate_prefetch", true);
+      case blink::mojom::SpeculationEagerness::kConservative:
+        return base::GetFieldTrialParamByFeatureAsBool(
+            features::kPrefetchUseContentRefactor,
+            "block_until_head_conservative_prefetch", true);
+    }
+  } else {
+    return base::GetFieldTrialParamByFeatureAsBool(
+        features::kPrefetchUseContentRefactor,
+        "block_until_head_embedder_prefetch", true);
   }
 }
 
 base::TimeDelta PrefetchBlockUntilHeadTimeout(
-    blink::mojom::SpeculationEagerness prefetch_eagerness) {
+    const PrefetchType& prefetch_type) {
   int timeout_in_milliseconds = 0;
-  switch (prefetch_eagerness) {
-    case blink::mojom::SpeculationEagerness::kEager:
-      timeout_in_milliseconds = base::GetFieldTrialParamByFeatureAsInt(
-          features::kPrefetchUseContentRefactor,
-          "block_until_head_timeout_eager_prefetch", 1000);
-      break;
-    case blink::mojom::SpeculationEagerness::kModerate:
-      timeout_in_milliseconds = base::GetFieldTrialParamByFeatureAsInt(
-          features::kPrefetchUseContentRefactor,
-          "block_until_head_timeout_moderate_prefetch", 0);
-      break;
-    case blink::mojom::SpeculationEagerness::kConservative:
-      timeout_in_milliseconds = base::GetFieldTrialParamByFeatureAsInt(
-          features::kPrefetchUseContentRefactor,
-          "block_until_head_timeout_conservative_prefetch", 0);
-      break;
+  if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
+    switch (prefetch_type.GetEagerness()) {
+      case blink::mojom::SpeculationEagerness::kEager:
+        timeout_in_milliseconds = base::GetFieldTrialParamByFeatureAsInt(
+            features::kPrefetchUseContentRefactor,
+            "block_until_head_timeout_eager_prefetch", 1000);
+        break;
+      case blink::mojom::SpeculationEagerness::kModerate:
+        timeout_in_milliseconds = base::GetFieldTrialParamByFeatureAsInt(
+            features::kPrefetchUseContentRefactor,
+            "block_until_head_timeout_moderate_prefetch", 0);
+        break;
+      case blink::mojom::SpeculationEagerness::kConservative:
+        timeout_in_milliseconds = base::GetFieldTrialParamByFeatureAsInt(
+            features::kPrefetchUseContentRefactor,
+            "block_until_head_timeout_conservative_prefetch", 0);
+        break;
+    }
+  } else {
+    timeout_in_milliseconds = base::GetFieldTrialParamByFeatureAsInt(
+        features::kPrefetchUseContentRefactor,
+        "block_until_head_timeout_embedder_prefetch", 1000);
   }
   return base::Milliseconds(timeout_in_milliseconds);
 }
diff --git a/content/browser/preloading/prefetch/prefetch_params.h b/content/browser/preloading/prefetch/prefetch_params.h
index a627d3f4..c67bf2c 100644
--- a/content/browser/preloading/prefetch/prefetch_params.h
+++ b/content/browser/preloading/prefetch/prefetch_params.h
@@ -9,6 +9,7 @@
 #include <string_view>
 
 #include "base/time/time.h"
+#include "content/browser/preloading/prefetch/prefetch_type.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom.h"
 #include "url/gurl.h"
@@ -106,13 +107,12 @@
 
 // Whether or not |PrefetchService| should block until the head of a prefetch
 // request is received when considering to serve a prefetch for a navigation.
-bool PrefetchShouldBlockUntilHead(
-    blink::mojom::SpeculationEagerness prefetch_eagerness);
+bool PrefetchShouldBlockUntilHead(const PrefetchType& prefetch_type);
 
 // The maximum amount of time to block until the head of a prefetch is received.
 // If the value is zero or less, then a navigation can be blocked indefinitely.
 CONTENT_EXPORT base::TimeDelta PrefetchBlockUntilHeadTimeout(
-    blink::mojom::SpeculationEagerness prefetch_eagerness);
+    const PrefetchType& prefetch_type);
 
 // Gets the histogram suffix to use for the given eagerness parameter.
 CONTENT_EXPORT std::string GetPrefetchEagernessHistogramSuffix(
diff --git a/content/browser/preloading/prefetch/prefetch_service.cc b/content/browser/preloading/prefetch/prefetch_service.cc
index ef62778..754fa5d9 100644
--- a/content/browser/preloading/prefetch/prefetch_service.cc
+++ b/content/browser/preloading/prefetch/prefetch_service.cc
@@ -1533,8 +1533,8 @@
           weak_method_factory_.GetWeakPtr(), key,
           prefetch_match_resolver.GetWeakPtr(), prefetch_container.GetURL(),
           prefetch_container.GetWeakPtr()));
-      base::TimeDelta block_until_head_timeout = PrefetchBlockUntilHeadTimeout(
-          prefetch_container.GetPrefetchType().GetEagerness());
+      base::TimeDelta block_until_head_timeout =
+          PrefetchBlockUntilHeadTimeout(prefetch_container.GetPrefetchType());
       if (block_until_head_timeout.is_positive()) {
         std::unique_ptr<base::OneShotTimer> block_until_head_timer =
             std::make_unique<base::OneShotTimer>();
diff --git a/content/browser/preloading/prefetch/prefetch_service_unittest.cc b/content/browser/preloading/prefetch/prefetch_service_unittest.cc
index 39d7fd5..ebe87172 100644
--- a/content/browser/preloading/prefetch/prefetch_service_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_service_unittest.cc
@@ -26,6 +26,7 @@
 #include "content/browser/preloading/prefetch/prefetch_params.h"
 #include "content/browser/preloading/prefetch/prefetch_serving_page_metrics_container.h"
 #include "content/browser/preloading/prefetch/prefetch_status.h"
+#include "content/browser/preloading/prefetch/prefetch_type.h"
 #include "content/browser/preloading/preloading.h"
 #include "content/browser/preloading/preloading_attempt_impl.h"
 #include "content/browser/preloading/preloading_config.h"
@@ -4175,10 +4176,9 @@
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
 
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
-      PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true, GetParam()));
+  PrefetchType prefetch_type(PreloadingTriggerType::kSpeculationRule,
+                             /*use_prefetch_proxy=*/true, GetParam());
+  MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestState(
@@ -4208,7 +4208,7 @@
   std::string histogram_suffix =
       GetPrefetchEagernessHistogramSuffix(GetParam());
   base::TimeDelta block_until_head_timeout =
-      PrefetchBlockUntilHeadTimeout(GetParam());
+      PrefetchBlockUntilHeadTimeout(prefetch_type);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
           "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s",
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 7a1f883..64f527f 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -183,77 +183,6 @@
   raw_ptr<CompositorImpl> compositor_;
 };
 
-class CompositorImpl::HostBeginFrameObserver
-    : public viz::mojom::BeginFrameObserver {
- public:
-  HostBeginFrameObserver(
-      const base::flat_set<raw_ptr<SimpleBeginFrameObserver, CtnExperimental>>&
-          observers,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-      : simple_begin_frame_observers_(observers),
-        task_runner_(std::move(task_runner)) {}
-
-  void OnStandaloneBeginFrame(const viz::BeginFrameArgs& args) override {
-    // Mark the current task as interesting, as it maybe be responsible for
-    // handling input events for flings.
-    base::TaskAnnotator::MarkCurrentTaskAsInterestingForTracing();
-    if (args.type == viz::BeginFrameArgs::MISSED) {
-      return;
-    }
-
-    if (pending_coalesce_callback_) {
-      begin_frame_args_ = args;
-      return;
-    }
-
-    if ((base::TimeTicks::Now() - args.frame_time) > args.interval) {
-      begin_frame_args_ = args;
-      pending_coalesce_callback_ = true;
-      task_runner_->PostDelayedTask(
-          FROM_HERE,
-          base::BindOnce(
-              &CompositorImpl::HostBeginFrameObserver::CoalescedBeginFrame,
-              weak_factory_.GetWeakPtr()),
-          base::Microseconds(1));
-      return;
-    }
-
-    CallObservers(args);
-  }
-
-  mojo::PendingRemote<viz::mojom::BeginFrameObserver> GetBoundRemote() {
-    return receiver_.BindNewPipeAndPassRemote(task_runner_);
-  }
-
- private:
-  void CoalescedBeginFrame() {
-    DCHECK(begin_frame_args_.IsValid());
-    pending_coalesce_callback_ = false;
-    viz::BeginFrameArgs args = begin_frame_args_;
-    begin_frame_args_ = viz::BeginFrameArgs();
-    CallObservers(args);
-  }
-
-  // This may be deleted as part of `CallObservers`.
-  void CallObservers(const viz::BeginFrameArgs& args) {
-    auto observers_copy = *simple_begin_frame_observers_;
-    for (SimpleBeginFrameObserver* simple_observer : observers_copy) {
-      simple_observer->OnBeginFrame(args.frame_time);
-    }
-  }
-
-  const raw_ref<
-      const base::flat_set<raw_ptr<SimpleBeginFrameObserver, CtnExperimental>>>
-      simple_begin_frame_observers_;
-  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  bool pending_coalesce_callback_ = false;
-  viz::BeginFrameArgs begin_frame_args_;
-
-  mojo::Receiver<viz::mojom::BeginFrameObserver> receiver_{this};
-  base::WeakPtrFactory<HostBeginFrameObserver> weak_factory_{this};
-};
-
 class CompositorImpl::ScopedCachedBackBuffer {
  public:
   ScopedCachedBackBuffer(const viz::FrameSinkId& root_sink_id) {
@@ -979,7 +908,7 @@
 }
 
 void CompositorImpl::AddSimpleBeginFrameObserver(
-    SimpleBeginFrameObserver* obs) {
+    ui::HostBeginFrameObserver::SimpleBeginFrameObserver* obs) {
   DCHECK(obs);
   DCHECK(!base::Contains(simple_begin_frame_observers_, obs));
   simple_begin_frame_observers_.insert(obs);
@@ -987,7 +916,7 @@
 }
 
 void CompositorImpl::RemoveSimpleBeginFrameObserver(
-    SimpleBeginFrameObserver* obs) {
+    ui::HostBeginFrameObserver::SimpleBeginFrameObserver* obs) {
   DCHECK(obs);
   DCHECK(base::Contains(simple_begin_frame_observers_, obs));
 
@@ -1009,7 +938,7 @@
     return;
   }
 
-  host_begin_frame_observer_ = std::make_unique<HostBeginFrameObserver>(
+  host_begin_frame_observer_ = std::make_unique<ui::HostBeginFrameObserver>(
       simple_begin_frame_observers_,
       GetUIThreadTaskRunner({BrowserTaskType::kUserInput}));
   display_private_->SetStandaloneBeginFrameObserver(
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index fead4a5..7197fa18 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -40,6 +40,7 @@
 #include "ui/android/window_android.h"
 #include "ui/android/window_android_compositor.h"
 #include "ui/compositor/compositor_lock.h"
+#include "ui/compositor/host_begin_frame_observer.h"
 #include "ui/display/display_observer.h"
 #include "ui/gl/android/scoped_a_native_window.h"
 
@@ -90,17 +91,13 @@
   }
   cc::slim::LayerTree* GetLayerTreeForTesting() const { return host_.get(); }
 
-  class SimpleBeginFrameObserver {
-   public:
-    virtual ~SimpleBeginFrameObserver() = default;
-    virtual void OnBeginFrame(base::TimeTicks frame_begin_time) = 0;
-  };
-  void AddSimpleBeginFrameObserver(SimpleBeginFrameObserver* obs);
-  void RemoveSimpleBeginFrameObserver(SimpleBeginFrameObserver* obs);
+  void AddSimpleBeginFrameObserver(
+      ui::HostBeginFrameObserver::SimpleBeginFrameObserver* obs);
+  void RemoveSimpleBeginFrameObserver(
+      ui::HostBeginFrameObserver::SimpleBeginFrameObserver* obs);
 
  private:
   class AndroidHostDisplayClient;
-  class HostBeginFrameObserver;
   class ScopedCachedBackBuffer;
   class ReadbackRefImpl;
 
@@ -266,9 +263,10 @@
 
   ui::CompositorLockManager lock_manager_;
 
-  base::flat_set<raw_ptr<SimpleBeginFrameObserver, CtnExperimental>>
+  base::flat_set<raw_ptr<ui::HostBeginFrameObserver::SimpleBeginFrameObserver,
+                         CtnExperimental>>
       simple_begin_frame_observers_;
-  std::unique_ptr<HostBeginFrameObserver> host_begin_frame_observer_;
+  std::unique_ptr<ui::HostBeginFrameObserver> host_begin_frame_observer_;
 
   base::WeakPtrFactory<CompositorImpl> weak_factory_{this};
 };
diff --git a/content/browser/renderer_host/input/fling_scheduler_android.cc b/content/browser/renderer_host/input/fling_scheduler_android.cc
index 84c5453..e806fd3 100644
--- a/content/browser/renderer_host/input/fling_scheduler_android.cc
+++ b/content/browser/renderer_host/input/fling_scheduler_android.cc
@@ -150,10 +150,18 @@
   RemoveCompositorTick();
 }
 
-void FlingSchedulerAndroid::OnBeginFrame(base::TimeTicks frame_begin_time) {
+void FlingSchedulerAndroid::OnBeginFrame(base::TimeTicks frame_begin_time,
+                                         base::TimeDelta frame_interval) {
   DCHECK(observed_compositor_);
   if (fling_controller_)
     fling_controller_->ProgressFling(frame_begin_time);
 }
 
+void FlingSchedulerAndroid::OnBeginFrameSourceShuttingDown() {
+  if (observed_compositor_) {
+    observed_compositor_->RemoveSimpleBeginFrameObserver(this);
+    observed_compositor_ = nullptr;
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/input/fling_scheduler_android.h b/content/browser/renderer_host/input/fling_scheduler_android.h
index dde705e82..ccbe0f2 100644
--- a/content/browser/renderer_host/input/fling_scheduler_android.h
+++ b/content/browser/renderer_host/input/fling_scheduler_android.h
@@ -12,6 +12,7 @@
 #include "ui/android/view_android_observer.h"
 #include "ui/android/window_android.h"
 #include "ui/android/window_android_observer.h"
+#include "ui/compositor/host_begin_frame_observer.h"
 
 namespace content {
 
@@ -21,7 +22,7 @@
     : public FlingSchedulerBase,
       public ui::ViewAndroidObserver,
       public ui::WindowAndroidObserver,
-      public CompositorImpl::SimpleBeginFrameObserver {
+      public ui::HostBeginFrameObserver::SimpleBeginFrameObserver {
  public:
   explicit FlingSchedulerAndroid(RenderWidgetHostImpl* host);
 
@@ -62,8 +63,10 @@
   void OnDetachedFromWindow() override;
   void OnViewAndroidDestroyed() override;
 
-  // CompositorImpl::SimpleBeginFrameObserver implementation.
-  void OnBeginFrame(base::TimeTicks frame_begin_time) override;
+  // ui::HostBeginFrameObserver::SimpleBeginFrameObserver implementation.
+  void OnBeginFrame(base::TimeTicks frame_begin_time,
+                    base::TimeDelta frame_interval) override;
+  void OnBeginFrameSourceShuttingDown() override;
 
   raw_ptr<ui::ViewAndroid> observed_view_ = nullptr;
   raw_ptr<ui::WindowAndroid> observed_window_ = nullptr;
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 9786b96..8203ecd 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -716,6 +716,22 @@
   const size_t kRequestQueueLength =
       blink::features::kSpeculativeServiceWorkerWarmUpRequestQueueLength.Get();
 
+  // Erase redundant warm-up requests.
+  std::vector<ServiceWorkerContext::WarmUpServiceWorkerCallback>
+      callback_for_redundant_requests;
+  std::erase_if(warm_up_requests_, [&](auto& it) {
+    auto& [queued_url, _, queued_callback] = it;
+    if (document_url == queued_url) {
+      callback_for_redundant_requests.push_back(std::move(queued_callback));
+      return true;
+    } else {
+      return false;
+    }
+  });
+  for (auto& cb : callback_for_redundant_requests) {
+    std::move(cb).Run();
+  }
+
   // TODO(crbug.com/1431792): Move `kFifo` to the caller.
   const bool kFifo =
       blink::features::kSpeculativeServiceWorkerWarmUpOnInsertedIntoDom.Get();
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index a6cb098..d0dad33 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -13,7 +13,6 @@
 #include <string>
 #include <vector>
 
-#include "base/containers/circular_deque.h"
 #include "base/containers/id_map.h"
 #include "base/functional/callback.h"
 #include "base/gtest_prod_util.h"
@@ -578,7 +577,7 @@
   bool registrations_initialized_ = false;
   base::OnceClosure on_registrations_initialized_for_test_;
 
-  base::circular_deque<WarmUpRequest> warm_up_requests_;
+  std::deque<WarmUpRequest> warm_up_requests_;
 
   bool is_processing_warming_up_ = false;
 
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 0394ec2..80d9b70 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -588,7 +588,6 @@
       scoped_refptr<net::X509Certificate> cert,
       scoped_refptr<net::SSLPrivateKey> private_key) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    DCHECK((cert && private_key) || (!cert && !private_key));
 
     if (cert && private_key) {
       mojo::PendingRemote<network::mojom::SSLPrivateKey> ssl_private_key;
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc
index 609fde21..9d7b26e 100644
--- a/content/browser/tracing/background_tracing_manager_impl.cc
+++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -459,6 +459,7 @@
     scenarios_.back()->Enable();
   }
   RecordMetric(Metrics::SCENARIO_ACTIVATED_SUCCESSFULLY);
+  DoEmitNamedTrigger(kStartupTracingTriggerName);
   return true;
 }
 
@@ -875,29 +876,36 @@
   system_profile_recorder_ = std::move(recorder);
 }
 
-void BackgroundTracingManagerImpl::SetNamedTriggerCallback(
+void BackgroundTracingManagerImpl::AddNamedTriggerObserver(
     const std::string& trigger_name,
-    base::RepeatingCallback<bool()> callback) {
-  if (!callback) {
-    named_trigger_callbacks_.erase(trigger_name);
-  } else {
-    named_trigger_callbacks_.emplace(trigger_name, std::move(callback));
-  }
+    BackgroundTracingRule* observer) {
+  named_trigger_observers_[trigger_name].AddObserver(observer);
+}
+
+void BackgroundTracingManagerImpl::RemoveNamedTriggerObserver(
+    const std::string& trigger_name,
+    BackgroundTracingRule* observer) {
+  named_trigger_observers_[trigger_name].RemoveObserver(observer);
 }
 
 bool BackgroundTracingManagerImpl::DoEmitNamedTrigger(
     const std::string& trigger_name) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  auto it = named_trigger_callbacks_.find(trigger_name);
-  if (it == named_trigger_callbacks_.end()) {
+  auto it = named_trigger_observers_.find(trigger_name);
+  if (it == named_trigger_observers_.end()) {
     return false;
   }
-  return it->second.Run();
+  for (BackgroundTracingRule& obs : it->second) {
+    if (obs.OnRuleTriggered()) {
+      return true;
+    }
+  }
+  return false;
 }
 
 void BackgroundTracingManagerImpl::InvalidateTriggersCallbackForTesting() {
-  named_trigger_callbacks_.clear();
+  named_trigger_observers_.clear();
 }
 
 void BackgroundTracingManagerImpl::OnStartTracingDone() {
diff --git a/content/browser/tracing/background_tracing_manager_impl.h b/content/browser/tracing/background_tracing_manager_impl.h
index 4b3d5fb..a192519 100644
--- a/content/browser/tracing/background_tracing_manager_impl.h
+++ b/content/browser/tracing/background_tracing_manager_impl.h
@@ -15,6 +15,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/no_destructor.h"
+#include "base/observer_list.h"
 #include "base/threading/sequence_bound.h"
 #include "base/timer/timer.h"
 #include "base/token.h"
@@ -121,8 +122,10 @@
                  const BackgroundTracingRule* triggered_rule,
                  std::string&& serialized_trace) override;
 
-  void SetNamedTriggerCallback(const std::string& trigger_name,
-                               base::RepeatingCallback<bool()> callback);
+  void AddNamedTriggerObserver(const std::string& trigger_name,
+                               BackgroundTracingRule* observer);
+  void RemoveNamedTriggerObserver(const std::string& trigger_name,
+                                  BackgroundTracingRule* observer);
 
   bool HasTraceToUpload() override;
   void GetTraceToUpload(
@@ -225,8 +228,8 @@
 
   bool requires_anonymized_data_ = true;
 
-  std::map<std::string, base::RepeatingCallback<bool()>>
-      named_trigger_callbacks_;
+  std::map<std::string, base::ObserverList<BackgroundTracingRule>>
+      named_trigger_observers_;
 
   // Note, these sets are not mutated during iteration so it is okay to not use
   // base::ObserverList.
diff --git a/content/browser/tracing/background_tracing_rule.cc b/content/browser/tracing/background_tracing_rule.cc
index c17a4f0..4eb89d73 100644
--- a/content/browser/tracing/background_tracing_rule.cc
+++ b/content/browser/tracing/background_tracing_rule.cc
@@ -216,14 +216,13 @@
   }
 
   void DoInstall() override {
-    BackgroundTracingManagerImpl::GetInstance().SetNamedTriggerCallback(
-        named_event_, base::BindRepeating(&NamedTriggerRule::OnRuleTriggered,
-                                          base::Unretained(this)));
+    BackgroundTracingManagerImpl::GetInstance().AddNamedTriggerObserver(
+        named_event_, this);
   }
 
   void DoUninstall() override {
-    BackgroundTracingManagerImpl::GetInstance().SetNamedTriggerCallback(
-        named_event_, base::NullCallback());
+    BackgroundTracingManagerImpl::GetInstance().RemoveNamedTriggerObserver(
+        named_event_, this);
   }
 
   base::Value::Dict ToDict() const override {
@@ -340,17 +339,16 @@
         base::BindRepeating(&HistogramRule::OnHistogramChangedCallback,
                             base::Unretained(this), histogram_lower_value_,
                             histogram_upper_value_));
-    BackgroundTracingManagerImpl::GetInstance().SetNamedTriggerCallback(
-        rule_id(), base::BindRepeating(&HistogramRule::OnRuleTriggered,
-                                       base::Unretained(this)));
+    BackgroundTracingManagerImpl::GetInstance().AddNamedTriggerObserver(
+        rule_id(), this);
     BackgroundTracingManagerImpl::GetInstance().AddAgentObserver(this);
   }
 
   void DoUninstall() override {
     histogram_sample_callback_.reset();
     BackgroundTracingManagerImpl::GetInstance().RemoveAgentObserver(this);
-    BackgroundTracingManagerImpl::GetInstance().SetNamedTriggerCallback(
-        rule_id(), base::NullCallback());
+    BackgroundTracingManagerImpl::GetInstance().RemoveNamedTriggerObserver(
+        rule_id(), this);
   }
 
   base::Value::Dict ToDict() const override {
diff --git a/content/browser/tracing/background_tracing_rule.h b/content/browser/tracing/background_tracing_rule.h
index 18a8f98..87931616 100644
--- a/content/browser/tracing/background_tracing_rule.h
+++ b/content/browser/tracing/background_tracing_rule.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/observer_list_types.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
 #include "content/browser/tracing/background_tracing_config_impl.h"
@@ -16,7 +17,7 @@
 
 namespace content {
 
-class CONTENT_EXPORT BackgroundTracingRule {
+class CONTENT_EXPORT BackgroundTracingRule : public base::CheckedObserver {
  public:
   using MetadataProto =
       perfetto::protos::pbzero::BackgroundTracingMetadata::TriggerRule;
@@ -30,7 +31,7 @@
   BackgroundTracingRule(const BackgroundTracingRule&) = delete;
   BackgroundTracingRule& operator=(const BackgroundTracingRule&) = delete;
 
-  virtual ~BackgroundTracingRule();
+  ~BackgroundTracingRule() override;
 
   virtual void Install(RuleTriggeredCallback);
   virtual void Uninstall();
@@ -55,12 +56,13 @@
 
   bool is_crash() const { return is_crash_; }
 
+  bool OnRuleTriggered();
+
  protected:
   virtual std::string GetDefaultRuleId() const;
 
   virtual void DoInstall() = 0;
   virtual void DoUninstall() = 0;
-  bool OnRuleTriggered();
 
   bool installed() const { return installed_; }
 
diff --git a/content/browser/tracing/tracing_scenario.cc b/content/browser/tracing/tracing_scenario.cc
index 45829bf..2e6b27e3 100644
--- a/content/browser/tracing/tracing_scenario.cc
+++ b/content/browser/tracing/tracing_scenario.cc
@@ -15,12 +15,18 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/tracing/public/cpp/perfetto/perfetto_config.h"
+#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
+#include "services/tracing/public/cpp/triggers_data_source.h"
 #include "third_party/perfetto/protos/perfetto/config/track_event/track_event_config.gen.h"
 
 namespace content {
 
 namespace {
 
+#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
+constexpr uint32_t kStartupTracingTimeoutMs = 30 * 1000;  // 30 sec
+#endif
+
 bool AppendRules(const std::vector<perfetto::protos::gen::TriggerRule>& configs,
                  std::vector<std::unique_ptr<BackgroundTracingRule>>& rules) {
   for (const auto& rule_config : configs) {
@@ -142,6 +148,7 @@
   if (current_state() != State::kEnabled) {
     return false;
   }
+  tracing::TriggersDataSource::EmitTrigger(triggered_rule->rule_id());
   for (auto& rule : start_rules_) {
     rule->Uninstall();
   }
@@ -161,6 +168,7 @@
 bool NestedTracingScenario::OnStopTrigger(
     const BackgroundTracingRule* triggered_rule) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  tracing::TriggersDataSource::EmitTrigger(triggered_rule->rule_id());
   for (auto& rule : stop_rules_) {
     rule->Uninstall();
   }
@@ -403,12 +411,24 @@
   }
 
   SetState(State::kRecording);
+
+#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
+  perfetto::Tracing::SetupStartupTracingOpts opts;
+  opts.timeout_ms = kStartupTracingTimeoutMs;
+  opts.backend = perfetto::kCustomBackend;
+  tracing::PerfettoTracedProcess::Get()->RequestStartupTracing(trace_config_,
+                                                               opts);
+#endif
+
   tracing_session_->SetOnStopCallback([task_runner = task_runner_,
                                        weak_ptr = GetWeakPtr()]() {
     task_runner->PostTask(
         FROM_HERE, base::BindOnce(&TracingScenario::OnTracingStop, weak_ptr));
   });
   tracing_session_->Start();
+  if (triggered_rule) {
+    tracing::TriggersDataSource::EmitTrigger(triggered_rule->rule_id());
+  }
   return true;
 }
 
@@ -416,6 +436,7 @@
     const BackgroundTracingRule* triggered_rule) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  tracing::TriggersDataSource::EmitTrigger(triggered_rule->rule_id());
   for (auto& rule : stop_rules_) {
     rule->Uninstall();
   }
@@ -450,6 +471,7 @@
     const BackgroundTracingRule* triggered_rule) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  tracing::TriggersDataSource::EmitTrigger(triggered_rule->rule_id());
   for (auto& rule : stop_rules_) {
     rule->Uninstall();
   }
diff --git a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
index 3051d39..83a930e 100644
--- a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
@@ -910,7 +910,7 @@
 // by running generate-test-certs.sh and generate-test-sxgs.sh in
 // src/content/test/data/sxg. See https://crbug.com/1279652.
 IN_PROC_BROWSER_TEST_F(SignedExchangeRequestHandlerRealCertVerifierBrowserTest,
-                       DISABLED_Basic) {
+                       Basic) {
   InstallUrlInterceptor(
       GURL("https://cert.example.org/cert.msg"),
       "content/test/data/sxg/test.example.org-long-validity.public.pem.cbor");
diff --git a/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc b/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc
index 0eebc46..88e8de5 100644
--- a/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc
+++ b/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc
@@ -24,7 +24,7 @@
 
 // See content/test/data/sxg/README on how to generate these data.
 // clang-format off
-constexpr char kSignatureHeaderECDSAP256[] = R"(label;cert-sha256=*4gNFIZRsc0QyiaUY/ekUZU6h3q/1mtQafd53/yaF5Ms=*;cert-url="https://example.com/cert.msg";date=1517892341;expires=1517895941;integrity="digest/mi-sha256-03";sig=*MEYCIQDLap5Ns9tI0JmCr1nc58GTHqzyfWJmTiZ+AIPt0OBE6gIhAJ8uHk3RyxX0/pnMmmKKdr63T0XHqyz00aaxuECJ4Ez/*;validity-url="https://test.example.org/resource.validity.msg")";
+constexpr char kSignatureHeaderECDSAP256[] = R"(label;cert-sha256=*Pk1v56luvimpV9pI8xUFDtIh5jJb5T7LEmCnwvC5p2U=*;cert-url="https://example.com/cert.msg";date=1517892341;expires=1517895941;integrity="digest/mi-sha256-03";sig=*MEQCID7HTBrxbUl1n0dVg0S7DtF2DatBiYBzKQCAjHgrmL2YAiBjTLJbkQ0HQBexcpkDxhhCJZN8qgZUS2CDcHO7r48DMQ==*;validity-url="https://test.example.org/resource.validity.msg")";
 constexpr uint8_t kCborHeadersECDSAP256[] = {
   0xa4, 0x46, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x58, 0x39, 0x6d, 0x69,
   0x2d, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x2d, 0x30, 0x33, 0x3d, 0x77,
@@ -40,7 +40,7 @@
   0x69, 0x6e, 0x67, 0x4c, 0x6d, 0x69, 0x2d, 0x73, 0x68, 0x61, 0x32, 0x35,
   0x36, 0x2d, 0x30, 0x33
 };
-constexpr char kSignatureHeaderECDSAP384[] = R"(label;cert-sha256=*KrfLZg1xcHbdKYZ1nb8cnUjp/6iaEo6LeQrRSV6StKw=*;cert-url="https://example.com/cert.msg";date=1517892341;expires=1517895941;integrity="digest/mi-sha256-03";sig=*MGUCMBTSH0TpKGv5JSspWU+7hYeSDwaoRzYDzxxQaDQ2kV/IQS+3bQd4SFm0dPvsPzJH7AIxAPT1MXZoEiDhu45Ssr+ubU8n68QZZ92eI7TvtsEF1LEAXtx2YYC2UARu6ok9UxtrZQ==*;validity-url="https://test.example.org/resource.validity.msg")";
+constexpr char kSignatureHeaderECDSAP384[] = R"(label;cert-sha256=*zXJfOCr77C3XNWxrPrhWNh8nsLK4jhW5neDBRIzario=*;cert-url="https://example.com/cert.msg";date=1517892341;expires=1517895941;integrity="digest/mi-sha256-03";sig=*MGQCMDLDn/k5ToXnmxOOcL80NAU6JrLUNfXvE05BdTN0N67z9fFeoZiCID+5x9oapey7SgIwOYhaIX2Lbm0i0wCY6+WSbGgsgp9HWu+utJhXJYLR4cFkCxLEpMCmARyeDaGQvWzU*;validity-url="https://test.example.org/resource.validity.msg")";
 // clang-format on
 
 // |expires| (1518497142) is more than 7 days (604800 seconds) after |date|
diff --git a/content/browser/web_package/signed_exchange_test_utils.h b/content/browser/web_package/signed_exchange_test_utils.h
index 779fa21..7996614 100644
--- a/content/browser/web_package/signed_exchange_test_utils.h
+++ b/content/browser/web_package/signed_exchange_test_utils.h
@@ -8,9 +8,9 @@
 namespace content {
 
 constexpr char kPEMECDSAP256SPKIHash[] =
-    "Roij6gF5orGtG9K8u8PkTe1LiRbVklxLM+EVABTK8I0=";
+    "ZFO6LtBASoBUvOnNeXEuD3YGnUHS+eFQBZoRemB8YCs=";
 constexpr char kPEMECDSAP384SPKIHash[] =
-    "wfk/4dogHdX/MbLIvpjbKQ7fhO+ovkX2wjzDPSrIsMY=";
+    "DiD+PNhd9XvhGqZap4Hrf0kwWui7Kf5SI5bSLfHC9C4=";
 
 }  // namespace content
 
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index f24acf2..21fc25a 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -799,6 +799,8 @@
   if (IsFedCmButtonModeEnabled() &&
       idp_get_params_ptrs[0]->mode == blink::mojom::RpMode::kButton) {
     rp_mode_ = RpMode::kButton;
+    // TODO(crbug.com/329235198): Support other mediation mode in button mode.
+    mediation_requirement_ = MediationRequirement::kRequired;
     if (!had_transient_user_activation_) {
       render_frame_host().AddMessageToConsole(
           blink::mojom::ConsoleMessageLevel::kError,
@@ -1424,7 +1426,7 @@
     if (idp_info_it != idp_infos_.end() && idp_info_it->second->data) {
       idp_data_for_display_.push_back(*idp_info_it->second->data);
     }
-    if (IsFedCmUseOtherAccountEnabled()) {
+    if (IsFedCmUseOtherAccountEnabled(rp_mode_ == RpMode::kButton)) {
       if (!login_url_.is_empty() &&
           login_url_ == idp_info_it->second->metadata.idp_login_url) {
         for (const auto& account : idp_info_it->second->data->accounts) {
@@ -2894,7 +2896,7 @@
   }
   permission_delegate_->AddIdpSigninStatusObserver(this);
 
-  if (IsFedCmUseOtherAccountEnabled()) {
+  if (IsFedCmUseOtherAccountEnabled(rp_mode_ == RpMode::kButton)) {
     account_ids_before_login_.clear();
     for (const auto& idp_data : idp_data_for_display_) {
       if (idp_data.idp_metadata.idp_login_url == login_url) {
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 84987c0..51a86a1 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -6517,76 +6517,21 @@
       HasSharingPermission(OriginFromString(kRpUrl), OriginFromString(kRpUrl),
                            OriginFromString(kProviderUrlFull),
                            Optional(std::string(kAccountId))))
-      .Times(2)
-      .WillRepeatedly(Return(true));
-
-  // Pretend the auto re-authn permission has been granted.
-  EXPECT_CALL(*test_auto_reauthn_permission_delegate_,
-              IsAutoReauthnSettingEnabled())
       .WillOnce(Return(true));
-  EXPECT_CALL(*test_auto_reauthn_permission_delegate_,
-              IsAutoReauthnEmbargoed(OriginFromString(kRpUrl)))
-      .WillOnce(Return(false));
 
   for (const auto& idp_info : kConfigurationValid.idp_info) {
     ASSERT_EQ(idp_info.second.accounts.size(), 1u);
   }
-
-  std::unique_ptr<IdpNetworkRequestManagerParamChecker> checker =
-      std::make_unique<IdpNetworkRequestManagerParamChecker>();
-  checker->SetExpectedTokenPostData("client_id=" + std::string(kClientId) +
-                                    "&nonce=" + std::string(kNonce) +
-                                    "&account_id=" + std::string(kAccountId) +
-                                    "&disclosure_text_shown=false" +
-                                    "&is_auto_selected=true" + "&mode=button");
-  SetNetworkRequestManager(std::move(checker));
-
-  static_cast<TestRenderFrameHost*>(web_contents()->GetPrimaryMainFrame())
-      ->SimulateUserActivation();
-
-  RequestParameters parameters = kDefaultRequestParameters;
-  parameters.rp_mode = blink::mojom::RpMode::kButton;
-
-  MockConfiguration config = kConfigurationValid;
-  config.rp_mode = blink::mojom::RpMode::kButton;
-
-  RequestExpectations expectation = kExpectationSuccess;
-  expectation.is_auto_selected = true;
-
-  RunAuthTest(parameters, expectation, config);
-
-  histogram_tester_.ExpectTotalCount("Blink.FedCm.Timing.ShowAccountsDialog",
-                                     0);
-
-  ExpectAutoReauthnMetrics(FedCmMetrics::NumAccounts::kOne,
-                           /*expected_succeeded=*/true,
-                           /*expected_auto_reauthn_setting_blocked=*/false,
-                           /*expected_auto_reauthn_embargoed=*/false,
-                           /*expected_prevent_silent_access=*/false);
-}
-
-// Test that auto re-authn in button mode does not have delay for the auto
-// reauthn UI.
-TEST_F(FederatedAuthRequestImplTest, AutoReauthnInButtonModeHasNoRequestDelay) {
-  base::test::ScopedFeatureList list;
-  list.InitAndEnableFeature(features::kFedCmButtonMode);
-
-  // Pretend the sharing permission has been granted for this account.
-  EXPECT_CALL(
-      *test_permission_delegate_,
-      HasSharingPermission(OriginFromString(kRpUrl), OriginFromString(kRpUrl),
-                           OriginFromString(kProviderUrlFull),
-                           Optional(std::string(kAccountId))))
-      .Times(2)
-      .WillRepeatedly(Return(true));
-
-  // Pretend the auto re-authn permission has been granted.
-  EXPECT_CALL(*test_auto_reauthn_permission_delegate_,
-              IsAutoReauthnSettingEnabled())
-      .WillOnce(Return(true));
+  // The following checks are skipped in button mode.
   EXPECT_CALL(*test_auto_reauthn_permission_delegate_,
               IsAutoReauthnEmbargoed(OriginFromString(kRpUrl)))
-      .WillOnce(Return(false));
+      .Times(0);
+  EXPECT_CALL(*test_auto_reauthn_permission_delegate_,
+              RequiresUserMediation(GURL(kRpUrl)))
+      .Times(0);
+  EXPECT_CALL(*test_auto_reauthn_permission_delegate_,
+              IsAutoReauthnSettingEnabled())
+      .Times(0);
 
   static_cast<TestRenderFrameHost*>(web_contents()->GetPrimaryMainFrame())
       ->SimulateUserActivation();
@@ -6594,23 +6539,11 @@
   RequestParameters parameters = kDefaultRequestParameters;
   parameters.rp_mode = blink::mojom::RpMode::kButton;
 
-  MockConfiguration config = kConfigurationValid;
-  config.rp_mode = blink::mojom::RpMode::kButton;
+  RunAuthDontWaitForCallback(parameters, kConfigurationValid);
 
-  RequestExpectations expectation = kExpectationSuccess;
-  expectation.is_auto_selected = true;
-
-  // Note that we do not call `WaitForCurrentAuthRequest` to fast-forward. If
-  // request delay is required, the test would fail.
-  RunAuthDontWaitForCallback(parameters, config);
-
-  CheckAuthExpectations(config, expectation);
-
-  ExpectAutoReauthnMetrics(FedCmMetrics::NumAccounts::kOne,
-                           /*expected_succeeded=*/true,
-                           /*expected_auto_reauthn_setting_blocked=*/false,
-                           /*expected_auto_reauthn_embargoed=*/false,
-                           /*expected_prevent_silent_access=*/false);
+  ASSERT_EQ(displayed_accounts().size(), 1u);
+  EXPECT_EQ(CountNumLoginStateIsSignin(), 1);
+  EXPECT_EQ(dialog_controller_state_.sign_in_mode, SignInMode::kExplicit);
 }
 
 // Test button flow is exempted if the FedCM is disabled in  settings.
diff --git a/content/browser/webid/flags.cc b/content/browser/webid/flags.cc
index 9068512..c4347d1 100644
--- a/content/browser/webid/flags.cc
+++ b/content/browser/webid/flags.cc
@@ -76,8 +76,12 @@
   return base::FeatureList::IsEnabled(features::kFedCmDisconnect);
 }
 
-bool IsFedCmUseOtherAccountEnabled() {
-  return base::FeatureList::IsEnabled(features::kFedCmUseOtherAccount);
+bool IsFedCmUseOtherAccountEnabled(bool is_button_mode) {
+  // TODO(crbug.com/328470597): this feature is bundled with the button mode at
+  // the moment. We should decouple them when supporting the feature in the
+  // widget flow.
+  return base::FeatureList::IsEnabled(features::kFedCmUseOtherAccount) ||
+         (IsFedCmButtonModeEnabled() && is_button_mode);
 }
 
 bool IsFedCmExemptIdpWithThirdPartyCookiesEnabled() {
diff --git a/content/browser/webid/flags.h b/content/browser/webid/flags.h
index 9bfa5ff..3bcaec7b 100644
--- a/content/browser/webid/flags.h
+++ b/content/browser/webid/flags.h
@@ -60,7 +60,7 @@
 bool IsFedCmDisconnectEnabled();
 
 // Whether "Use Other Account" is enabled.
-bool IsFedCmUseOtherAccountEnabled();
+bool IsFedCmUseOtherAccountEnabled(bool is_button_mode);
 
 // Whether the ExemptIdpWithThirdPartyCookies feature is enabled.
 bool IsFedCmExemptIdpWithThirdPartyCookiesEnabled();
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
index 5876e2c..8606b1fe 100644
--- a/content/browser/webid/idp_network_request_manager.cc
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -535,7 +535,7 @@
   }
   idp_metadata.idp_login_url =
       ExtractEndpoint(provider, response, kLoginUrlKey);
-  if (IsFedCmUseOtherAccountEnabled()) {
+  if (IsFedCmUseOtherAccountEnabled(rp_mode == blink::mojom::RpMode::kButton)) {
     const base::Value::Dict* modes_dict = response.FindDict(kModesKey);
     const base::Value::Dict* selected_mode_dict = nullptr;
     if (modes_dict) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoBuilder.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoBuilder.java
index 60cfcd9..731b03bb 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoBuilder.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoBuilder.java
@@ -156,9 +156,6 @@
 
         // Set of coordinates for providing the correct size and scroll of the View.
         AccessibilityDelegate.AccessibilityCoordinates getAccessibilityCoordinates();
-
-        // EXPERIMENTAL - Returns the Java-side cached node so JNI can pass only the id.
-        AccessibilityNodeInfoCompat getInfo(int virtualViewId);
     }
 
     public final BuilderDelegate mDelegate;
@@ -667,234 +664,4 @@
             rect.bottom = viewportRectBottom;
         }
     }
-
-    // ---------------------------------[ EXPERIMENTAL ]---------------------------------------- //
-    // Although verbose, we need to provide separate JNI bindings for the differing signatures.
-    // However, we can call the existing methods after fetching the object from the cache.
-
-    @CalledByNative
-    private void addAccessibilityNodeInfoChildren_exp(int virtualViewId, int[] childIds) {
-        addAccessibilityNodeInfoChildren(mDelegate.getInfo(virtualViewId), childIds);
-    }
-
-    @CalledByNative
-    private void setAccessibilityNodeInfoBooleanAttributes_exp(
-            int virtualViewId,
-            boolean checkable,
-            boolean checked,
-            boolean clickable,
-            boolean contentInvalid,
-            boolean enabled,
-            boolean focusable,
-            boolean focused,
-            boolean hasImage,
-            boolean password,
-            boolean scrollable,
-            boolean selected,
-            boolean visibleToUser,
-            boolean hasCharacterLocations) {
-
-        setAccessibilityNodeInfoBooleanAttributes(
-                mDelegate.getInfo(virtualViewId),
-                virtualViewId,
-                checkable,
-                checked,
-                clickable,
-                contentInvalid,
-                enabled,
-                focusable,
-                focused,
-                hasImage,
-                password,
-                scrollable,
-                selected,
-                visibleToUser,
-                hasCharacterLocations);
-    }
-
-    @CalledByNative
-    private void addAccessibilityNodeInfoActions_exp(
-            int virtualViewId,
-            boolean canScrollForward,
-            boolean canScrollBackward,
-            boolean canScrollUp,
-            boolean canScrollDown,
-            boolean canScrollLeft,
-            boolean canScrollRight,
-            boolean clickable,
-            boolean editableText,
-            boolean enabled,
-            boolean focusable,
-            boolean focused,
-            boolean isCollapsed,
-            boolean isExpanded,
-            boolean hasNonEmptyValue,
-            boolean hasNonEmptyInnerText,
-            boolean isSeekControl,
-            boolean isForm) {
-
-        addAccessibilityNodeInfoActions(
-                mDelegate.getInfo(virtualViewId),
-                virtualViewId,
-                canScrollForward,
-                canScrollBackward,
-                canScrollUp,
-                canScrollDown,
-                canScrollLeft,
-                canScrollRight,
-                clickable,
-                editableText,
-                enabled,
-                focusable,
-                focused,
-                isCollapsed,
-                isExpanded,
-                hasNonEmptyValue,
-                hasNonEmptyInnerText,
-                isSeekControl,
-                isForm);
-    }
-
-    @CalledByNative
-    private void setAccessibilityNodeInfoBaseAttributes_exp(
-            int virtualViewId,
-            int parentId,
-            String className,
-            String role,
-            String roleDescription,
-            String hint,
-            String targetUrl,
-            boolean canOpenPopup,
-            boolean multiLine,
-            int inputType,
-            int liveRegion,
-            String errorMessage,
-            int clickableScore,
-            String display,
-            String brailleLabel,
-            String brailleRoleDescription) {
-
-        setAccessibilityNodeInfoBaseAttributes(
-                mDelegate.getInfo(virtualViewId),
-                virtualViewId,
-                parentId,
-                className,
-                role,
-                roleDescription,
-                hint,
-                targetUrl,
-                canOpenPopup,
-                multiLine,
-                inputType,
-                liveRegion,
-                errorMessage,
-                clickableScore,
-                display,
-                brailleLabel,
-                brailleRoleDescription);
-    }
-
-    @SuppressLint("NewApi")
-    @CalledByNative
-    protected void setAccessibilityNodeInfoText_exp(
-            int virtualViewId,
-            String text,
-            String targetUrl,
-            boolean annotateAsLink,
-            boolean isEditableText,
-            String language,
-            int[] suggestionStarts,
-            int[] suggestionEnds,
-            String[] suggestions,
-            String stateDescription) {
-
-        setAccessibilityNodeInfoText(
-                mDelegate.getInfo(virtualViewId),
-                text,
-                targetUrl,
-                annotateAsLink,
-                isEditableText,
-                language,
-                suggestionStarts,
-                suggestionEnds,
-                suggestions,
-                stateDescription);
-    }
-
-    @CalledByNative
-    protected void setAccessibilityNodeInfoLocation_exp(
-            final int virtualViewId,
-            int absoluteLeft,
-            int absoluteTop,
-            int parentRelativeLeft,
-            int parentRelativeTop,
-            int width,
-            int height,
-            boolean isOffscreen) {
-
-        setAccessibilityNodeInfoLocation(
-                mDelegate.getInfo(virtualViewId),
-                virtualViewId,
-                absoluteLeft,
-                absoluteTop,
-                parentRelativeLeft,
-                parentRelativeTop,
-                width,
-                height,
-                isOffscreen);
-    }
-
-    @CalledByNative
-    protected void setAccessibilityNodeInfoCollectionInfo_exp(
-            int virtualViewId, int rowCount, int columnCount, boolean hierarchical) {
-        setAccessibilityNodeInfoCollectionInfo(
-                mDelegate.getInfo(virtualViewId), rowCount, columnCount, hierarchical);
-    }
-
-    @CalledByNative
-    protected void setAccessibilityNodeInfoCollectionItemInfo_exp(
-            int virtualViewId,
-            int rowIndex,
-            int rowSpan,
-            int columnIndex,
-            int columnSpan,
-            boolean heading) {
-
-        setAccessibilityNodeInfoCollectionItemInfo(
-                mDelegate.getInfo(virtualViewId),
-                rowIndex,
-                rowSpan,
-                columnIndex,
-                columnSpan,
-                heading);
-    }
-
-    @CalledByNative
-    protected void setAccessibilityNodeInfoRangeInfo_exp(
-            int virtualViewId, int rangeType, float min, float max, float current) {
-        setAccessibilityNodeInfoRangeInfo(
-                mDelegate.getInfo(virtualViewId), rangeType, min, max, current);
-    }
-
-    @CalledByNative
-    protected void setAccessibilityNodeInfoViewIdResourceName_exp(
-            int virtualViewId, String viewIdResourceName) {
-        setAccessibilityNodeInfoViewIdResourceName(
-                mDelegate.getInfo(virtualViewId), viewIdResourceName);
-    }
-
-    @CalledByNative
-    protected void setAccessibilityNodeInfoPaneTitle_exp(int virtualViewId, String title) {
-        setAccessibilityNodeInfoPaneTitle(mDelegate.getInfo(virtualViewId), title);
-    }
-
-    @CalledByNative
-    protected void setAccessibilityNodeInfoSelectionAttrs_exp(
-            int virtualViewId, int startIndex, int endIndex) {
-        setAccessibilityNodeInfoSelectionAttrs(
-                mDelegate.getInfo(virtualViewId), startIndex, endIndex);
-    }
-
-    // ----------------------------------------------------------------------------------------- //
-
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/AssistDataBuilder.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/AssistDataBuilder.java
index f81b90d8..72e54b7 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/AssistDataBuilder.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/AssistDataBuilder.java
@@ -5,12 +5,16 @@
 package org.chromium.content.browser.accessibility;
 
 import android.app.assist.AssistStructure.ViewNode;
+import android.os.Bundle;
 import android.view.ViewStructure;
 import android.view.ViewStructure.HtmlInfo;
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
 /**
  * Basic helper class to build ViewStructure objects for the WebContents in Chrome. The tree is
  * commonly referred to as the "AssistData" tree, hence the name, and to keep it separate from the
@@ -21,6 +25,9 @@
  */
 @JNINamespace("content")
 public class AssistDataBuilder {
+    private static final String CSS_DISPLAY_BUNDLE_KEY = "display";
+    private static final String METADATA_BUNDLE_KEY = "metadata";
+
     @CalledByNative
     public void populateBaseProperties(ViewStructure node, String className, int childCount) {
         node.setClassName(className);
@@ -70,16 +77,19 @@
             return;
         }
 
-        htmlBuilder.addAttribute("display", cssDisplay);
+        htmlBuilder.addAttribute(CSS_DISPLAY_BUNDLE_KEY, cssDisplay);
         for (String[] attr : htmlAttributes) {
             htmlBuilder.addAttribute(attr[0], attr[1]);
         }
         node.setHtmlInfo(htmlBuilder.build());
     }
 
-    // Stubbed.
     @CalledByNative
-    public void populateHTMLMetadataProperties(ViewStructure node) {}
+    public void populateHTMLMetadataProperties(ViewStructure node, String[] metadataStrings) {
+        Bundle extras = node.getExtras();
+        extras.putStringArrayList(
+                METADATA_BUNDLE_KEY, new ArrayList<String>(Arrays.asList(metadataStrings)));
+    }
 
     @CalledByNative
     public ViewStructure addChildNode(ViewStructure node, int childIndex) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index cd0edae..0bc1027e 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -327,18 +327,6 @@
                             public AccessibilityCoordinates getAccessibilityCoordinates() {
                                 return mDelegate.getAccessibilityCoordinates();
                             }
-
-                            @Override
-                            public AccessibilityNodeInfoCompat getInfo(int virtualViewId) {
-                                // There is no implementation for this when the experiment is not
-                                // running, so we will force a crash with assert.
-                                assert ContentFeatureMap.isEnabled(
-                                                ContentFeatureList.ACCESSIBILITY_JNI_OPTIMIZATIONS)
-                                        : "AccessibilityNodeInfoBuilder should not be fetching info"
-                                                + " outside of JNI experiments.";
-
-                                return mNodeInfoCache.get(virtualViewId);
-                            }
                         });
 
         mAutoDisableAccessibilityHandler =
@@ -933,46 +921,6 @@
         // have one in our cache, then communicate this so web_contents_accessibility_android.cc
         // will update a fraction of the object and for the rest leverage what is already there.
         if (mNodeInfoCache.get(virtualViewId) != null) {
-
-            // -----------------------------[ EXPERIMENTAL ]------------------------------------ //
-            // We take a different approach when the JNI testing feature is enabled.
-            if (ContentFeatureMap.isEnabled(ContentFeatureList.ACCESSIBILITY_JNI_OPTIMIZATIONS)) {
-                // We still need to update the source node id, but we do not need to obtain a new
-                // copy of the node until we are ready to return one to the framework.
-                mNodeInfoCache.get(virtualViewId).setSource(mView, virtualViewId);
-
-                if (WebContentsAccessibilityImplJni.get()
-                        .updateCachedAccessibilityNodeInfo_exp(mNativeObj, virtualViewId)) {
-                    // After successfully re-populating this cached node, update the accessibility
-                    // focus since this would not be included in the update call, and set the
-                    // available actions accordingly, then return result.
-                    mNodeInfoCache
-                            .get(virtualViewId)
-                            .setAccessibilityFocused(mAccessibilityFocusId == virtualViewId);
-
-                    if (mAccessibilityFocusId == virtualViewId) {
-                        mNodeInfoCache
-                                .get(virtualViewId)
-                                .addAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS);
-                        mNodeInfoCache.get(virtualViewId).removeAction(ACTION_ACCESSIBILITY_FOCUS);
-                    } else {
-                        mNodeInfoCache
-                                .get(virtualViewId)
-                                .removeAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS);
-                        mNodeInfoCache.get(virtualViewId).addAction(ACTION_ACCESSIBILITY_FOCUS);
-                    }
-
-                    mHistogramRecorder.incrementNodeWasReturnedFromCache();
-                    return AccessibilityNodeInfoCompat.obtain(mNodeInfoCache.get(virtualViewId));
-                } else {
-                    // If the node is no longer valid, wipe it from the cache and return null
-                    mNodeInfoCache.get(virtualViewId).recycle();
-                    mNodeInfoCache.remove(virtualViewId);
-                    return null;
-                }
-            } // End of special case for Finch experiment, below is original code.
-            // --------------------------------------------------------------------------------- //
-
             AccessibilityNodeInfoCompat cachedNode =
                     AccessibilityNodeInfoCompat.obtain(mNodeInfoCache.get(virtualViewId));
 
@@ -1013,28 +961,6 @@
                 info.setParent(mView);
             }
 
-            // -----------------------------[ EXPERIMENTAL ]------------------------------------ //
-            // We take a different approach when the JNI testing feature is enabled.
-            if (ContentFeatureMap.isEnabled(ContentFeatureList.ACCESSIBILITY_JNI_OPTIMIZATIONS)) {
-                // Add this node to the cache, which we will remove if populating fails. By doing
-                // this we can pass only the |virtualViewId| over the JNI boundary.
-                mNodeInfoCache.put(virtualViewId, info);
-
-                if (WebContentsAccessibilityImplJni.get()
-                        .populateAccessibilityNodeInfo_exp(mNativeObj, virtualViewId)) {
-                    // After successfully populating this node, add it to our cache then return.
-                    mHistogramRecorder.incrementNodeWasCreatedFromScratch();
-                    // We still need a local copy of the node before passing back to the framework.
-                    mNodeInfoCache.put(virtualViewId, AccessibilityNodeInfoCompat.obtain(info));
-                    return info;
-                } else {
-                    info.recycle();
-                    mNodeInfoCache.remove(virtualViewId);
-                    return null;
-                }
-            } // End of special case for Finch experiment, below is original code.
-            // --------------------------------------------------------------------------------- //
-
             if (WebContentsAccessibilityImplJni.get()
                     .populateAccessibilityNodeInfo(mNativeObj, info, virtualViewId)) {
                 // After successfully populating this node, add it to our cache then return.
@@ -2244,13 +2170,6 @@
                 AccessibilityNodeInfoCompat info,
                 int id);
 
-        // These two methods are experimental.
-        boolean updateCachedAccessibilityNodeInfo_exp(
-                long nativeWebContentsAccessibilityAndroid, int id);
-
-        boolean populateAccessibilityNodeInfo_exp(
-                long nativeWebContentsAccessibilityAndroid, int id);
-
         boolean populateAccessibilityEvent(
                 long nativeWebContentsAccessibilityAndroid,
                 AccessibilityEvent event,
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
index 0e9aa25..f3384db7 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
@@ -15,8 +15,6 @@
     public static final String ACCESSIBILITY_INCLUDE_LONG_CLICK_ACTION =
             "AccessibilityIncludeLongClickAction";
 
-    public static final String ACCESSIBILITY_JNI_OPTIMIZATIONS = "AccessibilityJNIOptimizations";
-
     public static final String ACCESSIBILITY_PAGE_ZOOM = "AccessibilityPageZoom";
     // Field trial param associated with the Page Zoom feature.
     public static final String ACCESSIBILITY_PAGE_ZOOM_PARAM = "AdjustForOSLevel";
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 4e56d8b29..e9a5d6c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1241,13 +1241,6 @@
              "AccessibilityIncludeLongClickAction",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// When enabled, the Android accessibility code will use an experimental code
-// path that is an alternative to the existing JNI path to see if the path
-// can be made more performant.
-BASE_FEATURE(kAccessibilityJNIOptimizations,
-             "AccessibilityJNIOptimizations",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Allows the use of page zoom in place of accessibility text autosizing, and
 // updated UI to replace existing Chrome Accessibility Settings.
 BASE_FEATURE(kAccessibilityPageZoom,
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 37a4760..016e9a9 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -301,7 +301,6 @@
 
 #if BUILDFLAG(IS_ANDROID)
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kAccessibilityIncludeLongClickAction);
-CONTENT_EXPORT BASE_DECLARE_FEATURE(kAccessibilityJNIOptimizations);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kAccessibilityPageZoom);
 CONTENT_EXPORT extern const base::FeatureParam<bool>
     kAccessibilityPageZoomOSLevelAdjustment;
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index adfbc60..e3eace9 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -434,9 +434,6 @@
   command_line_ =
       std::make_unique<base::CommandLine>(base::CommandLine::NO_PROGRAM);
   params_ = std::make_unique<MainFunctionParams>(command_line_.get());
-  // Platform needs to be initialized before blink::Initialize. This is because
-  // Blink retrieves fonts for NativeThemeFluent, but the platform expects the
-  // font manager singleton to be uninitialized.
   platform_ = std::make_unique<RendererMainPlatformDelegate>(*params_);
   platform_->PlatformInitialize();
 
diff --git a/content/renderer/fluent_scrollbar_browsertest.cc b/content/renderer/fluent_scrollbar_browsertest.cc
deleted file mode 100644
index 713f1c8..0000000
--- a/content/renderer/fluent_scrollbar_browsertest.cc
+++ /dev/null
@@ -1,37 +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 "base/test/scoped_feature_list.h"
-#include "content/public/test/render_view_test.h"
-#include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/public/platform/web_theme_engine.h"
-#include "ui/native_theme/native_theme_features.h"
-
-namespace content {
-
-class FluentOverlayScrollbarImplTest : public RenderViewTest {
- public:
-  explicit FluentOverlayScrollbarImplTest() {
-    feature_list_.InitAndEnableFeature(features::kFluentOverlayScrollbar);
-  }
-
-  ~FluentOverlayScrollbarImplTest() override = default;
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-// Ensures that RenderViewTest based tests can properly initialize when Fluent
-// scrollbars are enabled. At one point RenderViewTest's ordering of platform vs
-// NativeThemeFluent initialization would fail when fluent scrollbars were
-// enabled. See https://crrev.com/c/4257851 for more details.
-TEST_F(FluentOverlayScrollbarImplTest,
-       FluentOverlayScrollbarsInitializeProperly) {
-  blink::WebThemeEngine* theme_engine =
-      blink::Platform::Current()->ThemeEngine();
-  EXPECT_EQ(theme_engine->IsFluentOverlayScrollbarEnabled(),
-            ui::IsFluentScrollbarEnabled());
-}
-
-}  // namespace content
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index 36bb4f10..3bc07da 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -68,6 +68,7 @@
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
 #include "third_party/blink/public/web/modules/mediastream/web_media_player_ms.h"
 #include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_view.h"
 #include "url/origin.h"
 
 #if BUILDFLAG(IS_ANDROID)
@@ -418,10 +419,15 @@
   if (!render_thread)
     return nullptr;
 
+  // There may be many media elements on a page. Creating OS output streams for
+  // each can be very expensive, so we create an audio output sink which can be
+  // shared (if parameters match) with all RenderFrames in the process which
+  // have the same main frame.
   scoped_refptr<media::SwitchableAudioRendererSink> audio_renderer_sink =
       blink::AudioDeviceFactory::GetInstance()->NewMixableSink(
           blink::WebAudioDeviceSourceType::kMediaElement,
           render_frame_->GetWebFrame()->GetLocalFrameToken(),
+          render_frame_->GetWebView()->MainFrame()->GetFrameToken(),
           media::AudioSinkParameters(/*session_id=*/base::UnguessableToken(),
                                      sink_id.Utf8()));
 
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 5b185b6c..7e889d17 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -274,7 +274,7 @@
     ":content_shell_java",
     "//base:base_java",
     "//content/public/android:content_java",
-    "//testing/android/native_test:native_test_java",
+    "//testing/android/native_test:native_browser_test_java",
     "//third_party/androidx:androidx_core_core_java",
     "//ui/android:ui_java",
   ]
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc
index 8b0edcf2..b508519 100644
--- a/content/shell/app/shell_main_delegate.cc
+++ b/content/shell/app/shell_main_delegate.cc
@@ -79,6 +79,8 @@
 
 #include <initguid.h>
 #include "base/logging_win.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/win_util.h"
 #include "content/shell/common/v8_crashpad_support_win.h"
 #endif
 
@@ -92,6 +94,14 @@
 
 namespace {
 
+enum class LoggingDest {
+  kFile,
+  kStderr,
+#if BUILDFLAG(IS_WIN)
+  kHandle,
+#endif
+};
+
 #if !BUILDFLAG(IS_FUCHSIA)
 base::LazyInstance<content::ShellCrashReporterClient>::Leaky
     g_shell_crash_client = LAZY_INSTANCE_INITIALIZER;
@@ -115,20 +125,70 @@
 #endif
 
 void InitLogging(const base::CommandLine& command_line) {
-  base::FilePath log_filename =
-      command_line.GetSwitchValuePath(switches::kLogFile);
-  if (log_filename.empty()) {
+  LoggingDest dest = LoggingDest::kFile;
+
+  if (command_line.GetSwitchValueASCII(switches::kEnableLogging) == "stderr") {
+    dest = LoggingDest::kStderr;
+  }
+
+#if BUILDFLAG(IS_WIN)
+  // On Windows child process may be given a handle in the --log-file switch.
+  base::win::ScopedHandle log_handle;
+  if (command_line.GetSwitchValueASCII(switches::kEnableLogging) == "handle") {
+    auto handle_str = command_line.GetSwitchValueNative(switches::kLogFile);
+    uint32_t handle_value = 0;
+    if (base::StringToUint(handle_str, &handle_value)) {
+      // This handle is owned by the logging framework and is closed when the
+      // process exits.
+      HANDLE duplicate = nullptr;
+      if (::DuplicateHandle(GetCurrentProcess(),
+                            base::win::Uint32ToHandle(handle_value),
+                            GetCurrentProcess(), &duplicate, 0, FALSE,
+                            DUPLICATE_SAME_ACCESS)) {
+        log_handle.Set(duplicate);
+        dest = LoggingDest::kHandle;
+      }
+    }
+  }
+#endif  // BUILDFLAG(IS_WIN)
+
+  base::FilePath log_filename;
+  if (dest == LoggingDest::kFile) {
+    log_filename = command_line.GetSwitchValuePath(switches::kLogFile);
+    if (log_filename.empty()) {
 #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_IOS)
-    base::PathService::Get(base::DIR_TEMP, &log_filename);
+      base::PathService::Get(base::DIR_TEMP, &log_filename);
 #else
-    base::PathService::Get(base::DIR_EXE, &log_filename);
+      base::PathService::Get(base::DIR_EXE, &log_filename);
 #endif
-    log_filename = log_filename.AppendASCII("content_shell.log");
+      log_filename = log_filename.AppendASCII("content_shell.log");
+    }
   }
 
   logging::LoggingSettings settings;
-  settings.logging_dest = logging::LOG_TO_ALL;
-  settings.log_file_path = log_filename.value().c_str();
+#if BUILDFLAG(IS_WIN)
+  if (dest == LoggingDest::kHandle) {
+    // TODO(crbug.com/328285906) Use a ScopedHandle in logging settings.
+    settings.log_file = log_handle.release();
+  } else {
+    settings.log_file = nullptr;
+  }
+#endif  // BUILDFLAG(IS_WIN)
+
+  if (dest == LoggingDest::kFile) {
+    settings.log_file_path = log_filename.value().c_str();
+  } else {
+    settings.log_file_path = nullptr;
+  }
+
+  if (dest == LoggingDest::kStderr) {
+    settings.logging_dest =
+        logging::LOG_TO_STDERR | logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  } else {
+    // Includes both handle or provided filename on Windows.
+    settings.logging_dest = logging::LOG_TO_ALL;
+  }
+
   settings.delete_old = logging::DELETE_OLD_LOG_FILE;
   logging::InitLogging(settings);
   logging::SetLogItems(true /* Process ID */, true /* Thread ID */,
diff --git a/content/shell/browser/shell_browser_main_parts_ios.mm b/content/shell/browser/shell_browser_main_parts_ios.mm
index 162dd70..99aa8fd 100644
--- a/content/shell/browser/shell_browser_main_parts_ios.mm
+++ b/content/shell/browser/shell_browser_main_parts_ios.mm
@@ -4,7 +4,7 @@
 
 #include "content/shell/browser/shell_browser_main_parts.h"
 
-#include "services/device/public/cpp/geolocation/system_geolocation_source_mac.h"
+#include "services/device/public/cpp/geolocation/system_geolocation_source_apple.h"
 
 namespace content {
 
@@ -12,8 +12,8 @@
 ShellBrowserMainParts::GetGeolocationSystemPermissionManager() {
   if (!device::GeolocationSystemPermissionManager::GetInstance()) {
     device::GeolocationSystemPermissionManager::SetInstance(
-        device::SystemGeolocationSourceMac::
-            CreateGeolocationSystemPermissionManagerOnMac());
+        device::SystemGeolocationSourceApple::
+            CreateGeolocationSystemPermissionManager());
   }
   return device::GeolocationSystemPermissionManager::GetInstance();
 }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index f8b1c7ebd..4d74791 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1290,7 +1290,7 @@
       "//content/public/android:content_java",
       "//content/public/test/android:content_java_test_support",
       "//content/shell/android:content_shell_browsertests_java",
-      "//testing/android/native_test:native_test_java",
+      "//testing/android/native_test:native_browser_test_java",
       "//third_party/jni_zero:jni_zero_java",
       "//ui/android:ui_java",
     ]
@@ -1652,7 +1652,6 @@
     "../renderer/accessibility/annotations/ax_image_annotator_browsertest.cc",
     "../renderer/accessibility/render_accessibility_impl_browsertest.cc",
     "../renderer/blink_platform_audio_hardware_browsertest.cc",
-    "../renderer/fluent_scrollbar_browsertest.cc",
     "../renderer/gin_browsertest.cc",
     "../renderer/media/renderer_web_media_player_delegate_browsertest.cc",
     "../renderer/media/too_many_web_media_players_intervention_browsertest.cc",
diff --git a/content/test/content_browser_test_test.cc b/content/test/content_browser_test_test.cc
index a49aa2e..c9c7db6 100644
--- a/content/test/content_browser_test_test.cc
+++ b/content/test/content_browser_test_test.cc
@@ -118,6 +118,8 @@
                              "ContentBrowserTest.MANUAL_RendererCrash");
   new_test.AppendSwitch(switches::kRunManualTestsFlag);
   new_test.AppendSwitch(switches::kSingleProcessTests);
+  // Test needs to capture stderr so force logging to go there.
+  new_test.AppendSwitchASCII(switches::kEnableLogging, "stderr");
 
 #if defined(THREAD_SANITIZER)
   // TSan appears to not be able to report intentional crashes from sandboxed
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-android-assist-data.txt b/content/test/data/accessibility/aria/aria-combobox-expected-android-assist-data.txt
index bd75087..88bb7ddd 100644
--- a/content/test/data/accessibility/aria/aria-combobox-expected-android-assist-data.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-android-assist-data.txt
@@ -1,12 +1,13 @@
 WebView textSize:16.0 style:0 htmlInfo:[{htmlTag="#document"}, {display=""}]
-++View textSize:16.0 style:0 htmlInfo:[{htmlTag="div"}, {display="block"}, {id="state_label"}]
-++++TextView text:"State" textSize:16.0 style:0 htmlInfo:[{htmlTag=""}, {display=""}]
-++EditText text:"State" textSize:13.3 style:0 htmlInfo:[{htmlTag="input"}, {display="inline-block"}, {aria-activedescendant="state2"}, {aria-autocomplete="list"}, {aria-expanded="true"}, {aria-haspopup="listbox"}, {aria-labelledby="state_label"}, {aria-owns="state_list"}, {aria-readonly="true"}, {autofocus=""}, {role="combobox"}, {type="text"}]
-++++View textSize:13.3 style:0 htmlInfo:[{htmlTag="div"}, {display="flow-root"}]
+++TextView text:"State" textSize:16.0 style:0 htmlInfo:[{htmlTag="div"}, {display="block"}, {id="state_label"}]
+++EditText textSize:13.0 style:0 htmlInfo:[{htmlTag="input"}, {display="inline-block"}, {aria-activedescendant="state2"}, {aria-autocomplete="list"}, {aria-expanded="true"}, {aria-haspopup="listbox"}, {aria-labelledby="state_label"}, {aria-owns="state_list"}, {aria-readonly="true"}, {autofocus=""}, {role="combobox"}, {type="text"}]
 ++ListView textSize:16.0 style:0 htmlInfo:[{htmlTag="ul"}, {display="block"}, {id="state_list"}, {onclick="console.log('hi')"}, {role="listbox"}]
-++++View textSize:16.0 style:0 htmlInfo:[{htmlTag="li"}, {display="list-item"}, {id="state1"}, {role="option"}]
-++++++TextView text:"• " textSize:16.0 style:0 htmlInfo:[{htmlTag=""}, {display=""}]
-++++++TextView text:"Alabama" textSize:16.0 style:0 htmlInfo:[{htmlTag=""}, {display=""}]
-++++View textSize:16.0 style:0 htmlInfo:[{htmlTag="li"}, {display="list-item"}, {id="state2"}, {role="option"}]
-++++++TextView text:"• " textSize:16.0 style:0 htmlInfo:[{htmlTag=""}, {display=""}]
-++++++TextView text:"Alaska" textSize:16.0 style:0 htmlInfo:[{htmlTag=""}, {display=""}]
+++++View text:"Alabama" textSize:16.0 style:0 htmlInfo:[{htmlTag="li"}, {display="list-item"}, {id="state1"}, {role="option"}]
+++++View text:"Alaska" textSize:16.0 style:0 htmlInfo:[{htmlTag="li"}, {display="list-item"}, {id="state2"}, {role="option"}]
+++EditText textSize:13.0 style:0 htmlInfo:[{htmlTag="input"}, {display="inline-block"}, {aria-activedescendant="grid-row"}, {role="combobox"}, {type="text"}]
+++GridView textSize:16.0 style:0 htmlInfo:[{htmlTag="div"}, {display="block"}, {role="grid"}]
+++++View text:"Grid row" textSize:16.0 style:0 htmlInfo:[{htmlTag="div"}, {display="block"}, {id="grid-row"}, {role="row"}]
+++EditText textSize:13.0 style:0 htmlInfo:[{htmlTag="input"}, {display="inline-block"}, {aria-activedescendant="treegrid-row"}, {role="combobox"}, {type="text"}]
+++GridView textSize:16.0 style:0 htmlInfo:[{htmlTag="div"}, {display="block"}, {role="treegrid"}]
+++++View textSize:16.0 style:0 htmlInfo:[{htmlTag="div"}, {display="block"}, {role="rowgroup"}]
+++++++View text:"Treegrid row" textSize:16.0 style:0 htmlInfo:[{htmlTag="div"}, {display="block"}, {id="treegrid-row"}, {role="row"}]
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt
index 3c248e3..f7d1e70d 100644
--- a/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt
@@ -4,3 +4,10 @@
 ++ListView viewIdResName:"state_list" clickable CollectionInfo:[rows=2, cols=1] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
 ++++View text:"Alabama" viewIdResName:"state1" clickable focusable CollectionItemInfo:[rowIndex=0, colIndex=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
 ++++View text:"Alaska" viewIdResName:"state2" clickable focusable selected CollectionItemInfo:[rowIndex=1, colIndex=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++EditText canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300"]
+++GridView CollectionInfo:[rows=0, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View text:"Grid row" viewIdResName:"grid-row" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="row"]
+++EditText canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300"]
+++GridView clickable CollectionInfo:[rows=0, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="treeGrid", roleDescription="tree grid"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="rowGroup"]
+++++++View text:"Treegrid row" viewIdResName:"treegrid-row" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="row"]
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-android.txt b/content/test/data/accessibility/aria/aria-combobox-expected-android.txt
index 89d58d9..194d884 100644
--- a/content/test/data/accessibility/aria/aria-combobox-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-android.txt
@@ -4,3 +4,10 @@
 ++android.widget.ListView role_description='list box' clickable collection item_count=2 row_count=2 column_count=1
 ++++android.view.View clickable collection_item focusable name='Alabama' row_span=1 column_span=1
 ++++android.view.View clickable collection_item focusable selected name='Alaska' item_index=1 row_index=1 row_span=1 column_span=1
+++android.widget.EditText clickable editable_text focusable input_type=1
+++android.widget.GridView role_description='table' collection
+++++android.view.View focusable name='Grid row'
+++android.widget.EditText clickable editable_text focusable input_type=1
+++android.widget.GridView role_description='tree grid' clickable collection
+++++android.view.View
+++++++android.view.View focusable name='Treegrid row'
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-combobox-expected-auralinux.txt
index 3252efc..d7ea8f4 100644
--- a/content/test/data/accessibility/aria/aria-combobox-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-auralinux.txt
@@ -5,3 +5,12 @@
 ++[list box] controlled-by=[combo box]
 ++++[list item] name='Alabama' selectable
 ++++[list item] name='Alaska' selectable selected
+++[combo box] selectable-text controller-for=[table] haspopup:listbox
+++[table] controlled-by=[combo box] cols=0 headers=(NONE); rows=0 headers=(NONE); caption=false; spans=(all: 1x1)
+++++[table row] name='Grid row'
+++++++[static] name='Grid row'
+++[combo box] selectable-text controller-for=[tree table] haspopup:listbox
+++[tree table] controlled-by=[combo box] cols=0 headers=(NONE); rows=0 headers=(NONE); caption=false; spans=(all: 1x1)
+++++[panel]
+++++++[table row] name='Treegrid row'
+++++++++[static] name='Treegrid row'
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-blink.txt b/content/test/data/accessibility/aria/aria-combobox-expected-blink.txt
index c81e7c7..ee39a0df 100644
--- a/content/test/data/accessibility/aria/aria-combobox-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-blink.txt
@@ -19,3 +19,16 @@
 ++++++++++++++inlineTextBox name='%E2%80%A2 '
 ++++++++++staticText name='Alaska'
 ++++++++++++inlineTextBox name='Alaska'
+++++++textFieldWithComboBox focusable activedescendantId=row haspopup=listbox controlsIds=grid
+++++++++genericContainer
+++++++grid
+++++++++row focusable name='Grid row'
+++++++++++staticText name='Grid row'
+++++++++++++inlineTextBox name='Grid row'
+++++++textFieldWithComboBox focusable activedescendantId=row haspopup=listbox controlsIds=treeGrid
+++++++++genericContainer
+++++++treeGrid
+++++++++rowGroup
+++++++++++row focusable name='Treegrid row'
+++++++++++++staticText name='Treegrid row'
+++++++++++++++inlineTextBox name='Treegrid row'
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt b/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt
index 85ab071e4..c7ab0e8 100644
--- a/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt
@@ -5,3 +5,14 @@
 ++AXList
 ++++AXStaticText AXValue='Alabama'
 ++++AXStaticText AXFocused=1 AXValue='Alaska'
+++AXComboBox AXLinkedUIElements=[:9]
+++AXTable
+++++AXRow AXTitle='Grid row'
+++++++AXStaticText AXValue='Grid row'
+++++AXGroup
+++AXComboBox AXLinkedUIElements=[:14]
+++AXTable
+++++AXGroup
+++++++AXRow AXTitle='Treegrid row'
+++++++++AXStaticText AXValue='Treegrid row'
+++++AXGroup
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-win.txt b/content/test/data/accessibility/aria/aria-combobox-expected-win.txt
index d41d9161..1fc927b 100644
--- a/content/test/data/accessibility/aria/aria-combobox-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-win.txt
@@ -1,7 +1,16 @@
-ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0><obj1><obj2>'
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0><obj1><obj2><obj3><obj4><obj5><obj6>'
 ++IA2_ROLE_SECTION ia2_hypertext='State'
 ++++ROLE_SYSTEM_STATICTEXT name='State' ia2_hypertext='State'
 ++ROLE_SYSTEM_COMBOBOX name='State' READONLY EXPANDED FOCUSABLE HASPOPUP haspopup:listbox
 ++ROLE_SYSTEM_LIST ia2_hypertext='<obj0><obj1>'
 ++++ROLE_SYSTEM_LISTITEM name='Alabama' FOCUSABLE SELECTABLE ia2_hypertext='Alabama'
 ++++ROLE_SYSTEM_LISTITEM name='Alaska' SELECTED FOCUSED FOCUSABLE SELECTABLE ia2_hypertext='Alaska'
+++ROLE_SYSTEM_COMBOBOX FOCUSABLE HASPOPUP haspopup:listbox
+++ROLE_SYSTEM_TABLE ia2_hypertext='<obj0>'
+++++ROLE_SYSTEM_ROW name='Grid row' FOCUSABLE ia2_hypertext='Grid row'
+++++++ROLE_SYSTEM_STATICTEXT name='Grid row' ia2_hypertext='Grid row'
+++ROLE_SYSTEM_COMBOBOX FOCUSABLE HASPOPUP haspopup:listbox
+++ROLE_SYSTEM_OUTLINE ia2_hypertext='<obj0>'
+++++ROLE_SYSTEM_GROUPING ia2_hypertext='<obj0>'
+++++++ROLE_SYSTEM_OUTLINEITEM name='Treegrid row' FOCUSABLE ia2_hypertext='Treegrid row'
+++++++++ROLE_SYSTEM_STATICTEXT name='Treegrid row' ia2_hypertext='Treegrid row'
diff --git a/content/test/data/accessibility/aria/aria-combobox.html b/content/test/data/accessibility/aria/aria-combobox.html
index 06e210f..98fa6305 100644
--- a/content/test/data/accessibility/aria/aria-combobox.html
+++ b/content/test/data/accessibility/aria/aria-combobox.html
@@ -34,5 +34,17 @@
       <li id="state1" role="option">Alabama</li>
       <li id="state2" role="option">Alaska</li>
     </ul>
+
+    <input type="text" role="combobox" aria-activedescendant="grid-row">
+    <div role="grid">
+      <div role="row" id="grid-row">Grid row</div>
+    </div>
+
+    <input type="text" role="combobox" aria-activedescendant="treegrid-row">
+    <div role="treegrid">
+      <div role="rowgroup">
+        <div role="row" id="treegrid-row">Treegrid row</div>
+      </div>
+    </div>
   </body>
 </html>
diff --git a/content/test/data/attribution_reporting/interop/README.md b/content/test/data/attribution_reporting/interop/README.md
index a8c237d6..e8864f66 100644
--- a/content/test/data/attribution_reporting/interop/README.md
+++ b/content/test/data/attribution_reporting/interop/README.md
@@ -99,15 +99,6 @@
 
         "payload": ...
       }
-    ],
-
-    "unparsable_registrations": [
-      {
-        // Time of the input that failed to register.
-        "time": "123",
-
-        "type": "source" // or "trigger"
-      }
     ]
   }
 }
diff --git a/content/test/data/attribution_reporting/interop/aggregatable_budget.json b/content/test/data/attribution_reporting/interop/aggregatable_budget.json
index f65cd97..60511bae 100644
--- a/content/test/data/attribution_reporting/interop/aggregatable_budget.json
+++ b/content/test/data/attribution_reporting/interop/aggregatable_budget.json
@@ -228,7 +228,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/aggregatable_contributions_creation.json b/content/test/data/attribution_reporting/interop/aggregatable_contributions_creation.json
index 2e7f393b..8817eb2 100644
--- a/content/test/data/attribution_reporting/interop/aggregatable_contributions_creation.json
+++ b/content/test/data/attribution_reporting/interop/aggregatable_contributions_creation.json
@@ -258,7 +258,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/aggregatable_dedup_key.json b/content/test/data/attribution_reporting/interop/aggregatable_dedup_key.json
index 318ff14..2539c25 100644
--- a/content/test/data/attribution_reporting/interop/aggregatable_dedup_key.json
+++ b/content/test/data/attribution_reporting/interop/aggregatable_dedup_key.json
@@ -226,7 +226,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/aggregatable_large_key.json b/content/test/data/attribution_reporting/interop/aggregatable_large_key.json
index 94d276fc..fdad591 100644
--- a/content/test/data/attribution_reporting/interop/aggregatable_large_key.json
+++ b/content/test/data/attribution_reporting/interop/aggregatable_large_key.json
@@ -51,7 +51,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/aggregatable_report_trigger_context_id.json b/content/test/data/attribution_reporting/interop/aggregatable_report_trigger_context_id.json
index 523256e..44bc051 100644
--- a/content/test/data/attribution_reporting/interop/aggregatable_report_trigger_context_id.json
+++ b/content/test/data/attribution_reporting/interop/aggregatable_report_trigger_context_id.json
@@ -35,6 +35,7 @@
           {
             "url": "https://reporter.test/register-trigger",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Trigger": {
                 "trigger_context_id": 456 // wrong type
               }
@@ -52,6 +53,7 @@
           {
             "url": "https://reporter.test/register-trigger",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Trigger": {
                 "trigger_context_id": "" // empty
               }
@@ -69,6 +71,7 @@
           {
             "url": "https://reporter.test/register-trigger",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Trigger": {
                 "trigger_context_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // too long
               }
@@ -86,6 +89,7 @@
           {
             "url": "https://reporter.test/register-trigger",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Trigger": {
                 "aggregatable_source_registration_time": "include",
                 "trigger_context_id": "123" // not allowed
@@ -123,14 +127,56 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [
-      {"time": "1", "type": "trigger"},
-      {"time": "2", "type": "trigger"},
-      {"time": "3", "type": "trigger"},
-      {"time": "4", "type": "trigger"}
-    ],
     "reports": [
       {
+        "payload": [{
+           "body": {
+              "context_site": "https://destination.test",
+              "header": "Attribution-Reporting-Register-Trigger",
+              "value": "{\"trigger_context_id\":456}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "1",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://destination.test",
+              "header": "Attribution-Reporting-Register-Trigger",
+              "value": "{\"trigger_context_id\":\"\"}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "2",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://destination.test",
+              "header": "Attribution-Reporting-Register-Trigger",
+              "value": "{\"trigger_context_id\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "3",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://destination.test",
+              "header": "Attribution-Reporting-Register-Trigger",
+              "value": "{\"aggregatable_source_registration_time\":\"include\",\"trigger_context_id\":\"123\"}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "4",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
         "payload": {
           "attribution_destination": "https://destination.test",
           "histograms": [
diff --git a/content/test/data/attribution_reporting/interop/aggregatable_report_window.json b/content/test/data/attribution_reporting/interop/aggregatable_report_window.json
index 95d4295..c0dd5be 100644
--- a/content/test/data/attribution_reporting/interop/aggregatable_report_window.json
+++ b/content/test/data/attribution_reporting/interop/aggregatable_report_window.json
@@ -207,7 +207,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/aggregatable_storage_limit.json b/content/test/data/attribution_reporting/interop/aggregatable_storage_limit.json
index 5441d6eb..2c8a8ea 100644
--- a/content/test/data/attribution_reporting/interop/aggregatable_storage_limit.json
+++ b/content/test/data/attribution_reporting/interop/aggregatable_storage_limit.json
@@ -171,7 +171,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/aggregatable_values_filtering.json b/content/test/data/attribution_reporting/interop/aggregatable_values_filtering.json
index 9f56722..1d998b54ff 100644
--- a/content/test/data/attribution_reporting/interop/aggregatable_values_filtering.json
+++ b/content/test/data/attribution_reporting/interop/aggregatable_values_filtering.json
@@ -154,7 +154,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/aggregatable_with_event_disabled.json b/content/test/data/attribution_reporting/interop/aggregatable_with_event_disabled.json
index 86f9001..eb3be5d1 100644
--- a/content/test/data/attribution_reporting/interop/aggregatable_with_event_disabled.json
+++ b/content/test/data/attribution_reporting/interop/aggregatable_with_event_disabled.json
@@ -59,7 +59,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/basic.json b/content/test/data/attribution_reporting/interop/basic.json
index 7b2e558..a541d47 100644
--- a/content/test/data/attribution_reporting/interop/basic.json
+++ b/content/test/data/attribution_reporting/interop/basic.json
@@ -47,7 +47,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/basic_aggregatable.json b/content/test/data/attribution_reporting/interop/basic_aggregatable.json
index 8f3e3552..225bb67 100644
--- a/content/test/data/attribution_reporting/interop/basic_aggregatable.json
+++ b/content/test/data/attribution_reporting/interop/basic_aggregatable.json
@@ -57,7 +57,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/channel_capacity.json b/content/test/data/attribution_reporting/interop/channel_capacity.json
index 0cc82ca..301f8e2e 100644
--- a/content/test/data/attribution_reporting/interop/channel_capacity.json
+++ b/content/test/data/attribution_reporting/interop/channel_capacity.json
@@ -291,7 +291,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       // No debug reports are generated for exceeding channel capacity.
       {
diff --git a/content/test/data/attribution_reporting/interop/custom_trigger_data.json b/content/test/data/attribution_reporting/interop/custom_trigger_data.json
index 08ddef2..c1854c4 100644
--- a/content/test/data/attribution_reporting/interop/custom_trigger_data.json
+++ b/content/test/data/attribution_reporting/interop/custom_trigger_data.json
@@ -80,6 +80,7 @@
           {
             "url": "https://reporter1.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 // wrong type
@@ -100,6 +101,7 @@
           {
             "url": "https://reporter1.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 "trigger_data": [
@@ -122,6 +124,7 @@
           {
             "url": "https://reporter1.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 "trigger_data": [
@@ -144,6 +147,7 @@
           {
             "url": "https://reporter1.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 "trigger_data": [
@@ -166,6 +170,7 @@
           {
             "url": "https://reporter1.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 // too long
@@ -191,6 +196,7 @@
           {
             "url": "https://reporter1.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 // duplicates
@@ -211,6 +217,7 @@
           {
             "url": "https://reporter1.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 // invalid for modulus (start not 0)
@@ -231,6 +238,7 @@
           {
             "url": "https://reporter1.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 // invalid for modulus (non-contiguous)
@@ -251,6 +259,7 @@
           {
             "url": "https://reporter1.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 "trigger_data": [
@@ -310,17 +319,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [
-      {"time": "3", "type": "source"},
-      {"time": "4", "type": "source"},
-      {"time": "5", "type": "source"},
-      {"time": "6", "type": "source"},
-      {"time": "7", "type": "source"},
-      {"time": "8", "type": "source"},
-      {"time": "9", "type": "source"},
-      {"time": "10", "type": "source"},
-      {"time": "11", "type": "source"}
-    ],
     "reports": [
       {
         "payload": [
@@ -337,6 +335,114 @@
         "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
       },
       {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"trigger_data\":0}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "3",
+        "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"trigger_data\":[\"1\"]}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "4",
+        "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"trigger_data\":[-1]}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "5",
+        "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"trigger_data\":[1.5]}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "6",
+        "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"trigger_data\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "7",
+        "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"trigger_data\":[1,1]}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "8",
+        "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"trigger_data\":[1]}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "9",
+        "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"trigger_data\":[0,2]}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "10",
+        "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"trigger_data\":[4294967296.0]}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "11",
+        "report_url": "https://reporter1.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
         "payload": [
           {
             "body": {
diff --git a/content/test/data/attribution_reporting/interop/dedup_key.json b/content/test/data/attribution_reporting/interop/dedup_key.json
index f02aa9f..9b5f9aa5 100644
--- a/content/test/data/attribution_reporting/interop/dedup_key.json
+++ b/content/test/data/attribution_reporting/interop/dedup_key.json
@@ -120,7 +120,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/dedup_key_report_window_ordering.json b/content/test/data/attribution_reporting/interop/dedup_key_report_window_ordering.json
index 809261d2..7263c9a 100644
--- a/content/test/data/attribution_reporting/interop/dedup_key_report_window_ordering.json
+++ b/content/test/data/attribution_reporting/interop/dedup_key_report_window_ordering.json
@@ -72,7 +72,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/deduplication_sequence.json b/content/test/data/attribution_reporting/interop/deduplication_sequence.json
index a5bdde0..d4c9810 100644
--- a/content/test/data/attribution_reporting/interop/deduplication_sequence.json
+++ b/content/test/data/attribution_reporting/interop/deduplication_sequence.json
@@ -99,7 +99,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "report_time": "86400000",
diff --git a/content/test/data/attribution_reporting/interop/destination_limit.json b/content/test/data/attribution_reporting/interop/destination_limit.json
index b08827c..b8ece47d 100644
--- a/content/test/data/attribution_reporting/interop/destination_limit.json
+++ b/content/test/data/attribution_reporting/interop/destination_limit.json
@@ -367,7 +367,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/destination_rate_limit.json b/content/test/data/attribution_reporting/interop/destination_rate_limit.json
index 5f9dc93..9c31b66 100644
--- a/content/test/data/attribution_reporting/interop/destination_rate_limit.json
+++ b/content/test/data/attribution_reporting/interop/destination_rate_limit.json
@@ -316,7 +316,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/event_level_epsilon.json b/content/test/data/attribution_reporting/interop/event_level_epsilon.json
index 087641e..8aa0c721 100644
--- a/content/test/data/attribution_reporting/interop/event_level_epsilon.json
+++ b/content/test/data/attribution_reporting/interop/event_level_epsilon.json
@@ -13,6 +13,7 @@
           {
             "url": "https://reporter.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 "event_level_epsilon": -1 // invalid
@@ -32,6 +33,7 @@
           {
             "url": "https://reporter.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 "event_level_epsilon": 14.1 // exceeds max
@@ -115,12 +117,32 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [
-      {"time": "0", "type": "source"},
-      {"time": "1", "type": "source"}
-    ],
     "reports": [
       {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"event_level_epsilon\":-1}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "0",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"event_level_epsilon\":14.1}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "1",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
         "payload": {
            "attribution_destination": "https://destination1.test",
            "randomized_trigger_rate": 0.0024263,
diff --git a/content/test/data/attribution_reporting/interop/event_level_report_time.json b/content/test/data/attribution_reporting/interop/event_level_report_time.json
index a534192..5ab4820 100644
--- a/content/test/data/attribution_reporting/interop/event_level_report_time.json
+++ b/content/test/data/attribution_reporting/interop/event_level_report_time.json
@@ -219,7 +219,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/event_level_storage_limit.json b/content/test/data/attribution_reporting/interop/event_level_storage_limit.json
index 90a40079..2a05981 100644
--- a/content/test/data/attribution_reporting/interop/event_level_storage_limit.json
+++ b/content/test/data/attribution_reporting/interop/event_level_storage_limit.json
@@ -171,7 +171,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/event_level_trigger_filter_data.json b/content/test/data/attribution_reporting/interop/event_level_trigger_filter_data.json
index 708a4f2..3f34892 100644
--- a/content/test/data/attribution_reporting/interop/event_level_trigger_filter_data.json
+++ b/content/test/data/attribution_reporting/interop/event_level_trigger_filter_data.json
@@ -280,7 +280,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/event_level_trigger_priority.json b/content/test/data/attribution_reporting/interop/event_level_trigger_priority.json
index 4fb448d..e03e93b 100644
--- a/content/test/data/attribution_reporting/interop/event_level_trigger_priority.json
+++ b/content/test/data/attribution_reporting/interop/event_level_trigger_priority.json
@@ -192,7 +192,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/event_report_window.json b/content/test/data/attribution_reporting/interop/event_report_window.json
index e9d1b707..1a441f8 100644
--- a/content/test/data/attribution_reporting/interop/event_report_window.json
+++ b/content/test/data/attribution_reporting/interop/event_report_window.json
@@ -198,7 +198,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/event_report_windows.json b/content/test/data/attribution_reporting/interop/event_report_windows.json
index 71d1028..a31f87dc 100644
--- a/content/test/data/attribution_reporting/interop/event_report_windows.json
+++ b/content/test/data/attribution_reporting/interop/event_report_windows.json
@@ -204,7 +204,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/event_source.json b/content/test/data/attribution_reporting/interop/event_source.json
index c8c6e29..4642782 100644
--- a/content/test/data/attribution_reporting/interop/event_source.json
+++ b/content/test/data/attribution_reporting/interop/event_source.json
@@ -45,7 +45,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/event_source_event_report_windows.json b/content/test/data/attribution_reporting/interop/event_source_event_report_windows.json
index 4cb6135..082fac9 100644
--- a/content/test/data/attribution_reporting/interop/event_source_event_report_windows.json
+++ b/content/test/data/attribution_reporting/interop/event_source_event_report_windows.json
@@ -105,7 +105,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/event_source_rounds_expiry.json b/content/test/data/attribution_reporting/interop/event_source_rounds_expiry.json
index a4762a1..466cbb1 100644
--- a/content/test/data/attribution_reporting/interop/event_source_rounds_expiry.json
+++ b/content/test/data/attribution_reporting/interop/event_source_rounds_expiry.json
@@ -39,6 +39,7 @@
           {
             "url": "https://reporter.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Source": {
                 "destination": "https://destination.test",
                 "source_event_id": "456",
@@ -101,11 +102,20 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [
-      { "time": "1000", "type": "source" }
-    ],
     "reports": [
       {
+        "payload": [ {
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"event_report_windows\":{\"end_times\":[43200,86400,108000]},\"expiry\":\"129599\",\"priority\":\"200\",\"source_event_id\":\"456\"}"
+           },
+           "type": "header-parsing-error"
+        } ],
+        "report_time": "1000",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
         "payload": {
           "attribution_destination": "https://destination.test",
           "randomized_trigger_rate": 0.0000025,
diff --git a/content/test/data/attribution_reporting/interop/event_source_trigger_priority.json b/content/test/data/attribution_reporting/interop/event_source_trigger_priority.json
index 3837e0e..91fa4bb 100644
--- a/content/test/data/attribution_reporting/interop/event_source_trigger_priority.json
+++ b/content/test/data/attribution_reporting/interop/event_source_trigger_priority.json
@@ -93,7 +93,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/expired_source.json b/content/test/data/attribution_reporting/interop/expired_source.json
index 84b4aa1..326e1628d 100644
--- a/content/test/data/attribution_reporting/interop/expired_source.json
+++ b/content/test/data/attribution_reporting/interop/expired_source.json
@@ -213,7 +213,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/lookback_window_precision.json b/content/test/data/attribution_reporting/interop/lookback_window_precision.json
index 78254f9..2aa3da16 100644
--- a/content/test/data/attribution_reporting/interop/lookback_window_precision.json
+++ b/content/test/data/attribution_reporting/interop/lookback_window_precision.json
@@ -49,7 +49,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": []
   }
 }
diff --git a/content/test/data/attribution_reporting/interop/max_aggregatable_reports_per_source.json b/content/test/data/attribution_reporting/interop/max_aggregatable_reports_per_source.json
index 98afafa..6d69380 100644
--- a/content/test/data/attribution_reporting/interop/max_aggregatable_reports_per_source.json
+++ b/content/test/data/attribution_reporting/interop/max_aggregatable_reports_per_source.json
@@ -555,7 +555,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/max_event_level_reports_per_source.json b/content/test/data/attribution_reporting/interop/max_event_level_reports_per_source.json
index 77e69e1c..5c10d04 100644
--- a/content/test/data/attribution_reporting/interop/max_event_level_reports_per_source.json
+++ b/content/test/data/attribution_reporting/interop/max_event_level_reports_per_source.json
@@ -121,7 +121,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/most_recent_source.json b/content/test/data/attribution_reporting/interop/most_recent_source.json
index a337d88e..59b7f56 100644
--- a/content/test/data/attribution_reporting/interop/most_recent_source.json
+++ b/content/test/data/attribution_reporting/interop/most_recent_source.json
@@ -64,7 +64,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/multiple_destinations.json b/content/test/data/attribution_reporting/interop/multiple_destinations.json
index d76ad49..04926c3 100644
--- a/content/test/data/attribution_reporting/interop/multiple_destinations.json
+++ b/content/test/data/attribution_reporting/interop/multiple_destinations.json
@@ -90,7 +90,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/parse_failures.json b/content/test/data/attribution_reporting/interop/parse_failures.json
index 2e54693..193b33d 100644
--- a/content/test/data/attribution_reporting/interop/parse_failures.json
+++ b/content/test/data/attribution_reporting/interop/parse_failures.json
@@ -13,6 +13,7 @@
           {
             "url": "https://reporter.test/register-source",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               // missing destination
               "Attribution-Reporting-Register-Source": {}
             }
@@ -29,6 +30,7 @@
           {
             "url": "https://reporter.test/register-trigger",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Trigger": {
                 // dictionary instead of list
                 "event_trigger_data": {}
@@ -48,14 +50,15 @@
           {
             "url": "https://reporter.test/register-source",
             "response": {
-            "Attribution-Reporting-Register-Source": {
-              "destination": "https://destination.test",
-              // filter data using reserved key
-              "filter_data": {
-                "_some_key": ["123", "456"]
+              "Attribution-Reporting-Info": "report-header-errors",
+              "Attribution-Reporting-Register-Source": {
+               "destination": "https://destination.test",
+               // filter data using reserved key
+               "filter_data": {
+                 "_some_key": ["123", "456"]
+               }
               }
             }
-            }
           }
         ]
       },
@@ -69,10 +72,10 @@
           {
             "url": "https://reporter.test/register-trigger",
             "response": {
+              "Attribution-Reporting-Info": "report-header-errors",
               "Attribution-Reporting-Register-Trigger": {
                 "event_trigger_data": [
                   {
-                    "trigger_data": "1",
                     // filters using reserved key
                     "filters": {
                       "_some_key": []
@@ -87,12 +90,55 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [
-      {"time": "0", "type": "source"},
-      {"time": "1", "type": "trigger"},
-      {"time": "2", "type": "source"},
-      {"time": "3", "type": "trigger"}
-    ],
-    "reports": []
+    "reports": [
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "0",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://destination.test",
+              "header": "Attribution-Reporting-Register-Trigger",
+              "value": "{\"event_trigger_data\":{}}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "1",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://source.test",
+              "header": "Attribution-Reporting-Register-Source",
+              "value": "{\"destination\":\"https://destination.test\",\"filter_data\":{\"_some_key\":[\"123\",\"456\"]}}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "2",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      },
+      {
+        "payload": [{
+           "body": {
+              "context_site": "https://destination.test",
+              "header": "Attribution-Reporting-Register-Trigger",
+              "value": "{\"event_trigger_data\":[{\"filters\":{\"_some_key\":[]}}]}"
+           },
+           "type": "header-parsing-error"
+        }],
+        "report_time": "3",
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose"
+      }
+    ]
   }
 }
diff --git a/content/test/data/attribution_reporting/interop/rate_limit_max_attribution_reporting_endpoints.json b/content/test/data/attribution_reporting/interop/rate_limit_max_attribution_reporting_endpoints.json
index 81ff78a02..c6cae3a 100644
--- a/content/test/data/attribution_reporting/interop/rate_limit_max_attribution_reporting_endpoints.json
+++ b/content/test/data/attribution_reporting/interop/rate_limit_max_attribution_reporting_endpoints.json
@@ -299,7 +299,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/rate_limit_max_attributions.json b/content/test/data/attribution_reporting/interop/rate_limit_max_attributions.json
index 980368f..426d8c9e 100644
--- a/content/test/data/attribution_reporting/interop/rate_limit_max_attributions.json
+++ b/content/test/data/attribution_reporting/interop/rate_limit_max_attributions.json
@@ -430,7 +430,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/rate_limit_max_distinct_reporting_origins_per_source_reporting_site.json b/content/test/data/attribution_reporting/interop/rate_limit_max_distinct_reporting_origins_per_source_reporting_site.json
index 634f98f..61dbb63 100644
--- a/content/test/data/attribution_reporting/interop/rate_limit_max_distinct_reporting_origins_per_source_reporting_site.json
+++ b/content/test/data/attribution_reporting/interop/rate_limit_max_distinct_reporting_origins_per_source_reporting_site.json
@@ -111,7 +111,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/rate_limit_max_reporting_origins_per_source_reporting_site.json b/content/test/data/attribution_reporting/interop/rate_limit_max_reporting_origins_per_source_reporting_site.json
index f46580bb..4005cb3 100644
--- a/content/test/data/attribution_reporting/interop/rate_limit_max_reporting_origins_per_source_reporting_site.json
+++ b/content/test/data/attribution_reporting/interop/rate_limit_max_reporting_origins_per_source_reporting_site.json
@@ -181,7 +181,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/rate_limit_max_source_registration_reporting_origin_endpoints.json b/content/test/data/attribution_reporting/interop/rate_limit_max_source_registration_reporting_origin_endpoints.json
index 2edf8f8..42a10942 100644
--- a/content/test/data/attribution_reporting/interop/rate_limit_max_source_registration_reporting_origin_endpoints.json
+++ b/content/test/data/attribution_reporting/interop/rate_limit_max_source_registration_reporting_origin_endpoints.json
@@ -290,7 +290,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/rate_limit_max_source_reporting_origin_ignores_expiry.json b/content/test/data/attribution_reporting/interop/rate_limit_max_source_reporting_origin_ignores_expiry.json
index 5b87414..afee704 100644
--- a/content/test/data/attribution_reporting/interop/rate_limit_max_source_reporting_origin_ignores_expiry.json
+++ b/content/test/data/attribution_reporting/interop/rate_limit_max_source_reporting_origin_ignores_expiry.json
@@ -73,7 +73,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": []
   }
 }
diff --git a/content/test/data/attribution_reporting/interop/same_destination_site.json b/content/test/data/attribution_reporting/interop/same_destination_site.json
index bf78aad..c9802518 100644
--- a/content/test/data/attribution_reporting/interop/same_destination_site.json
+++ b/content/test/data/attribution_reporting/interop/same_destination_site.json
@@ -68,7 +68,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/same_reporting_origin.json b/content/test/data/attribution_reporting/interop/same_reporting_origin.json
index 065e88a..c603c1ed 100644
--- a/content/test/data/attribution_reporting/interop/same_reporting_origin.json
+++ b/content/test/data/attribution_reporting/interop/same_reporting_origin.json
@@ -66,7 +66,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/source_deactivation.json b/content/test/data/attribution_reporting/interop/source_deactivation.json
index 63750cc6..63b8b23 100644
--- a/content/test/data/attribution_reporting/interop/source_deactivation.json
+++ b/content/test/data/attribution_reporting/interop/source_deactivation.json
@@ -260,7 +260,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/source_header_error_debug_report.json b/content/test/data/attribution_reporting/interop/source_header_error_debug_report.json
index c545a22..7b35445 100644
--- a/content/test/data/attribution_reporting/interop/source_header_error_debug_report.json
+++ b/content/test/data/attribution_reporting/interop/source_header_error_debug_report.json
@@ -39,10 +39,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [
-      {"time": "0", "type": "source"},
-      {"time": "1", "type": "source"}
-    ],
     "reports": [ {
       "payload": [ {
          "body": {
diff --git a/content/test/data/attribution_reporting/interop/source_priority.json b/content/test/data/attribution_reporting/interop/source_priority.json
index eb9f23b..63175e04 100644
--- a/content/test/data/attribution_reporting/interop/source_priority.json
+++ b/content/test/data/attribution_reporting/interop/source_priority.json
@@ -86,7 +86,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/source_storage_limit.json b/content/test/data/attribution_reporting/interop/source_storage_limit.json
index 4167da5..05505b29 100644
--- a/content/test/data/attribution_reporting/interop/source_storage_limit.json
+++ b/content/test/data/attribution_reporting/interop/source_storage_limit.json
@@ -204,7 +204,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/source_storage_limit_and_destination_rate_limit.json b/content/test/data/attribution_reporting/interop/source_storage_limit_and_destination_rate_limit.json
index 3d45e3d..1a68160 100644
--- a/content/test/data/attribution_reporting/interop/source_storage_limit_and_destination_rate_limit.json
+++ b/content/test/data/attribution_reporting/interop/source_storage_limit_and_destination_rate_limit.json
@@ -51,7 +51,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/source_storage_limit_expiry.json b/content/test/data/attribution_reporting/interop/source_storage_limit_expiry.json
index 2993c04..dc945a4 100644
--- a/content/test/data/attribution_reporting/interop/source_storage_limit_expiry.json
+++ b/content/test/data/attribution_reporting/interop/source_storage_limit_expiry.json
@@ -88,7 +88,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/success_debug_aggregatable.json b/content/test/data/attribution_reporting/interop/success_debug_aggregatable.json
index 8101c67..730f00eb 100644
--- a/content/test/data/attribution_reporting/interop/success_debug_aggregatable.json
+++ b/content/test/data/attribution_reporting/interop/success_debug_aggregatable.json
@@ -210,7 +210,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/success_debug_event_level.json b/content/test/data/attribution_reporting/interop/success_debug_event_level.json
index 09c445ff..d0f8884 100644
--- a/content/test/data/attribution_reporting/interop/success_debug_event_level.json
+++ b/content/test/data/attribution_reporting/interop/success_debug_event_level.json
@@ -184,7 +184,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/top_level_filter_data.json b/content/test/data/attribution_reporting/interop/top_level_filter_data.json
index b9d4df06..7906b80c 100644
--- a/content/test/data/attribution_reporting/interop/top_level_filter_data.json
+++ b/content/test/data/attribution_reporting/interop/top_level_filter_data.json
@@ -349,7 +349,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/trigger_data_matching.json b/content/test/data/attribution_reporting/interop/trigger_data_matching.json
index a9aa937..49e9df5 100644
--- a/content/test/data/attribution_reporting/interop/trigger_data_matching.json
+++ b/content/test/data/attribution_reporting/interop/trigger_data_matching.json
@@ -115,7 +115,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/attribution_reporting/interop/trigger_data_sanitization.json b/content/test/data/attribution_reporting/interop/trigger_data_sanitization.json
index f850b20..11a28a5 100644
--- a/content/test/data/attribution_reporting/interop/trigger_data_sanitization.json
+++ b/content/test/data/attribution_reporting/interop/trigger_data_sanitization.json
@@ -83,7 +83,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": {
diff --git a/content/test/data/attribution_reporting/interop/trigger_header_error_debug_report.json b/content/test/data/attribution_reporting/interop/trigger_header_error_debug_report.json
index 2f33824..b7b038e 100644
--- a/content/test/data/attribution_reporting/interop/trigger_header_error_debug_report.json
+++ b/content/test/data/attribution_reporting/interop/trigger_header_error_debug_report.json
@@ -37,10 +37,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [
-      {"time": "0", "type": "trigger"},
-      {"time": "1", "type": "trigger"}
-    ],
     "reports": [ {
       "payload": [ {
          "body": {
diff --git a/content/test/data/attribution_reporting/interop/trigger_verbose_debug_report_source_debug_permission.json b/content/test/data/attribution_reporting/interop/trigger_verbose_debug_report_source_debug_permission.json
index a43130f..16920d6 100644
--- a/content/test/data/attribution_reporting/interop/trigger_verbose_debug_report_source_debug_permission.json
+++ b/content/test/data/attribution_reporting/interop/trigger_verbose_debug_report_source_debug_permission.json
@@ -55,7 +55,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": []
   }
 }
diff --git a/content/test/data/attribution_reporting/interop/verbose_debug_report_multiple_data.json b/content/test/data/attribution_reporting/interop/verbose_debug_report_multiple_data.json
index 18236dfd..75972252 100644
--- a/content/test/data/attribution_reporting/interop/verbose_debug_report_multiple_data.json
+++ b/content/test/data/attribution_reporting/interop/verbose_debug_report_multiple_data.json
@@ -80,7 +80,6 @@
     ]
   },
   "output": {
-    "unparsable_registrations": [],
     "reports": [
       {
         "payload": [
diff --git a/content/test/data/sxg/generate-test-sxgs.sh b/content/test/data/sxg/generate-test-sxgs.sh
index 5dbbaa07..d2132c2e 100755
--- a/content/test/data/sxg/generate-test-sxgs.sh
+++ b/content/test/data/sxg/generate-test-sxgs.sh
@@ -12,7 +12,7 @@
 for cmd in gen-signedexchange gen-certurl dump-signedexchange; do
     if ! command -v $cmd > /dev/null 2>&1; then
         echo "$cmd is not installed. Please run:"
-        echo "  go get -u github.com/WICG/webpackage/go/signedexchange/cmd/..."
+        echo "  GO111MODULE=on go install github.com/WICG/webpackage/go/signedexchange/cmd/{gen-signedexchange,gen-certurl,dump-signedexchange}@latest"
         echo '  export PATH=$PATH:$(go env GOPATH)/bin'
         exit 1
     fi
diff --git a/content/test/data/sxg/google-com.example.org.public.pem.cbor b/content/test/data/sxg/google-com.example.org.public.pem.cbor
index a29246a8..578d7bb9 100644
--- a/content/test/data/sxg/google-com.example.org.public.pem.cbor
+++ b/content/test/data/sxg/google-com.example.org.public.pem.cbor
Binary files differ
diff --git a/content/test/data/sxg/google-com.example.org_test.sxg b/content/test/data/sxg/google-com.example.org_test.sxg
index 1a53ee27f..b136413 100644
--- a/content/test/data/sxg/google-com.example.org_test.sxg
+++ b/content/test/data/sxg/google-com.example.org_test.sxg
Binary files differ
diff --git a/content/test/data/sxg/prime256v1-sha256-google-com.csr b/content/test/data/sxg/prime256v1-sha256-google-com.csr
index dbb47f6..8b772f9 100644
--- a/content/test/data/sxg/prime256v1-sha256-google-com.csr
+++ b/content/test/data/sxg/prime256v1-sha256-google-com.csr
@@ -1,8 +1,8 @@
 -----BEGIN CERTIFICATE REQUEST-----
-MIH4MIGfAgEAMD0xHzAdBgNVBAMMFmdvb2dsZS1jb20uZXhhbXBsZS5vcmcxDTAL
+MIH5MIGfAgEAMD0xHzAdBgNVBAMMFmdvb2dsZS1jb20uZXhhbXBsZS5vcmcxDTAL
 BgNVBAoMBFRlc3QxCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
-QgAElFfdVfWVnYyDSfXmIkjMokHtKd1SLpMcJiV1gzYsTABuyq5PUhaghqpelLbG
-vTBUEYH8BupWh2DdIF01fR3Kw6AAMAoGCCqGSM49BAMCA0gAMEUCIQC4wI2Lo4CN
-/8MDiD14faGKqizZZURk1wNln5qQ7ZhLngIgZ3XkkcNcemHTc5srzamFFNKJU2L3
-zpJjorgaC+H3rn8=
+QgAE1Dt0qoFgKULKNerI6T5IK1uFjD6qGGV7b3KISC1k996X95wCSeQBpDwzeAkM
+prhlF217u68wgGlkt4z+tkL8naAAMAoGCCqGSM49BAMCA0kAMEYCIQDOVZjbdx67
+R1dIbHHbiZxKmwq3K+xUdMVHx1lInJBaXQIhANBQroPd67ZCg4co8YfQt4nE4p6t
+qCK8pc4TJvZruuxR
 -----END CERTIFICATE REQUEST-----
diff --git a/content/test/data/sxg/prime256v1-sha256-google-com.public.pem b/content/test/data/sxg/prime256v1-sha256-google-com.public.pem
index 1ba6d8ad..51bf5017 100644
--- a/content/test/data/sxg/prime256v1-sha256-google-com.public.pem
+++ b/content/test/data/sxg/prime256v1-sha256-google-com.public.pem
@@ -12,11 +12,11 @@
             Public Key Algorithm: id-ecPublicKey
                 Public-Key: (256 bit)
                 pub:
-                    04:94:57:dd:55:f5:95:9d:8c:83:49:f5:e6:22:48:
-                    cc:a2:41:ed:29:dd:52:2e:93:1c:26:25:75:83:36:
-                    2c:4c:00:6e:ca:ae:4f:52:16:a0:86:aa:5e:94:b6:
-                    c6:bd:30:54:11:81:fc:06:ea:56:87:60:dd:20:5d:
-                    35:7d:1d:ca:c3
+                    04:d4:3b:74:aa:81:60:29:42:ca:35:ea:c8:e9:3e:
+                    48:2b:5b:85:8c:3e:aa:18:65:7b:6f:72:88:48:2d:
+                    64:f7:de:97:f7:9c:02:49:e4:01:a4:3c:33:78:09:
+                    0c:a6:b8:65:17:6d:7b:bb:af:30:80:69:64:b7:8c:
+                    fe:b6:42:fc:9d
                 ASN1 OID: prime256v1
                 NIST CURVE: P-256
         X509v3 extensions:
@@ -29,43 +29,43 @@
             X509v3 Extended Key Usage: 
                 TLS Web Server Authentication
             X509v3 Subject Key Identifier: 
-                CF:65:B9:9B:EE:0E:EC:8E:A6:B9:69:96:82:20:F4:F7:8B:8A:B2:FA
+                C9:50:C2:2B:03:C3:11:69:26:AD:DF:E7:82:A0:43:65:91:01:D8:F7
             X509v3 Authority Key Identifier: 
-                keyid:9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
-
+                9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
             X509v3 Subject Alternative Name: 
                 DNS:google-com.example.org
     Signature Algorithm: sha256WithRSAEncryption
-         ae:16:c3:1d:a9:b1:53:86:22:ee:90:e6:07:e6:03:c1:a1:10:
-         79:98:32:d9:61:31:f2:27:5c:6a:a2:80:c6:0b:d1:40:16:06:
-         65:5e:b9:72:e1:77:e1:a9:02:bf:b9:18:56:8a:24:0f:b7:84:
-         94:d7:53:51:e6:9f:51:fd:7e:fe:d2:64:9e:73:d0:97:2d:2f:
-         ad:64:41:28:ef:9e:e7:86:ca:18:04:39:7d:7d:b1:9f:3c:20:
-         f4:44:5b:a0:d4:00:28:bf:8c:c2:50:c9:c8:5e:cf:d5:1c:37:
-         98:8e:2e:d8:81:71:43:79:77:60:6b:85:01:34:14:70:73:33:
-         1d:df:6e:2e:30:b5:99:ff:0c:ac:82:b5:23:c2:f4:8c:8a:e0:
-         53:f2:f4:3f:cb:18:78:3c:b3:f4:f9:41:e3:d4:83:75:24:c8:
-         b6:16:15:d7:36:d1:06:a3:9a:0b:59:6b:cd:e4:05:8e:25:d8:
-         1f:44:bf:30:20:3b:92:dd:66:74:3b:e6:d2:91:0c:5a:81:ac:
-         d1:9d:3f:9e:fe:cd:31:a0:36:40:58:6f:01:01:f5:9c:f7:ab:
-         81:91:3f:d3:f1:3c:29:a3:47:a0:60:71:55:86:8c:15:3e:9e:
-         0c:54:45:04:4c:10:33:36:09:c5:88:56:3a:8e:78:3b:dc:5d:
-         43:5a:cd:84
+    Signature Value:
+        71:10:dc:b8:b6:b8:b4:76:78:c1:b0:fb:73:05:69:b6:d6:14:
+        42:0f:12:73:52:d2:5b:16:c5:3a:55:78:97:65:40:9d:7d:3c:
+        ae:76:91:88:64:33:93:0e:dd:fe:91:fd:9b:32:de:0a:6a:26:
+        58:6f:23:7a:2a:c9:59:58:7c:8b:a6:f0:7f:09:1a:d7:c0:ce:
+        6a:2e:e6:ed:d3:26:92:fd:d8:ef:c2:16:2d:77:81:8d:35:be:
+        f7:6e:d3:c6:24:7b:bb:54:4d:cd:41:c2:3f:0c:4c:fe:ef:01:
+        57:61:b9:05:00:05:18:27:8c:71:bd:a4:c0:8a:c5:0d:c8:45:
+        51:b1:e3:e2:34:b2:83:61:36:16:bf:8c:02:e0:34:94:bc:a1:
+        f7:88:27:a7:ed:35:12:0e:c3:3f:39:1c:2f:dd:fd:65:88:58:
+        b0:b0:d5:45:20:f1:88:f8:45:ca:e7:bd:9e:6f:96:2d:be:1e:
+        09:0e:16:55:3d:4d:0e:61:d9:67:ee:7b:f7:24:d9:cf:68:c0:
+        b0:c0:9f:ec:02:74:11:16:48:06:12:40:fc:92:be:8f:fc:91:
+        da:a8:c4:c4:b2:1e:f4:18:5f:76:fd:a3:14:ea:1c:eb:27:17:
+        4b:c8:1b:9c:c5:20:45:b1:02:c6:c4:1e:76:51:b0:96:e2:6c:
+        17:a4:c5:b2
 -----BEGIN CERTIFICATE-----
 MIIC9jCCAd6gAwIBAgIBAjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
 MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
 A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE5MDYwMTAw
 MDAwMFoXDTE5MDgzMDAwMDAwMFowPTEfMB0GA1UEAwwWZ29vZ2xlLWNvbS5leGFt
 cGxlLm9yZzENMAsGA1UECgwEVGVzdDELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIB
-BggqhkjOPQMBBwNCAASUV91V9ZWdjINJ9eYiSMyiQe0p3VIukxwmJXWDNixMAG7K
-rk9SFqCGql6Utsa9MFQRgfwG6laHYN0gXTV9HcrDo4GlMIGiMAkGA1UdEwQCMAAw
+BggqhkjOPQMBBwNCAATUO3SqgWApQso16sjpPkgrW4WMPqoYZXtvcohILWT33pf3
+nAJJ5AGkPDN4CQymuGUXbXu7rzCAaWS3jP62Qvydo4GlMIGiMAkGA1UdEwQCMAAw
 EAYKKwYBBAHWeQIBFgQCBQAwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUF
-BwMBMB0GA1UdDgQWBBTPZbmb7g7sjqa5aZaCIPT3i4qy+jAfBgNVHSMEGDAWgBSb
+BwMBMB0GA1UdDgQWBBTJUMIrA8MRaSat3+eCoENlkQHY9zAfBgNVHSMEGDAWgBSb
 JguKmKm7HbkfHOMaQDPtjheIqzAhBgNVHREEGjAYghZnb29nbGUtY29tLmV4YW1w
-bGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQCuFsMdqbFThiLukOYH5gPBoRB5mDLZ
-YTHyJ1xqooDGC9FAFgZlXrly4XfhqQK/uRhWiiQPt4SU11NR5p9R/X7+0mSec9CX
-LS+tZEEo757nhsoYBDl9fbGfPCD0RFug1AAov4zCUMnIXs/VHDeYji7YgXFDeXdg
-a4UBNBRwczMd324uMLWZ/wysgrUjwvSMiuBT8vQ/yxh4PLP0+UHj1IN1JMi2FhXX
-NtEGo5oLWWvN5AWOJdgfRL8wIDuS3WZ0O+bSkQxagazRnT+e/s0xoDZAWG8BAfWc
-96uBkT/T8Twpo0egYHFVhowVPp4MVEUETBAzNgnFiFY6jng73F1DWs2E
+bGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQBxENy4tri0dnjBsPtzBWm21hRCDxJz
+UtJbFsU6VXiXZUCdfTyudpGIZDOTDt3+kf2bMt4KaiZYbyN6KslZWHyLpvB/CRrX
+wM5qLubt0yaS/djvwhYtd4GNNb73btPGJHu7VE3NQcI/DEz+7wFXYbkFAAUYJ4xx
+vaTAisUNyEVRsePiNLKDYTYWv4wC4DSUvKH3iCen7TUSDsM/ORwv3f1liFiwsNVF
+IPGI+EXK572eb5Ytvh4JDhZVPU0OYdln7nv3JNnPaMCwwJ/sAnQRFkgGEkD8kr6P
+/JHaqMTEsh70GF92/aMU6hzrJxdLyBucxSBFsQLGxB52UbCW4mwXpMWy
 -----END CERTIFICATE-----
diff --git a/content/test/data/sxg/prime256v1-sha256-long-validity.public.pem b/content/test/data/sxg/prime256v1-sha256-long-validity.public.pem
index c0aea11e..c68eaab 100644
--- a/content/test/data/sxg/prime256v1-sha256-long-validity.public.pem
+++ b/content/test/data/sxg/prime256v1-sha256-long-validity.public.pem
@@ -2,21 +2,21 @@
     Data:
         Version: 3 (0x2)
         Serial Number: 5 (0x5)
-    Signature Algorithm: sha256WithRSAEncryption
+        Signature Algorithm: sha256WithRSAEncryption
         Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
         Validity
-            Not Before: Dec 14 01:43:48 2021 GMT
-            Not After : Mar 18 01:43:48 2024 GMT
+            Not Before: Mar 18 06:03:47 2024 GMT
+            Not After : Jun 21 06:03:47 2026 GMT
         Subject: CN=test.example.org, O=Test, C=US
         Subject Public Key Info:
             Public Key Algorithm: id-ecPublicKey
                 Public-Key: (256 bit)
-                pub: 
-                    04:56:24:49:7a:0a:a9:df:1a:0a:07:e3:ad:2a:23:
-                    6d:e9:33:25:68:5a:96:33:03:95:13:26:d9:44:ff:
-                    a8:59:16:6d:b6:7d:50:74:6e:d9:f7:9d:48:b2:61:
-                    ba:f8:6a:1e:fe:c6:1b:83:09:7f:84:9a:56:4f:68:
-                    a8:3e:28:d1:10
+                pub:
+                    04:d4:3b:74:aa:81:60:29:42:ca:35:ea:c8:e9:3e:
+                    48:2b:5b:85:8c:3e:aa:18:65:7b:6f:72:88:48:2d:
+                    64:f7:de:97:f7:9c:02:49:e4:01:a4:3c:33:78:09:
+                    0c:a6:b8:65:17:6d:7b:bb:af:30:80:69:64:b7:8c:
+                    fe:b6:42:fc:9d
                 ASN1 OID: prime256v1
                 NIST CURVE: P-256
         X509v3 extensions:
@@ -29,43 +29,43 @@
             X509v3 Extended Key Usage: 
                 TLS Web Server Authentication
             X509v3 Subject Key Identifier: 
-                81:00:BA:FD:50:89:3A:F4:C7:7E:72:35:0A:53:4B:64:5F:02:02:7D
+                C9:50:C2:2B:03:C3:11:69:26:AD:DF:E7:82:A0:43:65:91:01:D8:F7
             X509v3 Authority Key Identifier: 
-                keyid:9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
-
+                9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
             X509v3 Subject Alternative Name: 
                 DNS:test.example.org
     Signature Algorithm: sha256WithRSAEncryption
-         b8:3e:97:bc:c4:0a:2c:78:29:9e:96:47:2f:18:04:25:00:61:
-         4d:b5:28:ea:19:0b:a8:c6:17:ef:fb:81:7d:8e:08:c5:74:16:
-         f0:53:5b:30:ad:29:78:ea:5a:c4:5b:34:c5:f9:3b:92:83:18:
-         84:7f:62:4c:88:ff:46:23:39:82:ec:a2:ad:c8:b8:8b:a1:9a:
-         b5:0d:23:82:72:78:26:25:59:20:a8:9a:9d:8d:2d:5f:c8:38:
-         46:87:eb:a2:1c:a9:07:d1:8a:72:73:5d:34:87:31:07:21:20:
-         a1:80:27:ba:f3:29:97:35:89:ca:65:fb:d3:ce:9a:6c:42:30:
-         9f:3d:37:72:f7:d5:f8:18:1f:5a:69:eb:fa:14:75:75:1a:45:
-         b8:d6:94:be:5b:b1:4a:8e:a8:b4:e3:00:fb:ab:94:70:e5:7b:
-         ec:ed:74:50:98:3d:be:6f:32:f1:c0:f0:ef:c8:e2:61:c7:c6:
-         7e:85:93:fd:f7:b4:e0:69:de:4c:43:7c:31:5a:6d:06:e1:d4:
-         70:fc:03:62:73:5d:db:f1:d3:b2:20:d7:13:20:de:93:0c:21:
-         d8:0b:00:eb:ae:48:45:a6:77:c3:74:9b:96:55:df:18:1a:40:
-         0c:26:85:26:58:a6:44:75:58:ed:4b:5d:98:5b:ec:21:98:92:
-         99:2f:d2:16
+    Signature Value:
+        81:ef:14:01:42:43:7c:f4:ca:32:91:eb:15:85:18:3e:9d:13:
+        2f:9d:23:e3:2b:26:92:fc:0c:f8:d4:9b:77:e1:cf:b0:7a:95:
+        cc:2e:17:ef:ff:02:ce:fa:8c:59:08:79:e0:9c:ed:58:bf:e6:
+        58:08:a3:a3:f2:51:f5:7a:36:e5:b6:f5:2c:29:e7:c3:68:8f:
+        47:a8:bc:08:1d:01:55:e3:cb:12:dd:f9:de:81:9c:90:3b:81:
+        1a:00:7b:52:76:80:c7:d3:ad:56:87:d1:01:21:dd:1e:a5:63:
+        6b:ff:d2:0c:ac:4b:86:07:38:29:9e:21:2a:43:d3:a2:35:19:
+        7e:e5:6c:9e:d6:e3:1c:a1:35:1e:cf:54:b1:7d:a0:31:54:6f:
+        be:ef:91:c5:33:2a:04:73:33:5f:a9:2d:18:63:22:b2:3d:3a:
+        42:fd:c6:1e:03:ec:f7:e5:d6:8c:7d:4d:f9:ce:c9:3b:f3:3f:
+        a8:16:6b:56:c2:9c:36:67:71:9c:36:8e:c8:97:d7:77:83:b6:
+        de:0e:d8:18:35:c9:c5:7a:fa:d4:0d:6f:bc:e3:68:b1:3d:14:
+        04:30:95:db:31:2e:d1:1e:e2:a8:c9:16:1d:85:24:7b:fa:aa:
+        32:87:79:cc:44:28:ef:9f:bd:f3:1d:2a:ac:dd:5c:ee:32:cf:
+        87:98:54:33
 -----BEGIN CERTIFICATE-----
 MIIC6jCCAdKgAwIBAgIBBTANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
 MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
-A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTIxMTIxNDAx
-NDM0OFoXDTI0MDMxODAxNDM0OFowNzEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLm9y
+A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI0MDMxODA2
+MDM0N1oXDTI2MDYyMTA2MDM0N1owNzEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLm9y
 ZzENMAsGA1UECgwEVGVzdDELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjO
-PQMBBwNCAARWJEl6CqnfGgoH460qI23pMyVoWpYzA5UTJtlE/6hZFm22fVB0btn3
-nUiyYbr4ah7+xhuDCX+EmlZPaKg+KNEQo4GfMIGcMAkGA1UdEwQCMAAwEAYKKwYB
+PQMBBwNCAATUO3SqgWApQso16sjpPkgrW4WMPqoYZXtvcohILWT33pf3nAJJ5AGk
+PDN4CQymuGUXbXu7rzCAaWS3jP62Qvydo4GfMIGcMAkGA1UdEwQCMAAwEAYKKwYB
 BAHWeQIBFgQCBQAwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0G
-A1UdDgQWBBSBALr9UIk69Md+cjUKU0tkXwICfTAfBgNVHSMEGDAWgBSbJguKmKm7
+A1UdDgQWBBTJUMIrA8MRaSat3+eCoENlkQHY9zAfBgNVHSMEGDAWgBSbJguKmKm7
 HbkfHOMaQDPtjheIqzAbBgNVHREEFDASghB0ZXN0LmV4YW1wbGUub3JnMA0GCSqG
-SIb3DQEBCwUAA4IBAQC4Ppe8xAoseCmelkcvGAQlAGFNtSjqGQuoxhfv+4F9jgjF
-dBbwU1swrSl46lrEWzTF+TuSgxiEf2JMiP9GIzmC7KKtyLiLoZq1DSOCcngmJVkg
-qJqdjS1fyDhGh+uiHKkH0Ypyc100hzEHISChgCe68ymXNYnKZfvTzppsQjCfPTdy
-99X4GB9aaev6FHV1GkW41pS+W7FKjqi04wD7q5Rw5Xvs7XRQmD2+bzLxwPDvyOJh
-x8Z+hZP997Tgad5MQ3wxWm0G4dRw/ANic13b8dOyINcTIN6TDCHYCwDrrkhFpnfD
-dJuWVd8YGkAMJoUmWKZEdVjtS12YW+whmJKZL9IW
+SIb3DQEBCwUAA4IBAQCB7xQBQkN89MoykesVhRg+nRMvnSPjKyaS/Az41Jt34c+w
+epXMLhfv/wLO+oxZCHngnO1Yv+ZYCKOj8lH1ejbltvUsKefDaI9HqLwIHQFV48sS
+3fnegZyQO4EaAHtSdoDH061Wh9EBId0epWNr/9IMrEuGBzgpniEqQ9OiNRl+5Wye
+1uMcoTUez1SxfaAxVG++75HFMyoEczNfqS0YYyKyPTpC/cYeA+z35daMfU35zsk7
+8z+oFmtWwpw2Z3GcNo7Il9d3g7beDtgYNcnFevrUDW+842ixPRQEMJXbMS7RHuKo
+yRYdhSR7+qoyh3nMRCjvn73zHSqs3VzuMs+HmFQz
 -----END CERTIFICATE-----
diff --git a/content/test/data/sxg/prime256v1-sha256-noext.public.pem b/content/test/data/sxg/prime256v1-sha256-noext.public.pem
index 56e76728..ae0fac8 100644
--- a/content/test/data/sxg/prime256v1-sha256-noext.public.pem
+++ b/content/test/data/sxg/prime256v1-sha256-noext.public.pem
@@ -1,7 +1,7 @@
 Certificate:
     Data:
         Version: 1 (0x0)
-        Serial Number: 2 (0x2)
+        Serial Number: 3 (0x3)
         Signature Algorithm: sha256WithRSAEncryption
         Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
         Validity
@@ -12,41 +12,42 @@
             Public Key Algorithm: id-ecPublicKey
                 Public-Key: (256 bit)
                 pub:
-                    04:56:24:49:7a:0a:a9:df:1a:0a:07:e3:ad:2a:23:
-                    6d:e9:33:25:68:5a:96:33:03:95:13:26:d9:44:ff:
-                    a8:59:16:6d:b6:7d:50:74:6e:d9:f7:9d:48:b2:61:
-                    ba:f8:6a:1e:fe:c6:1b:83:09:7f:84:9a:56:4f:68:
-                    a8:3e:28:d1:10
+                    04:d4:3b:74:aa:81:60:29:42:ca:35:ea:c8:e9:3e:
+                    48:2b:5b:85:8c:3e:aa:18:65:7b:6f:72:88:48:2d:
+                    64:f7:de:97:f7:9c:02:49:e4:01:a4:3c:33:78:09:
+                    0c:a6:b8:65:17:6d:7b:bb:af:30:80:69:64:b7:8c:
+                    fe:b6:42:fc:9d
                 ASN1 OID: prime256v1
                 NIST CURVE: P-256
     Signature Algorithm: sha256WithRSAEncryption
-         21:c1:39:a8:ba:d2:a5:ac:dd:a3:f5:00:24:cb:36:c3:80:0a:
-         32:f2:e1:7d:98:d3:43:44:75:80:15:6f:2a:63:88:0d:9d:f9:
-         ec:77:b5:0f:c6:0c:d8:32:00:95:4b:f4:0c:49:40:a4:7f:ba:
-         9f:f3:e0:65:c6:22:9f:c8:5b:ac:e6:5e:49:11:57:aa:ab:a2:
-         74:9d:d0:8a:7d:40:05:c1:8f:62:97:d2:f4:a4:7d:a6:20:60:
-         39:8c:eb:18:a7:28:5e:c8:66:b7:37:ea:10:ac:50:cc:14:69:
-         ac:1a:68:f6:be:56:19:db:08:15:2b:4e:64:3a:7b:82:ee:5e:
-         c9:5d:4f:b6:67:32:77:e5:b8:9d:00:16:5b:e6:18:d4:cd:b4:
-         92:1d:f2:47:a7:af:97:f3:ac:d2:8b:88:58:ba:3a:3a:2c:ff:
-         bf:79:b1:a0:c9:d6:b0:69:ce:68:d6:c3:2e:66:69:0b:8c:7a:
-         49:3f:9c:4a:e8:5f:f2:80:fb:cb:44:2c:7c:50:8a:db:24:6a:
-         26:a8:ea:6f:55:2c:ba:13:60:02:05:87:69:b9:b8:ce:99:a1:
-         ca:a6:45:00:26:a4:15:53:d2:28:f3:3c:28:2a:4c:17:65:b4:
-         50:e5:45:73:44:32:50:20:b1:c7:45:a1:e8:d2:3e:1d:d2:82:
-         5a:b7:42:ad
+    Signature Value:
+        01:8c:44:d8:dd:ce:88:5b:63:2e:72:28:ae:05:ff:c3:54:b6:
+        63:bb:2c:14:ac:b3:6c:c5:86:e8:06:f6:bb:78:35:b2:1e:62:
+        64:29:99:bc:51:fd:1a:e2:51:1a:f5:3b:aa:04:e2:93:47:6c:
+        da:08:3f:09:8b:14:ee:6b:bd:5e:48:cf:9e:60:af:a6:42:c1:
+        2a:16:91:f5:1d:f8:fe:b4:08:4f:86:d7:bd:ad:cb:b3:c5:90:
+        f8:db:a3:9c:2b:fd:ee:75:d5:ca:c9:a4:45:14:31:07:c0:f6:
+        cf:75:1b:f3:60:0b:f9:d0:56:46:4a:dd:55:bc:a4:7a:76:6f:
+        a4:70:07:68:b6:83:d7:ae:98:29:6c:5f:19:7a:0d:fb:f7:89:
+        d9:28:ee:63:99:57:f7:a3:2e:00:ef:cd:a0:8e:8f:5d:5c:c4:
+        55:fc:f3:46:c4:1d:72:40:fd:8b:89:f1:2d:97:71:b4:26:6f:
+        3f:7f:3d:d1:0c:4c:2d:f4:9c:e0:ee:dc:a4:c9:6b:e7:33:bb:
+        09:71:c7:c0:40:6b:d7:6d:85:fd:cb:6e:18:cd:03:34:9a:6a:
+        af:21:48:26:09:e1:ae:34:2e:3d:cc:49:a1:1d:77:e0:2b:f4:
+        e2:3d:cf:c4:17:75:10:13:19:b3:51:bc:ff:7f:e5:84:82:0f:
+        ed:c0:93:6e
 -----BEGIN CERTIFICATE-----
-MIICQzCCASsCAQIwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxEzARBgNV
+MIICQzCCASsCAQMwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxEzARBgNV
 BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoM
 B1Rlc3QgQ0ExFTATBgNVBAMMDFRlc3QgUm9vdCBDQTAeFw0xOTA2MDEwMDAwMDBa
 Fw0xOTA4MzAwMDAwMDBaMDcxGTAXBgNVBAMMEHRlc3QuZXhhbXBsZS5vcmcxDTAL
 BgNVBAoMBFRlc3QxCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
-QgAEViRJegqp3xoKB+OtKiNt6TMlaFqWMwOVEybZRP+oWRZttn1QdG7Z951IsmG6
-+Goe/sYbgwl/hJpWT2ioPijREDANBgkqhkiG9w0BAQsFAAOCAQEAIcE5qLrSpazd
-o/UAJMs2w4AKMvLhfZjTQ0R1gBVvKmOIDZ357He1D8YM2DIAlUv0DElApH+6n/Pg
-ZcYin8hbrOZeSRFXqquidJ3Qin1ABcGPYpfS9KR9piBgOYzrGKcoXshmtzfqEKxQ
-zBRprBpo9r5WGdsIFStOZDp7gu5eyV1Ptmcyd+W4nQAWW+YY1M20kh3yR6evl/Os
-0ouIWLo6Oiz/v3mxoMnWsGnOaNbDLmZpC4x6ST+cSuhf8oD7y0QsfFCK2yRqJqjq
-b1UsuhNgAgWHabm4zpmhyqZFACakFVPSKPM8KCpMF2W0UOVFc0QyUCCxx0Wh6NI+
-HdKCWrdCrQ==
+QgAE1Dt0qoFgKULKNerI6T5IK1uFjD6qGGV7b3KISC1k996X95wCSeQBpDwzeAkM
+prhlF217u68wgGlkt4z+tkL8nTANBgkqhkiG9w0BAQsFAAOCAQEAAYxE2N3OiFtj
+LnIorgX/w1S2Y7ssFKyzbMWG6Ab2u3g1sh5iZCmZvFH9GuJRGvU7qgTik0ds2gg/
+CYsU7mu9XkjPnmCvpkLBKhaR9R34/rQIT4bXva3Ls8WQ+NujnCv97nXVysmkRRQx
+B8D2z3Ub82AL+dBWRkrdVbykenZvpHAHaLaD166YKWxfGXoN+/eJ2SjuY5lX96Mu
+AO/NoI6PXVzEVfzzRsQdckD9i4nxLZdxtCZvP3890QxMLfSc4O7cpMlr5zO7CXHH
+wEBr122F/ctuGM0DNJpqryFIJgnhrjQuPcxJoR134Cv04j3PxBd1EBMZs1G8/3/l
+hIIP7cCTbg==
 -----END CERTIFICATE-----
diff --git a/content/test/data/sxg/prime256v1-sha256-validity-too-long.public.pem b/content/test/data/sxg/prime256v1-sha256-validity-too-long.public.pem
index 85b0d6d..282aa73 100644
--- a/content/test/data/sxg/prime256v1-sha256-validity-too-long.public.pem
+++ b/content/test/data/sxg/prime256v1-sha256-validity-too-long.public.pem
@@ -1,7 +1,7 @@
 Certificate:
     Data:
         Version: 3 (0x2)
-        Serial Number: 3 (0x3)
+        Serial Number: 4 (0x4)
         Signature Algorithm: sha256WithRSAEncryption
         Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
         Validity
@@ -12,11 +12,11 @@
             Public Key Algorithm: id-ecPublicKey
                 Public-Key: (256 bit)
                 pub:
-                    04:56:24:49:7a:0a:a9:df:1a:0a:07:e3:ad:2a:23:
-                    6d:e9:33:25:68:5a:96:33:03:95:13:26:d9:44:ff:
-                    a8:59:16:6d:b6:7d:50:74:6e:d9:f7:9d:48:b2:61:
-                    ba:f8:6a:1e:fe:c6:1b:83:09:7f:84:9a:56:4f:68:
-                    a8:3e:28:d1:10
+                    04:d4:3b:74:aa:81:60:29:42:ca:35:ea:c8:e9:3e:
+                    48:2b:5b:85:8c:3e:aa:18:65:7b:6f:72:88:48:2d:
+                    64:f7:de:97:f7:9c:02:49:e4:01:a4:3c:33:78:09:
+                    0c:a6:b8:65:17:6d:7b:bb:af:30:80:69:64:b7:8c:
+                    fe:b6:42:fc:9d
                 ASN1 OID: prime256v1
                 NIST CURVE: P-256
         X509v3 extensions:
@@ -29,43 +29,43 @@
             X509v3 Extended Key Usage: 
                 TLS Web Server Authentication
             X509v3 Subject Key Identifier: 
-                81:00:BA:FD:50:89:3A:F4:C7:7E:72:35:0A:53:4B:64:5F:02:02:7D
+                C9:50:C2:2B:03:C3:11:69:26:AD:DF:E7:82:A0:43:65:91:01:D8:F7
             X509v3 Authority Key Identifier: 
-                keyid:9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
-
+                9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
             X509v3 Subject Alternative Name: 
                 DNS:test.example.org
     Signature Algorithm: sha256WithRSAEncryption
-         6d:d8:47:dd:13:c8:7f:06:db:71:a1:ca:d3:ca:48:b7:16:8b:
-         b3:b8:c5:d0:73:16:d8:e2:ba:9e:fb:89:da:1a:21:f7:8c:46:
-         30:e7:26:21:94:7e:53:d7:04:0b:95:fa:df:4a:65:85:5e:bb:
-         80:43:03:62:71:3c:f7:7f:aa:06:da:7c:c7:0c:14:bf:92:7e:
-         b4:d0:0b:81:2d:de:2a:a9:99:40:e3:86:da:ee:4b:61:0c:81:
-         75:67:da:55:8e:4b:38:00:60:17:b0:9b:1a:d9:a2:74:8f:ac:
-         e8:4d:24:7c:91:ae:41:50:7a:27:e6:da:5f:92:57:6f:9b:3a:
-         ca:cd:5c:ec:55:f4:50:c9:13:c2:f3:0a:4f:53:8e:32:2b:7f:
-         0e:c9:f1:bb:7b:f0:74:e0:96:d4:e1:de:4b:37:8c:f5:97:de:
-         61:d2:b2:fe:77:97:fa:a2:f2:fb:d9:26:da:6b:cd:f3:68:1a:
-         d0:1d:24:aa:e9:e1:32:71:3a:41:fb:00:27:03:23:5b:25:6f:
-         19:bd:27:42:bb:ba:34:4e:9d:19:dc:65:e9:79:67:71:fa:28:
-         d7:97:a4:f1:5a:96:42:f9:5f:07:df:95:26:43:ff:25:62:43:
-         9b:c7:a2:04:7e:d6:87:a8:a5:b7:1c:e7:0a:aa:19:b5:fe:2f:
-         c0:1e:55:cc
+    Signature Value:
+        12:0b:73:8e:33:0c:aa:d0:4b:2d:f2:78:86:32:98:0a:cb:be:
+        e6:8a:93:c0:79:cc:df:04:c4:96:45:58:99:e2:b1:20:b4:0f:
+        62:f7:95:c3:1a:4e:6f:18:1b:73:f3:9c:ca:3f:28:21:fb:3c:
+        47:b6:e9:c3:82:af:21:de:c2:5d:85:f4:dc:24:1a:c5:bf:62:
+        10:ca:ac:e2:a4:9e:5d:1f:95:92:8c:8b:bd:21:79:29:e5:a5:
+        77:96:20:d8:25:ee:db:d0:b7:f2:d8:32:b4:08:39:54:5f:fd:
+        21:3d:07:08:97:c6:a2:d2:56:52:50:d9:85:43:5c:27:96:55:
+        75:b7:ad:ae:44:3b:39:fa:fb:37:28:fa:16:0b:e4:13:b7:d5:
+        be:3b:cf:70:98:fe:58:e0:4f:13:9a:1b:52:2c:1b:98:16:7b:
+        c9:72:8a:25:51:5f:51:5e:e0:dc:68:e0:58:5a:ce:90:8c:fb:
+        e6:41:dc:8a:44:48:2e:7d:56:cb:7d:ac:11:5f:8f:7d:b6:7b:
+        1d:e7:b2:4b:5b:a9:be:44:ee:6d:68:d5:d3:ad:8f:9a:b4:35:
+        b4:d7:39:56:3a:8c:94:f7:a8:08:a2:12:58:0d:8c:74:a5:7b:
+        20:3b:75:1d:fb:0a:43:ac:f3:0e:55:62:09:b7:ef:df:5c:c7:
+        e2:26:03:64
 -----BEGIN CERTIFICATE-----
-MIIC6jCCAdKgAwIBAgIBAzANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
+MIIC6jCCAdKgAwIBAgIBBDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
 MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
 A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE5MDUwMTAw
 MDAwMFoXDTE5MDczMTAwMDAwMFowNzEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLm9y
 ZzENMAsGA1UECgwEVGVzdDELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjO
-PQMBBwNCAARWJEl6CqnfGgoH460qI23pMyVoWpYzA5UTJtlE/6hZFm22fVB0btn3
-nUiyYbr4ah7+xhuDCX+EmlZPaKg+KNEQo4GfMIGcMAkGA1UdEwQCMAAwEAYKKwYB
+PQMBBwNCAATUO3SqgWApQso16sjpPkgrW4WMPqoYZXtvcohILWT33pf3nAJJ5AGk
+PDN4CQymuGUXbXu7rzCAaWS3jP62Qvydo4GfMIGcMAkGA1UdEwQCMAAwEAYKKwYB
 BAHWeQIBFgQCBQAwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0G
-A1UdDgQWBBSBALr9UIk69Md+cjUKU0tkXwICfTAfBgNVHSMEGDAWgBSbJguKmKm7
+A1UdDgQWBBTJUMIrA8MRaSat3+eCoENlkQHY9zAfBgNVHSMEGDAWgBSbJguKmKm7
 HbkfHOMaQDPtjheIqzAbBgNVHREEFDASghB0ZXN0LmV4YW1wbGUub3JnMA0GCSqG
-SIb3DQEBCwUAA4IBAQBt2EfdE8h/BttxocrTyki3FouzuMXQcxbY4rqe+4naGiH3
-jEYw5yYhlH5T1wQLlfrfSmWFXruAQwNicTz3f6oG2nzHDBS/kn600AuBLd4qqZlA
-44ba7kthDIF1Z9pVjks4AGAXsJsa2aJ0j6zoTSR8ka5BUHon5tpfkldvmzrKzVzs
-VfRQyRPC8wpPU44yK38OyfG7e/B04JbU4d5LN4z1l95h0rL+d5f6ovL72Sbaa83z
-aBrQHSSq6eEycTpB+wAnAyNbJW8ZvSdCu7o0Tp0Z3GXpeWdx+ijXl6TxWpZC+V8H
-35UmQ/8lYkObx6IEftaHqKW3HOcKqhm1/i/AHlXM
+SIb3DQEBCwUAA4IBAQASC3OOMwyq0Est8niGMpgKy77mipPAeczfBMSWRViZ4rEg
+tA9i95XDGk5vGBtz85zKPygh+zxHtunDgq8h3sJdhfTcJBrFv2IQyqzipJ5dH5WS
+jIu9IXkp5aV3liDYJe7b0Lfy2DK0CDlUX/0hPQcIl8ai0lZSUNmFQ1wnllV1t62u
+RDs5+vs3KPoWC+QTt9W+O89wmP5Y4E8TmhtSLBuYFnvJcoolUV9RXuDcaOBYWs6Q
+jPvmQdyKREgufVbLfawRX499tnsd57JLW6m+RO5taNXTrY+atDW01zlWOoyU96gI
+ohJYDYx0pXsgO3Ud+wpDrPMOVWIJt+/fXMfiJgNk
 -----END CERTIFICATE-----
diff --git a/content/test/data/sxg/prime256v1-sha256.csr b/content/test/data/sxg/prime256v1-sha256.csr
index 5382a1c..1aaf15a 100644
--- a/content/test/data/sxg/prime256v1-sha256.csr
+++ b/content/test/data/sxg/prime256v1-sha256.csr
@@ -1,8 +1,8 @@
 -----BEGIN CERTIFICATE REQUEST-----
-MIHyMIGZAgEAMDcxGTAXBgNVBAMMEHRlc3QuZXhhbXBsZS5vcmcxDTALBgNVBAoM
-BFRlc3QxCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEViRJ
-egqp3xoKB+OtKiNt6TMlaFqWMwOVEybZRP+oWRZttn1QdG7Z951IsmG6+Goe/sYb
-gwl/hJpWT2ioPijREKAAMAoGCCqGSM49BAMCA0gAMEUCIQD0E7nJvlMXoDembcQv
-BxrArv3zWZP0/2BL1rZKuszDVQIgfZkoHFDQcmd+D7/opKRqd42hHMbKvKAJZraN
-/U0Z54Y=
+MIHzMIGZAgEAMDcxGTAXBgNVBAMMEHRlc3QuZXhhbXBsZS5vcmcxDTALBgNVBAoM
+BFRlc3QxCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Dt0
+qoFgKULKNerI6T5IK1uFjD6qGGV7b3KISC1k996X95wCSeQBpDwzeAkMprhlF217
+u68wgGlkt4z+tkL8naAAMAoGCCqGSM49BAMCA0kAMEYCIQCmwOaw6DIUOGtlMcQJ
+N82CszlI+vHEXxWZ1OopEz7BRAIhALhH/wBKOdMHJRUo6V/fXD+hFzixNjvYBUaj
+I+GK/HPD
 -----END CERTIFICATE REQUEST-----
diff --git a/content/test/data/sxg/prime256v1-sha256.public.pem b/content/test/data/sxg/prime256v1-sha256.public.pem
index 4ef0c073..d32eebf 100644
--- a/content/test/data/sxg/prime256v1-sha256.public.pem
+++ b/content/test/data/sxg/prime256v1-sha256.public.pem
@@ -12,11 +12,11 @@
             Public Key Algorithm: id-ecPublicKey
                 Public-Key: (256 bit)
                 pub:
-                    04:56:24:49:7a:0a:a9:df:1a:0a:07:e3:ad:2a:23:
-                    6d:e9:33:25:68:5a:96:33:03:95:13:26:d9:44:ff:
-                    a8:59:16:6d:b6:7d:50:74:6e:d9:f7:9d:48:b2:61:
-                    ba:f8:6a:1e:fe:c6:1b:83:09:7f:84:9a:56:4f:68:
-                    a8:3e:28:d1:10
+                    04:d4:3b:74:aa:81:60:29:42:ca:35:ea:c8:e9:3e:
+                    48:2b:5b:85:8c:3e:aa:18:65:7b:6f:72:88:48:2d:
+                    64:f7:de:97:f7:9c:02:49:e4:01:a4:3c:33:78:09:
+                    0c:a6:b8:65:17:6d:7b:bb:af:30:80:69:64:b7:8c:
+                    fe:b6:42:fc:9d
                 ASN1 OID: prime256v1
                 NIST CURVE: P-256
         X509v3 extensions:
@@ -29,43 +29,43 @@
             X509v3 Extended Key Usage: 
                 TLS Web Server Authentication
             X509v3 Subject Key Identifier: 
-                81:00:BA:FD:50:89:3A:F4:C7:7E:72:35:0A:53:4B:64:5F:02:02:7D
+                C9:50:C2:2B:03:C3:11:69:26:AD:DF:E7:82:A0:43:65:91:01:D8:F7
             X509v3 Authority Key Identifier: 
-                keyid:9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
-
+                9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
             X509v3 Subject Alternative Name: 
                 DNS:test.example.org
     Signature Algorithm: sha256WithRSAEncryption
-         1e:dd:53:10:3d:08:8a:b7:38:63:3a:67:2c:62:f9:70:65:dd:
-         7d:67:7c:40:8e:ff:aa:3a:2c:75:69:87:3e:f7:3d:9d:99:4f:
-         df:85:3b:10:54:44:2d:f5:3d:bf:ff:3a:8d:49:ba:08:cb:3e:
-         10:ba:9f:4f:05:a0:68:ab:75:78:84:c7:0a:b6:f2:e2:d5:82:
-         a7:eb:87:7c:14:07:4f:68:cb:24:f8:9d:34:cc:56:be:6b:56:
-         44:0a:eb:cf:f4:35:27:32:cf:3a:6e:37:9b:8c:fb:fe:4f:d2:
-         37:74:86:e8:93:a8:27:51:e1:f3:87:f2:4f:b7:c9:d8:0e:f0:
-         7d:53:3f:cb:34:9e:5c:6e:84:bc:65:fb:74:a0:80:d1:ea:c4:
-         2a:c3:d5:a4:e2:b0:8d:ca:c1:6c:a3:34:f8:5a:33:de:a9:e0:
-         a8:a5:3d:6b:72:e4:52:4b:f9:bd:42:74:c6:47:54:80:67:81:
-         36:ee:f1:ff:2a:bf:da:f3:a6:dc:12:f7:b9:41:72:4a:aa:14:
-         9b:f5:d5:b5:b8:e4:73:37:03:97:55:0a:c5:89:f6:c5:d2:0c:
-         7f:fc:de:4e:f6:fd:dd:8f:27:52:c8:57:b4:f1:cf:57:45:23:
-         b9:45:38:ee:92:61:56:16:82:da:41:5e:70:f2:3b:2e:38:f8:
-         ee:a9:ce:fe
+    Signature Value:
+        81:e6:b3:2a:69:ba:29:39:df:a9:12:2c:86:b2:1a:8a:a7:2c:
+        67:ab:53:77:35:2e:cf:bc:e4:b4:d8:6f:13:bb:54:26:a8:37:
+        9d:74:83:f8:38:05:02:9e:87:bc:d8:6e:22:fe:cb:ad:9a:87:
+        91:c7:e9:70:f5:f9:18:ec:78:43:b7:0d:12:de:e1:8a:85:cb:
+        2f:49:4b:1f:7b:5c:d8:8f:54:86:15:f1:f5:7f:49:4b:7f:4d:
+        b9:80:19:d5:6a:09:b4:e4:52:d0:e2:be:23:b9:12:56:1a:78:
+        19:dc:14:1c:0c:31:75:36:d9:22:7c:54:72:44:13:b1:83:5a:
+        02:a9:9b:dc:2d:de:60:67:92:19:25:b3:82:85:72:eb:5f:57:
+        a9:d7:f3:50:7b:a8:ef:7b:af:ec:27:ba:11:9e:4e:06:f9:f1:
+        16:ba:86:e1:60:c2:a1:dd:43:cb:f3:c7:62:fb:6d:96:2d:0e:
+        b2:da:1e:62:18:f0:bf:3b:e1:46:3b:e2:ba:52:0d:19:db:9d:
+        5a:fc:b8:39:fe:18:fa:ec:03:56:74:1c:e8:5c:6c:8c:01:72:
+        11:93:48:98:af:1b:e2:ec:db:47:c3:36:90:73:c9:6c:40:44:
+        de:e4:09:3e:4d:22:b5:a8:06:e5:0c:50:30:63:ef:fb:27:5f:
+        ad:08:5e:56
 -----BEGIN CERTIFICATE-----
 MIIC6jCCAdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
 MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
 A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE5MDYwMTAw
 MDAwMFoXDTE5MDgzMDAwMDAwMFowNzEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLm9y
 ZzENMAsGA1UECgwEVGVzdDELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjO
-PQMBBwNCAARWJEl6CqnfGgoH460qI23pMyVoWpYzA5UTJtlE/6hZFm22fVB0btn3
-nUiyYbr4ah7+xhuDCX+EmlZPaKg+KNEQo4GfMIGcMAkGA1UdEwQCMAAwEAYKKwYB
+PQMBBwNCAATUO3SqgWApQso16sjpPkgrW4WMPqoYZXtvcohILWT33pf3nAJJ5AGk
+PDN4CQymuGUXbXu7rzCAaWS3jP62Qvydo4GfMIGcMAkGA1UdEwQCMAAwEAYKKwYB
 BAHWeQIBFgQCBQAwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0G
-A1UdDgQWBBSBALr9UIk69Md+cjUKU0tkXwICfTAfBgNVHSMEGDAWgBSbJguKmKm7
+A1UdDgQWBBTJUMIrA8MRaSat3+eCoENlkQHY9zAfBgNVHSMEGDAWgBSbJguKmKm7
 HbkfHOMaQDPtjheIqzAbBgNVHREEFDASghB0ZXN0LmV4YW1wbGUub3JnMA0GCSqG
-SIb3DQEBCwUAA4IBAQAe3VMQPQiKtzhjOmcsYvlwZd19Z3xAjv+qOix1aYc+9z2d
-mU/fhTsQVEQt9T2//zqNSboIyz4Qup9PBaBoq3V4hMcKtvLi1YKn64d8FAdPaMsk
-+J00zFa+a1ZECuvP9DUnMs86bjebjPv+T9I3dIbok6gnUeHzh/JPt8nYDvB9Uz/L
-NJ5cboS8Zft0oIDR6sQqw9Wk4rCNysFsozT4WjPeqeCopT1rcuRSS/m9QnTGR1SA
-Z4E27vH/Kr/a86bcEve5QXJKqhSb9dW1uORzNwOXVQrFifbF0gx//N5O9v3djydS
-yFe08c9XRSO5RTjukmFWFoLaQV5w8jsuOPjuqc7+
+SIb3DQEBCwUAA4IBAQCB5rMqabopOd+pEiyGshqKpyxnq1N3NS7PvOS02G8Tu1Qm
+qDeddIP4OAUCnoe82G4i/sutmoeRx+lw9fkY7HhDtw0S3uGKhcsvSUsfe1zYj1SG
+FfH1f0lLf025gBnVagm05FLQ4r4juRJWGngZ3BQcDDF1NtkifFRyRBOxg1oCqZvc
+Ld5gZ5IZJbOChXLrX1ep1/NQe6jve6/sJ7oRnk4G+fEWuobhYMKh3UPL88di+22W
+LQ6y2h5iGPC/O+FGO+K6Ug0Z251a/Lg5/hj67ANWdBzoXGyMAXIRk0iYrxvi7NtH
+wzaQc8lsQETe5Ak+TSK1qAblDFAwY+/7J1+tCF5W
 -----END CERTIFICATE-----
diff --git a/content/test/data/sxg/prime256v1.key b/content/test/data/sxg/prime256v1.key
index bde337a0..8929528 100644
--- a/content/test/data/sxg/prime256v1.key
+++ b/content/test/data/sxg/prime256v1.key
@@ -2,7 +2,7 @@
 BggqhkjOPQMBBw==
 -----END EC PARAMETERS-----
 -----BEGIN EC PRIVATE KEY-----
-MHcCAQEEIEq/FpadFG7SCIr3Ca9TMHlpZuUr8MqAKoYbI7eRNd9/oAoGCCqGSM49
-AwEHoUQDQgAEViRJegqp3xoKB+OtKiNt6TMlaFqWMwOVEybZRP+oWRZttn1QdG7Z
-951IsmG6+Goe/sYbgwl/hJpWT2ioPijREA==
+MHcCAQEEIBcB7+YllD0Xuh9bxWJFU82DGu40PHXbRRrrko7uRC5ooAoGCCqGSM49
+AwEHoUQDQgAE1Dt0qoFgKULKNerI6T5IK1uFjD6qGGV7b3KISC1k996X95wCSeQB
+pDwzeAkMprhlF217u68wgGlkt4z+tkL8nQ==
 -----END EC PRIVATE KEY-----
diff --git a/content/test/data/sxg/secp384r1-sha256.csr b/content/test/data/sxg/secp384r1-sha256.csr
index 6237f70..8b11f80 100644
--- a/content/test/data/sxg/secp384r1-sha256.csr
+++ b/content/test/data/sxg/secp384r1-sha256.csr
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE REQUEST-----
 MIIBLzCBtgIBADA3MRkwFwYDVQQDDBB0ZXN0LmV4YW1wbGUub3JnMQ0wCwYDVQQK
-DARUZXN0MQswCQYDVQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABHGex78f
-ymSJY5PstpHt/Qx/YTCNzA/gm5TyZ7iuYEDZ9Q/eJKCAinQm1ZTBW05PYYpbarAZ
-a2tfUP9Vkb5fbC1CNiwwUHWicdAnS9ZkC7YfzBXI2dsDpL/prMiCzxYAVKAAMAoG
-CCqGSM49BAMCA2gAMGUCMEHohXNNEAeTQzDfyqqbDdVJjKAmrNJH91TV3XhUYqVj
-9g8oqf+en9WHQMePAIWoQQIxAJOK7rQ5zaxyjODB52yatItorm7bs0tCUMT/0c8o
-Wykn0PfLgx41EuP3cRw5m2dw3Q==
+DARUZXN0MQswCQYDVQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ1dmc5W
+BRpOupn/yN993E5vXViPg+lcwRC+DKxNZtZ4zzEeKseCoEJa/wAC8IGVbjwNvg45
+Wxjed7EfWnqQgrILC4mewejxUAr1gm7LxB4F9Z70UgnlrFK43Vt8hngQDqAAMAoG
+CCqGSM49BAMCA2gAMGUCMGPP24kHcLe8CwOQhkveMcqiBKr8sDKB71maA1GKPyyC
+2dCpzfrzd4B6Vndr7xo3nwIxANP3jBZLe9Y/lhrAy2H9AZW6zXFYnzUxqqjlaXg8
+t/qGI42bhmPn5gS4TsAy7SGmDQ==
 -----END CERTIFICATE REQUEST-----
diff --git a/content/test/data/sxg/secp384r1-sha256.public.pem b/content/test/data/sxg/secp384r1-sha256.public.pem
index 073643b4..b181a14 100644
--- a/content/test/data/sxg/secp384r1-sha256.public.pem
+++ b/content/test/data/sxg/secp384r1-sha256.public.pem
@@ -1,7 +1,7 @@
 Certificate:
     Data:
         Version: 3 (0x2)
-        Serial Number: 5 (0x5)
+        Serial Number: 6 (0x6)
         Signature Algorithm: sha256WithRSAEncryption
         Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
         Validity
@@ -12,13 +12,13 @@
             Public Key Algorithm: id-ecPublicKey
                 Public-Key: (384 bit)
                 pub:
-                    04:71:9e:c7:bf:1f:ca:64:89:63:93:ec:b6:91:ed:
-                    fd:0c:7f:61:30:8d:cc:0f:e0:9b:94:f2:67:b8:ae:
-                    60:40:d9:f5:0f:de:24:a0:80:8a:74:26:d5:94:c1:
-                    5b:4e:4f:61:8a:5b:6a:b0:19:6b:6b:5f:50:ff:55:
-                    91:be:5f:6c:2d:42:36:2c:30:50:75:a2:71:d0:27:
-                    4b:d6:64:0b:b6:1f:cc:15:c8:d9:db:03:a4:bf:e9:
-                    ac:c8:82:cf:16:00:54
+                    04:9d:5d:99:ce:56:05:1a:4e:ba:99:ff:c8:df:7d:
+                    dc:4e:6f:5d:58:8f:83:e9:5c:c1:10:be:0c:ac:4d:
+                    66:d6:78:cf:31:1e:2a:c7:82:a0:42:5a:ff:00:02:
+                    f0:81:95:6e:3c:0d:be:0e:39:5b:18:de:77:b1:1f:
+                    5a:7a:90:82:b2:0b:0b:89:9e:c1:e8:f1:50:0a:f5:
+                    82:6e:cb:c4:1e:05:f5:9e:f4:52:09:e5:ac:52:b8:
+                    dd:5b:7c:86:78:10:0e
                 ASN1 OID: secp384r1
                 NIST CURVE: P-384
         X509v3 extensions:
@@ -31,44 +31,44 @@
             X509v3 Extended Key Usage: 
                 TLS Web Server Authentication
             X509v3 Subject Key Identifier: 
-                93:76:D0:33:7C:F7:12:E0:D0:D8:1F:31:65:C0:97:8A:1D:34:77:1B
+                16:99:B7:91:28:79:55:CB:4D:1D:C9:94:2E:42:39:B8:EF:74:53:AE
             X509v3 Authority Key Identifier: 
-                keyid:9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
-
+                9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
             X509v3 Subject Alternative Name: 
                 DNS:test.example.org
     Signature Algorithm: sha256WithRSAEncryption
-         0c:6b:0c:18:98:13:4e:39:ad:0c:19:d7:62:f0:06:20:5f:75:
-         5d:bd:43:f0:1a:ce:2e:fc:90:90:8f:3f:42:52:03:b9:82:ea:
-         a7:5f:cc:cf:c8:a4:69:35:55:8f:8f:d1:b7:22:d6:ee:78:91:
-         5d:7a:af:d7:80:55:26:f1:1d:f5:1d:c6:d1:64:f4:19:95:0c:
-         bd:44:cd:bd:57:68:4e:a3:b9:ce:18:a7:5f:f8:e0:0f:a1:42:
-         71:cb:b9:0e:7f:38:24:01:3c:03:45:f1:92:e1:cf:d5:ce:65:
-         b6:94:e0:b2:f3:b6:26:bf:3d:cc:0a:3a:45:a3:af:fe:c9:5b:
-         f5:b9:46:b9:32:87:16:5d:01:30:98:03:ea:d3:c4:3f:14:d8:
-         0d:cf:e5:0a:3b:11:29:6e:3f:ad:b6:d1:cc:da:c7:14:46:21:
-         1d:6a:dd:b8:3e:c4:df:fb:35:f9:63:7f:8e:51:92:5f:01:38:
-         d2:fc:7f:d1:70:be:bd:69:7c:5c:75:56:1e:0c:43:35:af:04:
-         ed:1b:d1:73:67:32:e3:63:c7:a5:65:a8:97:d2:4b:5e:1e:23:
-         2c:12:f9:f9:ab:1f:03:0f:b5:4d:c1:c7:52:38:5a:40:34:ec:
-         30:9c:6d:45:21:fa:06:7e:6d:8b:93:51:f0:36:d9:02:f6:87:
-         12:03:06:2c
+    Signature Value:
+        ba:38:8f:9f:e1:87:dc:27:d9:51:4a:44:c0:04:e8:20:d2:bc:
+        58:91:d1:bb:b0:f3:05:8a:4a:70:3c:b3:ba:8f:06:85:c2:b7:
+        31:02:88:16:c7:9c:bd:c4:c5:55:4b:e9:d2:33:c9:fc:4a:da:
+        40:23:23:dd:a5:fc:90:ca:db:77:c8:ab:93:f8:c1:2a:74:39:
+        38:12:29:86:92:2a:c7:35:a4:86:cc:0f:2f:9b:e4:f6:29:4e:
+        5d:91:93:2f:37:7f:df:86:93:39:42:77:ea:81:06:ea:8f:67:
+        39:3d:2b:c7:77:f0:1b:37:18:9c:98:ec:00:69:0d:35:51:8a:
+        7c:f9:fb:f6:26:3d:83:2e:d9:4e:28:5d:67:4b:5a:b0:16:23:
+        bb:b2:62:3e:dd:71:cc:66:0f:c2:49:8a:5a:ca:87:ea:f7:a0:
+        3e:80:b4:ef:37:8a:66:78:12:9f:95:ae:06:f9:8a:4f:a7:b0:
+        a4:f3:25:97:9c:cd:5a:02:8d:2b:10:50:6f:dc:84:f7:8e:f5:
+        d3:f6:37:2a:64:03:30:44:6d:21:d2:92:c4:c5:68:bb:67:bd:
+        23:43:0d:eb:4a:e0:13:d1:78:25:c4:de:d9:e3:4f:b8:a2:00:
+        fa:c0:48:76:df:e3:94:91:20:fb:3f:d7:e4:62:c1:27:5a:3b:
+        a4:e6:1f:a9
 -----BEGIN CERTIFICATE-----
-MIIDBzCCAe+gAwIBAgIBBTANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
+MIIDBzCCAe+gAwIBAgIBBjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
 MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
 A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE5MDYwMTAw
 MDAwMFoXDTE5MDgzMDAwMDAwMFowNzEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLm9y
 ZzENMAsGA1UECgwEVGVzdDELMAkGA1UEBhMCVVMwdjAQBgcqhkjOPQIBBgUrgQQA
-IgNiAARxnse/H8pkiWOT7LaR7f0Mf2EwjcwP4JuU8me4rmBA2fUP3iSggIp0JtWU
-wVtOT2GKW2qwGWtrX1D/VZG+X2wtQjYsMFB1onHQJ0vWZAu2H8wVyNnbA6S/6azI
-gs8WAFSjgZ8wgZwwCQYDVR0TBAIwADAQBgorBgEEAdZ5AgEWBAIFADALBgNVHQ8E
-BAMCBeAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYEFJN20DN89xLg0Ngf
-MWXAl4odNHcbMB8GA1UdIwQYMBaAFJsmC4qYqbsduR8c4xpAM+2OF4irMBsGA1Ud
-EQQUMBKCEHRlc3QuZXhhbXBsZS5vcmcwDQYJKoZIhvcNAQELBQADggEBAAxrDBiY
-E045rQwZ12LwBiBfdV29Q/Aazi78kJCPP0JSA7mC6qdfzM/IpGk1VY+P0bci1u54
-kV16r9eAVSbxHfUdxtFk9BmVDL1Ezb1XaE6juc4Yp1/44A+hQnHLuQ5/OCQBPANF
-8ZLhz9XOZbaU4LLztia/PcwKOkWjr/7JW/W5RrkyhxZdATCYA+rTxD8U2A3P5Qo7
-ESluP6220czaxxRGIR1q3bg+xN/7Nfljf45Rkl8BONL8f9Fwvr1pfFx1Vh4MQzWv
-BO0b0XNnMuNjx6VlqJfSS14eIywS+fmrHwMPtU3Bx1I4WkA07DCcbUUh+gZ+bYuT
-UfA22QL2hxIDBiw=
+IgNiAASdXZnOVgUaTrqZ/8jffdxOb11Yj4PpXMEQvgysTWbWeM8xHirHgqBCWv8A
+AvCBlW48Db4OOVsY3nexH1p6kIKyCwuJnsHo8VAK9YJuy8QeBfWe9FIJ5axSuN1b
+fIZ4EA6jgZ8wgZwwCQYDVR0TBAIwADAQBgorBgEEAdZ5AgEWBAIFADALBgNVHQ8E
+BAMCBeAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYEFBaZt5EoeVXLTR3J
+lC5CObjvdFOuMB8GA1UdIwQYMBaAFJsmC4qYqbsduR8c4xpAM+2OF4irMBsGA1Ud
+EQQUMBKCEHRlc3QuZXhhbXBsZS5vcmcwDQYJKoZIhvcNAQELBQADggEBALo4j5/h
+h9wn2VFKRMAE6CDSvFiR0buw8wWKSnA8s7qPBoXCtzECiBbHnL3ExVVL6dIzyfxK
+2kAjI92l/JDK23fIq5P4wSp0OTgSKYaSKsc1pIbMDy+b5PYpTl2Rky83f9+GkzlC
+d+qBBuqPZzk9K8d38Bs3GJyY7ABpDTVRinz5+/YmPYMu2U4oXWdLWrAWI7uyYj7d
+ccxmD8JJilrKh+r3oD6AtO83imZ4Ep+Vrgb5ik+nsKTzJZeczVoCjSsQUG/chPeO
+9dP2NypkAzBEbSHSksTFaLtnvSNDDetK4BPReCXE3tnjT7iiAPrASHbf45SRIPs/
+1+RiwSdaO6TmH6k=
 -----END CERTIFICATE-----
diff --git a/content/test/data/sxg/secp384r1.key b/content/test/data/sxg/secp384r1.key
index c2cde1d2..c6d619a4 100644
--- a/content/test/data/sxg/secp384r1.key
+++ b/content/test/data/sxg/secp384r1.key
@@ -2,8 +2,8 @@
 BgUrgQQAIg==
 -----END EC PARAMETERS-----
 -----BEGIN EC PRIVATE KEY-----
-MIGkAgEBBDCpLgEyXpuUhNnL70pb0AtsB7k+LT6oD36Q/2Mrj7ukipmdhnq5LA7b
-S1NInwaft6ygBwYFK4EEACKhZANiAARxnse/H8pkiWOT7LaR7f0Mf2EwjcwP4JuU
-8me4rmBA2fUP3iSggIp0JtWUwVtOT2GKW2qwGWtrX1D/VZG+X2wtQjYsMFB1onHQ
-J0vWZAu2H8wVyNnbA6S/6azIgs8WAFQ=
+MIGkAgEBBDCPiHBVzGW+xFBu39ie7rtdpuTyS1d11tVeHUSdiMrB9uyXoeg5VM9A
+N9E5yogyQI6gBwYFK4EEACKhZANiAASdXZnOVgUaTrqZ/8jffdxOb11Yj4PpXMEQ
+vgysTWbWeM8xHirHgqBCWv8AAvCBlW48Db4OOVsY3nexH1p6kIKyCwuJnsHo8VAK
+9YJuy8QeBfWe9FIJ5axSuN1bfIZ4EA4=
 -----END EC PRIVATE KEY-----
diff --git a/content/test/data/sxg/test.example.com_invalid_test.sxg b/content/test/data/sxg/test.example.com_invalid_test.sxg
index f35afc9..5f6a933 100644
--- a/content/test/data/sxg/test.example.com_invalid_test.sxg
+++ b/content/test/data/sxg/test.example.com_invalid_test.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org-long-validity.public.pem.cbor b/content/test/data/sxg/test.example.org-long-validity.public.pem.cbor
index 2e47392..422e936 100644
--- a/content/test/data/sxg/test.example.org-long-validity.public.pem.cbor
+++ b/content/test/data/sxg/test.example.org-long-validity.public.pem.cbor
Binary files differ
diff --git a/content/test/data/sxg/test.example.org-noext.public.pem.cbor b/content/test/data/sxg/test.example.org-noext.public.pem.cbor
index ca11624..d3d31405 100644
--- a/content/test/data/sxg/test.example.org-noext.public.pem.cbor
+++ b/content/test/data/sxg/test.example.org-noext.public.pem.cbor
Binary files differ
diff --git a/content/test/data/sxg/test.example.org-validity-too-long.public.pem.cbor b/content/test/data/sxg/test.example.org-validity-too-long.public.pem.cbor
index 4fa4b40..1e040e8c 100644
--- a/content/test/data/sxg/test.example.org-validity-too-long.public.pem.cbor
+++ b/content/test/data/sxg/test.example.org-validity-too-long.public.pem.cbor
Binary files differ
diff --git a/content/test/data/sxg/test.example.org.public.pem.cbor b/content/test/data/sxg/test.example.org.public.pem.cbor
index 4a647a182..5f2dac82 100644
--- a/content/test/data/sxg/test.example.org.public.pem.cbor
+++ b/content/test/data/sxg/test.example.org.public.pem.cbor
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_bad_content_type.sxg b/content/test/data/sxg/test.example.org_bad_content_type.sxg
index 09996d1..3b3b4a94 100644
--- a/content/test/data/sxg/test.example.org_bad_content_type.sxg
+++ b/content/test/data/sxg/test.example.org_bad_content_type.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_cert_validity_too_long.sxg b/content/test/data/sxg/test.example.org_cert_validity_too_long.sxg
index d0e38bb..50f27ed 100644
--- a/content/test/data/sxg/test.example.org_cert_validity_too_long.sxg
+++ b/content/test/data/sxg/test.example.org_cert_validity_too_long.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_csp.sxg b/content/test/data/sxg/test.example.org_csp.sxg
index d7e689de..d74d6af 100644
--- a/content/test/data/sxg/test.example.org_csp.sxg
+++ b/content/test/data/sxg/test.example.org_csp.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_fr_variant.sxg b/content/test/data/sxg/test.example.org_fr_variant.sxg
index 3160130c..3f2fb2a8 100644
--- a/content/test/data/sxg/test.example.org_fr_variant.sxg
+++ b/content/test/data/sxg/test.example.org_fr_variant.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_hello.txt.sxg b/content/test/data/sxg/test.example.org_hello.txt.sxg
index 5585239..5ae0e9b3 100644
--- a/content/test/data/sxg/test.example.org_hello.txt.sxg
+++ b/content/test/data/sxg/test.example.org_hello.txt.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_long_cert_validity.sxg b/content/test/data/sxg/test.example.org_long_cert_validity.sxg
index 7c94f61e..2061734 100644
--- a/content/test/data/sxg/test.example.org_long_cert_validity.sxg
+++ b/content/test/data/sxg/test.example.org_long_cert_validity.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_noext_test.sxg b/content/test/data/sxg/test.example.org_noext_test.sxg
index d29b3fd..defde0b7 100644
--- a/content/test/data/sxg/test.example.org_noext_test.sxg
+++ b/content/test/data/sxg/test.example.org_noext_test.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test.html.gz.sxg b/content/test/data/sxg/test.example.org_test.html.gz.sxg
index 1560f89..72f8f93 100644
--- a/content/test/data/sxg/test.example.org_test.html.gz.sxg
+++ b/content/test/data/sxg/test.example.org_test.html.gz.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test.sxg b/content/test/data/sxg/test.example.org_test.sxg
index f048767..5b19b01 100644
--- a/content/test/data/sxg/test.example.org_test.sxg
+++ b/content/test/data/sxg/test.example.org_test.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_bad_mice.sxg b/content/test/data/sxg/test.example.org_test_bad_mice.sxg
index 4118339..f973a1a 100644
--- a/content/test/data/sxg/test.example.org_test_bad_mice.sxg
+++ b/content/test/data/sxg/test.example.org_test_bad_mice.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_bad_mice_small.sxg b/content/test/data/sxg/test.example.org_test_bad_mice_small.sxg
index d01220e..8e0e4e3 100644
--- a/content/test/data/sxg/test.example.org_test_bad_mice_small.sxg
+++ b/content/test/data/sxg/test.example.org_test_bad_mice_small.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_download.sxg b/content/test/data/sxg/test.example.org_test_download.sxg
index f048767..5b19b01 100644
--- a/content/test/data/sxg/test.example.org_test_download.sxg
+++ b/content/test/data/sxg/test.example.org_test_download.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg b/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg
index 99a3448..0f547183 100644
--- a/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg
+++ b/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_invalid_content_type.sxg b/content/test/data/sxg/test.example.org_test_invalid_content_type.sxg
index f048767..5b19b01 100644
--- a/content/test/data/sxg/test.example.org_test_invalid_content_type.sxg
+++ b/content/test/data/sxg/test.example.org_test_invalid_content_type.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg b/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg
index e9515088..09a4d552 100644
--- a/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg
+++ b/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg b/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg
index f048767..5b19b01 100644
--- a/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg
+++ b/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_vary_cookie.sxg b/content/test/data/sxg/test.example.org_vary_cookie.sxg
index ea2e0c15a..4c1140ba 100644
--- a/content/test/data/sxg/test.example.org_vary_cookie.sxg
+++ b/content/test/data/sxg/test.example.org_vary_cookie.sxg
Binary files differ
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index 4b657a0..2323c04 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -917,7 +917,8 @@
                       test_rect=[0, 0, 200, 100]),
         PixelTestPage('pixel_canvas_low_latency_webgl_rounded_corners.html',
                       base_name + '_CanvasLowLatencyWebGLRoundedCorners',
-                      test_rect=[0, 0, 100, 100]),
+                      test_rect=[0, 0, 100, 100],
+                      matching_algorithm=ROUNDING_ERROR_ALGO),
         PixelTestPage('pixel_canvas_low_latency_webgl_occluded.html',
                       base_name + '_CanvasLowLatencyWebGLOccluded',
                       test_rect=[0, 0, 100, 100],
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 20522df..7e78666b 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
@@ -117,6 +117,8 @@
 crbug.com/1506258 [ asan graphite-enabled intel-0x3e9b mac no-clang-coverage release ] ExpectedColor_MediaRecorderFromVideoElement [ Failure ]
 crbug.com/1506258 [ debug graphite-enabled intel-0x3e9b mac no-asan no-clang-coverage ] ExpectedColor_MediaRecorderFromVideoElement [ Failure ]
 
+# Gardener 2024-03-18
+crbug.com/330132405 [ fuchsia fuchsia-board-qemu-x64 ] ExpectedColor_MediaRecorderFrom2DCanvas [ Failure ]
 
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
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 95ea3b2f..fd7ff5f6 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
@@ -103,6 +103,9 @@
 crbug.com/324934210 [ mac intel angle-metal passthrough asan graphite-enabled ] ScreenshotSync_GPURasterWithDivs [ RetryOnFailure ]
 crbug.com/324934210 [ mac intel angle-metal passthrough asan graphite-enabled ] ScreenshotSync_GPURasterWithCanvas [ RetryOnFailure ]
 
+# Gardener 2024-03-18
+crbug.com/330130724 [ fuchsia fuchsia-board-qemu-x64 ] ScreenshotSync_SWRasterWithDivs [ Failure ]
+crbug.com/330130724 [ fuchsia fuchsia-board-qemu-x64 ] ScreenshotSync_SWRasterWithCanvas [ Failure ]
 
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn
index 90845fd..9183fcb8 100644
--- a/crypto/BUILD.gn
+++ b/crypto/BUILD.gn
@@ -218,17 +218,12 @@
 static_library("test_support") {
   testonly = true
   sources = [
+    "scoped_fake_user_verifying_key_provider.cc",
+    "scoped_fake_user_verifying_key_provider.h",
     "scoped_mock_unexportable_key_provider.cc",
     "scoped_mock_unexportable_key_provider.h",
   ]
 
-  if (is_win || is_mac) {
-    sources += [
-      "scoped_fake_user_verifying_key_provider.cc",
-      "scoped_fake_user_verifying_key_provider.h",
-    ]
-  }
-
   if (use_nss_certs) {
     sources += [
       "scoped_test_nss_db.cc",
diff --git a/crypto/scoped_fake_user_verifying_key_provider.cc b/crypto/scoped_fake_user_verifying_key_provider.cc
index 95a0c7e8..4d93f6af 100644
--- a/crypto/scoped_fake_user_verifying_key_provider.cc
+++ b/crypto/scoped_fake_user_verifying_key_provider.cc
@@ -12,8 +12,6 @@
 #include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
-#include "base/strings/strcat.h"
-#include "crypto/random.h"
 #include "crypto/signature_verifier.h"
 #include "crypto/unexportable_key.h"
 #include "crypto/user_verifying_key.h"
@@ -22,13 +20,6 @@
 
 namespace {
 
-// Simulate the storing of UV keys by the platform.
-// TODO(enclave): This possibly can be replaced when the
-// UserVerifyingKeyProvider is modified to support vending of labels, rather
-// than the caller having to supply it. When that is implemented, this fake
-// should be able to store the wrapped software key directly in the label.
-base::flat_map<UserVerifyingKeyLabel, std::vector<uint8_t>> stored_uv_keys_;
-
 // Wraps a software `UnexportableSigningKey`.
 class FakeUserVerifyingSigningKey : public UserVerifyingSigningKey {
  public:
@@ -65,15 +56,11 @@
           acceptable_algorithms,
       base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)>
           callback) override {
-    std::vector<uint8_t> random(16);
-    crypto::RandBytes(random);
-    UserVerifyingKeyLabel key_label =
-        base::StrCat({"uvkey-", base::Base64Encode(random)});
     auto software_unexportable_key =
         GetSoftwareUnsecureUnexportableKeyProvider()->GenerateSigningKeySlowly(
             acceptable_algorithms);
-    stored_uv_keys_.insert_or_assign(
-        key_label, software_unexportable_key->GetWrappedKey());
+    UserVerifyingKeyLabel key_label =
+        base::Base64Encode(software_unexportable_key->GetWrappedKey());
     std::move(callback).Run(std::make_unique<FakeUserVerifyingSigningKey>(
         std::move(key_label), std::move(software_unexportable_key)));
   }
@@ -84,18 +71,23 @@
           callback) override {
     std::vector<SignatureVerifier::SignatureAlgorithm> algorithms = {
         SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256};
-    auto it = stored_uv_keys_.find(key_label);
-    if (it == stored_uv_keys_.end()) {
-      std::move(callback).Run(nullptr);
-      return;
-    }
+    std::optional<std::vector<uint8_t>> wrapped_key =
+        base::Base64Decode(key_label);
+    CHECK(wrapped_key);
     auto software_unexportable_key =
         GetSoftwareUnsecureUnexportableKeyProvider()
-            ->FromWrappedSigningKeySlowly(it->second);
+            ->FromWrappedSigningKeySlowly(*wrapped_key);
     CHECK(software_unexportable_key);
     std::move(callback).Run(std::make_unique<FakeUserVerifyingSigningKey>(
         std::move(key_label), std::move(software_unexportable_key)));
   }
+
+  void DeleteUserVerifyingKey(
+      UserVerifyingKeyLabel key_label,
+      base::OnceCallback<void(bool)> callback) override {
+    // The mock does not store any keys.
+    std::move(callback).Run(true);
+  }
 };
 
 std::unique_ptr<UserVerifyingKeyProvider> GetMockUserVerifyingKeyProvider() {
diff --git a/crypto/user_verifying_key.h b/crypto/user_verifying_key.h
index 19486af..5978343 100644
--- a/crypto/user_verifying_key.h
+++ b/crypto/user_verifying_key.h
@@ -21,13 +21,7 @@
 
 namespace crypto {
 
-// The type of the identifiers for user-verifying keys depends on the
-// underlying platform API.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 typedef std::string UserVerifyingKeyLabel;
-#else
-typedef std::monostate UserVerifyingKeyLabel;  // Unused.
-#endif
 
 // UserVerifyingSigningKey is a hardware-backed key that triggers a user
 // verification by the platform before a signature will be provided.
@@ -101,7 +95,7 @@
   // Similar to |GenerateSigningKeySlowly| but the resulting signing key can
   // only be used with a local user authentication by the platform. This can be
   // called from any thread as the work is done asynchronously on a
-  // low-priority thread.
+  // high-priority thread when the underlying platform is slow.
   // Invokes |callback| with the resulting key, or nullptr on error.
   virtual void GenerateUserVerifyingSigningKey(
       base::span<const SignatureVerifier::SignatureAlgorithm>
@@ -111,12 +105,21 @@
 
   // Similar to |FromWrappedSigningKey| but uses a wrapped key that was
   // generated from |GenerateUserVerifyingSigningKey|. This can be called from
-  // any thread as the work is done asynchronously on a low-priority thread.
+  // any thread as the work is done asynchronously on a high-priority thread
+  // when the underlying platform is slow.
   // Invokes |callback| with the resulting key, or nullptr on error.
   virtual void GetUserVerifyingSigningKey(
       UserVerifyingKeyLabel key_label,
       base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)>
           callback) = 0;
+
+  // Deletes a user verifying signing key. Work is be done asynchronously on a
+  // low-priority thread when the underlying platform is slow.
+  // Invokes |callback| with `true` if the key was found and deleted, `false`
+  // otherwise.
+  virtual void DeleteUserVerifyingKey(
+      UserVerifyingKeyLabel key_label,
+      base::OnceCallback<void(bool)> callback) = 0;
 };
 
 // GetUserVerifyingKeyProvider returns |UserVerifyingKeyProvider| for the
diff --git a/crypto/user_verifying_key_mac.mm b/crypto/user_verifying_key_mac.mm
index 83cd051..18bab09 100644
--- a/crypto/user_verifying_key_mac.mm
+++ b/crypto/user_verifying_key_mac.mm
@@ -144,6 +144,19 @@
         std::make_unique<UserVerifyingSigningKeyMac>(std::move(key)));
   }
 
+  void DeleteUserVerifyingKey(
+      UserVerifyingKeyLabel key_label,
+      base::OnceCallback<void(bool)> callback) override {
+    std::unique_ptr<UnexportableKeyProvider> key_provider =
+        GetUnexportableKeyProvider(MakeUnexportableKeyConfig());
+    if (!key_provider) {
+      std::move(callback).Run(false);
+      return;
+    }
+    std::vector<uint8_t> wrapped_key(key_label.begin(), key_label.end());
+    std::move(callback).Run(key_provider->DeleteSigningKey(wrapped_key));
+  }
+
  private:
   UnexportableKeyProvider::Config MakeUnexportableKeyConfig() {
     return {
diff --git a/crypto/user_verifying_key_mac_unittest.mm b/crypto/user_verifying_key_mac_unittest.mm
index b83d77b..e0c3417 100644
--- a/crypto/user_verifying_key_mac_unittest.mm
+++ b/crypto/user_verifying_key_mac_unittest.mm
@@ -31,6 +31,60 @@
 };
 
 class UserVerifyingKeyMacTest : public testing::Test {
+ public:
+  std::unique_ptr<UserVerifyingSigningKey> GenerateUserVerifyingSigningKey() {
+    std::unique_ptr<UserVerifyingSigningKey> key;
+    base::RunLoop run_loop;
+    provider_->GenerateUserVerifyingSigningKey(
+        kAcceptableAlgos,
+        base::BindLambdaForTesting(
+            [&](std::unique_ptr<UserVerifyingSigningKey> result) {
+              key = std::move(result);
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+    return key;
+  }
+
+  std::unique_ptr<UserVerifyingSigningKey> GetUserVerifyingSigningKey(
+      std::string key_label) {
+    std::unique_ptr<UserVerifyingSigningKey> key;
+    base::RunLoop run_loop;
+    provider_->GetUserVerifyingSigningKey(
+        key_label, base::BindLambdaForTesting(
+                       [&](std::unique_ptr<UserVerifyingSigningKey> result) {
+                         key = std::move(result);
+                         run_loop.Quit();
+                       }));
+    run_loop.Run();
+    return key;
+  }
+
+  bool DeleteUserVerifyingKey(std::string key_label) {
+    std::optional<bool> deleted;
+    base::RunLoop run_loop;
+    provider_->DeleteUserVerifyingKey(
+        key_label, base::BindLambdaForTesting([&](bool result) {
+          deleted = result;
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+    return *deleted;
+  }
+
+  std::optional<std::vector<uint8_t>> Sign(UserVerifyingSigningKey* key,
+                                           base::span<const uint8_t> message) {
+    std::optional<std::vector<uint8_t>> signature;
+    base::RunLoop run_loop;
+    key->Sign(message, base::BindLambdaForTesting(
+                           [&](std::optional<std::vector<uint8_t>> result) {
+                             signature = std::move(result);
+                             run_loop.Quit();
+                           }));
+    run_loop.Run();
+    return signature;
+  }
+
  protected:
   ScopedFakeAppleKeychainV2 scoped_fake_apple_keychain_{
       kTestKeychainAccessGroup};
@@ -39,40 +93,20 @@
 
   base::test::ScopedFeatureList scoped_feature_list_{
       kEnableMacUnexportableKeys};
+
+  std::unique_ptr<UserVerifyingKeyProvider> provider_ =
+      crypto::GetUserVerifyingKeyProvider(config);
 };
 
 TEST_F(UserVerifyingKeyMacTest, RoundTrip) {
-  std::unique_ptr<UserVerifyingKeyProvider> provider =
-      crypto::GetUserVerifyingKeyProvider(config);
-
-  std::unique_ptr<UserVerifyingSigningKey> key;
-
-  {
-    base::RunLoop run_loop;
-    provider->GenerateUserVerifyingSigningKey(
-        kAcceptableAlgos,
-        base::BindLambdaForTesting(
-            [&](std::unique_ptr<UserVerifyingSigningKey> result) {
-              key = std::move(result);
-              run_loop.Quit();
-            }));
-    run_loop.Run();
-  }
+  std::unique_ptr<UserVerifyingSigningKey> key =
+      GenerateUserVerifyingSigningKey();
   ASSERT_TRUE(key);
   ASSERT_TRUE(!key->GetKeyLabel().empty());
 
   const std::vector<uint8_t> spki = key->GetPublicKey();
   const uint8_t message[] = {1, 2, 3, 4};
-  std::optional<std::vector<uint8_t>> signature;
-  {
-    base::RunLoop run_loop;
-    key->Sign(message, base::BindLambdaForTesting(
-                           [&](std::optional<std::vector<uint8_t>> result) {
-                             signature = std::move(result);
-                             run_loop.Quit();
-                           }));
-    run_loop.Run();
-  }
+  std::optional<std::vector<uint8_t>> signature = Sign(key.get(), message);
   ASSERT_TRUE(signature);
 
   crypto::SignatureVerifier verifier;
@@ -80,30 +114,11 @@
   verifier.VerifyUpdate(message);
   ASSERT_TRUE(verifier.VerifyFinal());
 
-  std::unique_ptr<UserVerifyingSigningKey> key2;
-  {
-    base::RunLoop run_loop;
-    provider->GetUserVerifyingSigningKey(
-        key->GetKeyLabel(),
-        base::BindLambdaForTesting(
-            [&](std::unique_ptr<UserVerifyingSigningKey> result) {
-              key2 = std::move(result);
-              run_loop.Quit();
-            }));
-    run_loop.Run();
-  }
+  std::unique_ptr<UserVerifyingSigningKey> key2 =
+      GetUserVerifyingSigningKey(key->GetKeyLabel());
   ASSERT_TRUE(key2);
 
-  std::optional<std::vector<uint8_t>> signature2;
-  {
-    base::RunLoop run_loop;
-    key->Sign(message, base::BindLambdaForTesting(
-                           [&](std::optional<std::vector<uint8_t>> result) {
-                             signature2 = std::move(result);
-                             run_loop.Quit();
-                           }));
-    run_loop.Run();
-  }
+  std::optional<std::vector<uint8_t>> signature2 = Sign(key.get(), message);
   ASSERT_TRUE(signature2);
 
   crypto::SignatureVerifier verifier2;
@@ -128,6 +143,16 @@
   }
 }
 
+TEST_F(UserVerifyingKeyMacTest, DeleteSigningKey) {
+  std::unique_ptr<UserVerifyingSigningKey> key =
+      GenerateUserVerifyingSigningKey();
+  ASSERT_TRUE(key);
+
+  EXPECT_TRUE(DeleteUserVerifyingKey(key->GetKeyLabel()));
+  EXPECT_FALSE(GetUserVerifyingSigningKey(key->GetKeyLabel()));
+  EXPECT_FALSE(DeleteUserVerifyingKey(key->GetKeyLabel()));
+}
+
 }  // namespace
 
 }  // namespace crypto
diff --git a/crypto/user_verifying_key_win.cc b/crypto/user_verifying_key_win.cc
index c2d3e4d..2acbcd8 100644
--- a/crypto/user_verifying_key_win.cc
+++ b/crypto/user_verifying_key_win.cc
@@ -450,6 +450,13 @@
                        std::move(success_callback), std::move(error_callback)));
   }
 
+  void DeleteUserVerifyingKey(
+      UserVerifyingKeyLabel key_label,
+      base::OnceCallback<void(bool)> callback) override {
+    // TODO(crbug.com/40274370): implement.
+    std::move(callback).Run(false);
+  }
+
  private:
   void OnKeyCreationCompletionSuccess(
       std::string key_name,
diff --git a/extensions/browser/api/feedback_private/feedback_private_api.cc b/extensions/browser/api/feedback_private/feedback_private_api.cc
index 827d45e..cc05360 100644
--- a/extensions/browser/api/feedback_private/feedback_private_api.cc
+++ b/extensions/browser/api/feedback_private/feedback_private_api.cc
@@ -288,6 +288,9 @@
       info->product_id = FeedbackCommon::GetChromeOSProductId();
     }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+    if (ai_metadata.contains("from_mahi")) {
+      info->product_id = FeedbackCommon::GetMahiProductId();
+    }
   }
 
   return info;
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index ea54ecd..2a17c29 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -32,7 +32,6 @@
     checkedStateChanged,
     checkedStateDescriptionChanged,
     childrenChanged,
-    classNameChanged,
     clicked,
     collapsed,
     controlsChanged,
diff --git a/gpu/command_buffer/client/client_shared_image.cc b/gpu/command_buffer/client/client_shared_image.cc
index d438573..3330310 100644
--- a/gpu/command_buffer/client/client_shared_image.cc
+++ b/gpu/command_buffer/client/client_shared_image.cc
@@ -248,9 +248,13 @@
 
 scoped_refptr<ClientSharedImage> ClientSharedImage::ImportUnowned(
     const ExportedSharedImage& exported_shared_image) {
+  // TODO(crbug.com/41494843): Plumb information through ExportedSharedImage to
+  // ensure that the ClientSharedImage created here computes the same texture
+  // target via GetTextureTarget() as the source ClientSharedImage from which
+  // the ExportedSharedImage was created.
   return base::MakeRefCounted<ClientSharedImage>(
       exported_shared_image.mailbox_, exported_shared_image.metadata_,
-      exported_shared_image.sync_token_, nullptr);
+      exported_shared_image.sync_token_, nullptr, gfx::EMPTY_BUFFER);
 }
 
 ExportedSharedImage::ExportedSharedImage(const Mailbox& mailbox,
diff --git a/gpu/command_buffer/client/client_shared_image.h b/gpu/command_buffer/client/client_shared_image.h
index a9d740a..8ca55d6 100644
--- a/gpu/command_buffer/client/client_shared_image.h
+++ b/gpu/command_buffer/client/client_shared_image.h
@@ -172,7 +172,7 @@
   static scoped_refptr<ClientSharedImage> CreateForTesting() {
     return base::MakeRefCounted<ClientSharedImage>(
         Mailbox::GenerateForSharedImage(), SharedImageMetadata(),
-        gpu::SyncToken(), nullptr);
+        gpu::SyncToken(), nullptr, gfx::EMPTY_BUFFER);
   }
 
   static scoped_refptr<ClientSharedImage> CreateForTesting(
@@ -182,7 +182,8 @@
       std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
       scoped_refptr<SharedImageInterfaceHolder> sii_holder) {
     auto client_si = base::MakeRefCounted<ClientSharedImage>(
-        mailbox, metadata, sync_token, sii_holder);
+        mailbox, metadata, sync_token, sii_holder,
+        gpu_memory_buffer->GetType());
     client_si->gpu_memory_buffer_ = std::move(gpu_memory_buffer);
     return client_si;
   }
diff --git a/gpu/command_buffer/service/dawn_caching_interface.cc b/gpu/command_buffer/service/dawn_caching_interface.cc
index 213c69a..663e11bb 100644
--- a/gpu/command_buffer/service/dawn_caching_interface.cc
+++ b/gpu/command_buffer/service/dawn_caching_interface.cc
@@ -62,7 +62,8 @@
 DawnCachingInterfaceFactory::CreateInstance(
     const gpu::GpuDiskCacheHandle& handle,
     DawnCachingInterface::CacheBlobCallback callback) {
-  DCHECK(gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnWebGPU);
+  DCHECK(gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnWebGPU ||
+         gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnGraphite);
 
   if (const auto it = backends_.find(handle); it != backends_.end()) {
     return base::WrapUnique(
@@ -84,7 +85,9 @@
 
 void DawnCachingInterfaceFactory::ReleaseHandle(
     const gpu::GpuDiskCacheHandle& handle) {
-  DCHECK(gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnWebGPU);
+  DCHECK(gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnWebGPU ||
+         gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnGraphite);
+
   backends_.erase(handle);
 }
 
diff --git a/gpu/command_buffer/service/image_reader_gl_owner.cc b/gpu/command_buffer/service/image_reader_gl_owner.cc
index 0dc528b..eb4286dc 100644
--- a/gpu/command_buffer/service/image_reader_gl_owner.cc
+++ b/gpu/command_buffer/service/image_reader_gl_owner.cc
@@ -131,13 +131,15 @@
     std::unique_ptr<AbstractTextureAndroid> texture,
     Mode mode,
     scoped_refptr<SharedContextState> context_state,
-    scoped_refptr<RefCountedLock> drdc_lock)
+    scoped_refptr<RefCountedLock> drdc_lock,
+    TextureOwnerCodecType type_for_metrics)
     : TextureOwner(false /* binds_texture_on_image_update */,
                    std::move(texture),
                    std::move(context_state)),
       RefCountedLockHelperDrDc(std::move(drdc_lock)),
       context_(gl::GLContext::GetCurrent()),
-      surface_(gl::GLSurface::GetCurrent()) {
+      surface_(gl::GLSurface::GetCurrent()),
+      type_for_metrics_(type_for_metrics) {
   DCHECK(context_);
   DCHECK(surface_);
 
@@ -300,6 +302,9 @@
   base::UmaHistogramSparse("Media.AImageReaderGLOwner.AcquireImageResult",
                            return_code);
 
+  UMA_HISTOGRAM_ENUMERATION("Media.AImageReaderGLOwner.CodecType",
+                            type_for_metrics_);
+
   // TODO(http://crbug.com/846050).
   // Need to add some better error handling if below error occurs. Currently we
   // just return if error occurs.
diff --git a/gpu/command_buffer/service/image_reader_gl_owner.h b/gpu/command_buffer/service/image_reader_gl_owner.h
index f348923..8b80d80b 100644
--- a/gpu/command_buffer/service/image_reader_gl_owner.h
+++ b/gpu/command_buffer/service/image_reader_gl_owner.h
@@ -94,7 +94,8 @@
   ImageReaderGLOwner(std::unique_ptr<AbstractTextureAndroid> texture,
                      Mode secure_mode,
                      scoped_refptr<SharedContextState> context_state,
-                     scoped_refptr<RefCountedLock> drdc_lock);
+                     scoped_refptr<RefCountedLock> drdc_lock,
+                     TextureOwnerCodecType type_for_metrics);
   ~ImageReaderGLOwner() override;
 
   // Registers and releases a ref on the image. Once the ref-count for an image
@@ -159,6 +160,8 @@
 
   // This class is created on gpu main thread.
   THREAD_CHECKER(gpu_main_thread_checker_);
+
+  const TextureOwnerCodecType type_for_metrics_;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc b/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc
index a054539..c1cb59f3 100644
--- a/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc
+++ b/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc
@@ -74,7 +74,8 @@
         std::move(texture), SecureMode(), std::move(context_state),
         features::NeedThreadSafeAndroidMedia()
             ? base::MakeRefCounted<gpu::RefCountedLockForTest>()
-            : nullptr);
+            : nullptr,
+        TextureOwnerCodecType::kMediaCodec);
   }
 
   virtual TextureOwner::Mode SecureMode() {
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 17645db..9ca0f3cf 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
@@ -265,7 +265,7 @@
 
 bool DXGISwapChainImageBacking::Present(
     bool should_synchronize_present_with_vblank) {
-  if (!pending_swap_rect_.has_value()) {
+  if (!pending_swap_rect_.has_value() || pending_swap_rect_.value().IsEmpty()) {
     DVLOG(1) << "Skipping present without an update rect";
     return true;
   }
diff --git a/gpu/command_buffer/service/texture_owner.cc b/gpu/command_buffer/service/texture_owner.cc
index 7dc0597..d67f52e 100644
--- a/gpu/command_buffer/service/texture_owner.cc
+++ b/gpu/command_buffer/service/texture_owner.cc
@@ -97,7 +97,8 @@
 scoped_refptr<TextureOwner> TextureOwner::Create(
     Mode mode,
     scoped_refptr<SharedContextState> context_state,
-    scoped_refptr<RefCountedLock> drdc_lock) {
+    scoped_refptr<RefCountedLock> drdc_lock,
+    TextureOwnerCodecType type_for_metrics) {
   auto texture = CreateTexture(context_state.get());
   switch (mode) {
     case Mode::kAImageReaderInsecure:
@@ -105,7 +106,7 @@
     case Mode::kAImageReaderSecureSurfaceControl:
       return new ImageReaderGLOwner(std::move(texture), mode,
                                     std::move(context_state),
-                                    std::move(drdc_lock));
+                                    std::move(drdc_lock), type_for_metrics);
     case Mode::kSurfaceTextureInsecure:
       DCHECK(!drdc_lock);
       return new SurfaceTextureGLOwner(std::move(texture),
diff --git a/gpu/command_buffer/service/texture_owner.h b/gpu/command_buffer/service/texture_owner.h
index 9591d7e..3794551 100644
--- a/gpu/command_buffer/service/texture_owner.h
+++ b/gpu/command_buffer/service/texture_owner.h
@@ -29,6 +29,16 @@
 class AbstractTextureAndroid;
 class TextureBase;
 
+// Used for diagnosting metrics. Do not use for anything else.
+// TODO(crbug.com/329821776): Remove once we get enough data.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum TextureOwnerCodecType {
+  kMediaCodec = 0,
+  kStreamTexture = 1,
+  kMaxValue = kStreamTexture
+};
+
 // A Texture wrapper interface that creates and maintains ownership of the
 // attached GL or Vulkan texture. The texture is destroyed with the object.
 // It should only be accessed on the thread it was created on, with the
@@ -60,7 +70,8 @@
   static scoped_refptr<TextureOwner> Create(
       Mode mode,
       scoped_refptr<SharedContextState> context_state,
-      scoped_refptr<RefCountedLock> drdc_lock);
+      scoped_refptr<RefCountedLock> drdc_lock,
+      TextureOwnerCodecType type_for_metrics);
 
   TextureOwner(const TextureOwner&) = delete;
   TextureOwner& operator=(const TextureOwner&) = delete;
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 062e04eb..f2dad8ea 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1084,7 +1084,8 @@
       memory_transfer_service_(new DawnServiceMemoryTransferService(this)),
       wire_serializer_(new DawnServiceSerializer(client)),
       isolation_key_provider_(isolation_key_provider) {
-  if (gpu_preferences.enable_webgpu_experimental_features) {
+  if (gpu_preferences.enable_webgpu_developer_features ||
+      gpu_preferences.enable_webgpu_experimental_features) {
     safety_level_ = webgpu::SafetyLevel::kSafeExperimental;
   }
   if (gpu_preferences.enable_unsafe_webgpu) {
@@ -1203,6 +1204,8 @@
     case wgpu::FeatureName::ChromiumExperimentalSubgroups:
     case wgpu::FeatureName::ChromiumExperimentalSubgroupUniformControlFlow:
       return safety_level_ == webgpu::SafetyLevel::kUnsafe;
+    case wgpu::FeatureName::AdapterPropertiesD3D:
+    case wgpu::FeatureName::AdapterPropertiesVk:
     case wgpu::FeatureName::AdapterPropertiesMemoryHeaps:
       return safety_level_ == webgpu::SafetyLevel::kUnsafe ||
              safety_level_ == webgpu::SafetyLevel::kSafeExperimental;
diff --git a/gpu/ipc/common/BUILD.gn b/gpu/ipc/common/BUILD.gn
index 1331908f..171873a 100644
--- a/gpu/ipc/common/BUILD.gn
+++ b/gpu/ipc/common/BUILD.gn
@@ -579,6 +579,11 @@
           cpp = "::gpu::GpuDiskCacheDawnWebGPUHandle"
           nullable_is_same_type = true
         },
+        {
+          mojom = "gpu.mojom.GpuDiskCacheDawnGraphiteHandle"
+          cpp = "::gpu::GpuDiskCacheDawnGraphiteHandle"
+          nullable_is_same_type = true
+        },
 
         # General types that are the ones generally exposed in interfaces.
         {
diff --git a/gpu/ipc/common/gpu_disk_cache_type.cc b/gpu/ipc/common/gpu_disk_cache_type.cc
index db9e9f0..2424ba9 100644
--- a/gpu/ipc/common/gpu_disk_cache_type.cc
+++ b/gpu/ipc/common/gpu_disk_cache_type.cc
@@ -16,6 +16,9 @@
     case GpuDiskCacheType::kDawnWebGPU:
       s << "gpu::GpuDiskCacheType::kDawnWebGPU";
       break;
+    case GpuDiskCacheType::kDawnGraphite:
+      s << "gpu::GpuDiskCacheType::kDawnGraphite";
+      break;
   }
   return s;
 }
@@ -28,6 +31,9 @@
     case GpuDiskCacheType::kDawnWebGPU:
       s << "DawnWebGPUHandle(" << GetHandleValue(handle) << ")";
       break;
+    case GpuDiskCacheType::kDawnGraphite:
+      s << "DawnGraphiteHandle(" << GetHandleValue(handle) << ")";
+      break;
   }
   return s;
 }
@@ -37,7 +43,9 @@
     case GpuDiskCacheType::kGlShaders:
       return FILE_PATH_LITERAL("GPUCache");
     case GpuDiskCacheType::kDawnWebGPU:
-      return FILE_PATH_LITERAL("DawnCache");
+      return FILE_PATH_LITERAL("DawnWebGPUCache");
+    case GpuDiskCacheType::kDawnGraphite:
+      return FILE_PATH_LITERAL("DawnGraphiteCache");
   }
   NOTREACHED();
   return FILE_PATH_LITERAL("");
@@ -46,15 +54,21 @@
 GpuDiskCacheType GetHandleType(const GpuDiskCacheHandle& handle) {
   if (absl::holds_alternative<gpu::GpuDiskCacheGlShaderHandle>(handle))
     return GpuDiskCacheType::kGlShaders;
-  DCHECK(absl::holds_alternative<gpu::GpuDiskCacheDawnWebGPUHandle>(handle));
-  return GpuDiskCacheType::kDawnWebGPU;
+  if (absl::holds_alternative<gpu::GpuDiskCacheDawnWebGPUHandle>(handle)) {
+    return GpuDiskCacheType::kDawnWebGPU;
+  }
+  DCHECK(absl::holds_alternative<gpu::GpuDiskCacheDawnGraphiteHandle>(handle));
+  return GpuDiskCacheType::kDawnGraphite;
 }
 
 int32_t GetHandleValue(const GpuDiskCacheHandle& handle) {
   if (absl::holds_alternative<gpu::GpuDiskCacheGlShaderHandle>(handle))
     return absl::get<gpu::GpuDiskCacheGlShaderHandle>(handle).value();
-  DCHECK(absl::holds_alternative<gpu::GpuDiskCacheDawnWebGPUHandle>(handle));
-  return absl::get<gpu::GpuDiskCacheDawnWebGPUHandle>(handle).value();
+  if (absl::holds_alternative<gpu::GpuDiskCacheDawnWebGPUHandle>(handle)) {
+    return absl::get<gpu::GpuDiskCacheDawnWebGPUHandle>(handle).value();
+  }
+  DCHECK(absl::holds_alternative<gpu::GpuDiskCacheDawnGraphiteHandle>(handle));
+  return absl::get<gpu::GpuDiskCacheDawnGraphiteHandle>(handle).value();
 }
 
 bool IsReservedGpuDiskCacheHandle(const GpuDiskCacheHandle& handle) {
@@ -64,9 +78,12 @@
     return gl_shader_handle == kDisplayCompositorGpuDiskCacheHandle ||
            gl_shader_handle == kGrShaderGpuDiskCacheHandle;
   }
-  const auto& dawn_webgpu_handle =
-      absl::get<gpu::GpuDiskCacheDawnWebGPUHandle>(handle);
-  return dawn_webgpu_handle == kGraphiteDawnGpuDiskCacheHandle;
+  if (absl::holds_alternative<gpu::GpuDiskCacheDawnGraphiteHandle>(handle)) {
+    const auto& dawn_graphite_handle =
+        absl::get<gpu::GpuDiskCacheDawnGraphiteHandle>(handle);
+    return dawn_graphite_handle == kGraphiteDawnGpuDiskCacheHandle;
+  }
+  return false;
 }
 
 }  // namespace gpu
diff --git a/gpu/ipc/common/gpu_disk_cache_type.h b/gpu/ipc/common/gpu_disk_cache_type.h
index 5b08546e..e435c5e 100644
--- a/gpu/ipc/common/gpu_disk_cache_type.h
+++ b/gpu/ipc/common/gpu_disk_cache_type.h
@@ -27,15 +27,17 @@
 enum class GpuDiskCacheType {
   kGlShaders,
   kDawnWebGPU,
+  kDawnGraphite,
 };
 
 // Stream operator implemented for GpuDiskCacheType for debugging.
 GPU_EXPORT std::ostream& operator<<(std::ostream& s,
                                     const GpuDiskCacheType& type);
 
-static constexpr std::array<GpuDiskCacheType, 2> kGpuDiskCacheTypes = {
+static constexpr std::array<GpuDiskCacheType, 3> kGpuDiskCacheTypes = {
     GpuDiskCacheType::kGlShaders,
     GpuDiskCacheType::kDawnWebGPU,
+    GpuDiskCacheType::kDawnGraphite,
 };
 GPU_EXPORT base::FilePath::StringType GetGpuDiskCacheSubdir(
     GpuDiskCacheType type);
@@ -54,19 +56,28 @@
                  std::numeric_limits<int32_t>::min(),
                  1>;
 
-// Dawn cache handles (for the most part, these should be 1:1 per profile).
+// Dawn WebGPU cache handles (for the most part, these should be 1:1 per
+// profile).
 using GpuDiskCacheDawnWebGPUHandle =
     base::IdType<class GpuDiskCacheDawnWebGPU,
                  int32_t,
                  std::numeric_limits<int32_t>::min(),
                  1>;
 
+// Dawn Graphite cache handles (for the most part, these should be 1:1 per
+// profile).
+using GpuDiskCacheDawnGraphiteHandle =
+    base::IdType<class GpuDiskCacheDawnGraphite,
+                 int32_t,
+                 std::numeric_limits<int32_t>::min(),
+                 1>;
 //
 // Variant handle that encompasses all possible handles, and utilities.
 //
 using GpuDiskCacheHandle = absl::variant<absl::monostate,
                                          GpuDiskCacheGlShaderHandle,
-                                         GpuDiskCacheDawnWebGPUHandle>;
+                                         GpuDiskCacheDawnWebGPUHandle,
+                                         GpuDiskCacheDawnGraphiteHandle>;
 GPU_EXPORT GpuDiskCacheType GetHandleType(const GpuDiskCacheHandle& handle);
 GPU_EXPORT int32_t GetHandleValue(const GpuDiskCacheHandle& handle);
 
@@ -86,7 +97,7 @@
 
 // The handle used by GraphiteDawn running in the GPU process. It is used by
 // RasterDecoder and SkiaRenderer.
-constexpr GpuDiskCacheDawnWebGPUHandle kGraphiteDawnGpuDiskCacheHandle(-3);
+constexpr GpuDiskCacheDawnGraphiteHandle kGraphiteDawnGpuDiskCacheHandle(-3);
 
 GPU_EXPORT bool IsReservedGpuDiskCacheHandle(const GpuDiskCacheHandle& handle);
 
diff --git a/gpu/ipc/common/gpu_disk_cache_type.mojom b/gpu/ipc/common/gpu_disk_cache_type.mojom
index d7eed5ec..a2dffc4b 100644
--- a/gpu/ipc/common/gpu_disk_cache_type.mojom
+++ b/gpu/ipc/common/gpu_disk_cache_type.mojom
@@ -8,6 +8,7 @@
 enum GpuDiskCacheType {
   kGlShaders,
   kDawnWebGPU,
+  kDawnGraphite,
 };
 
 //
@@ -24,6 +25,11 @@
   int32 value;
 };
 
+// Corresponds to C++ type gpu::GpuDiskCacheDawnGraphiteHandle
+struct GpuDiskCacheDawnGraphiteHandle {
+  int32 value;
+};
+
 //
 // Union handle
 //
@@ -32,4 +38,5 @@
 union GpuDiskCacheHandle {
   GpuDiskCacheGlShaderHandle gl_shader_handle;
   GpuDiskCacheDawnWebGPUHandle dawn_webgpu_handle;
+  GpuDiskCacheDawnGraphiteHandle dawn_graphite_handle;
 };
\ No newline at end of file
diff --git a/gpu/ipc/common/gpu_disk_cache_type_mojom_traits.cc b/gpu/ipc/common/gpu_disk_cache_type_mojom_traits.cc
index 4ebae499..8e554d2b 100644
--- a/gpu/ipc/common/gpu_disk_cache_type_mojom_traits.cc
+++ b/gpu/ipc/common/gpu_disk_cache_type_mojom_traits.cc
@@ -18,6 +18,8 @@
       return gpu::mojom::GpuDiskCacheType::kGlShaders;
     case gpu::GpuDiskCacheType::kDawnWebGPU:
       return gpu::mojom::GpuDiskCacheType::kDawnWebGPU;
+    case gpu::GpuDiskCacheType::kDawnGraphite:
+      return gpu::mojom::GpuDiskCacheType::kDawnGraphite;
   }
 
   NOTREACHED() << "Invalid gpu::GpuDiskCacheType: " << gpu_disk_cache_type;
@@ -35,6 +37,9 @@
     case gpu::mojom::GpuDiskCacheType::kDawnWebGPU:
       *out = gpu::GpuDiskCacheType::kDawnWebGPU;
       return true;
+    case gpu::mojom::GpuDiskCacheType::kDawnGraphite:
+      *out = gpu::GpuDiskCacheType::kDawnGraphite;
+      return true;
     default:
       break;
   }
@@ -74,6 +79,12 @@
       *output = handle;
       return ret;
     }
+    case Tag::kDawnGraphiteHandle: {
+      gpu::GpuDiskCacheDawnGraphiteHandle handle;
+      bool ret = input.ReadDawnGraphiteHandle(&handle);
+      *output = handle;
+      return ret;
+    }
   }
   return false;
 }
@@ -85,8 +96,11 @@
   using Tag = gpu::mojom::GpuDiskCacheHandleDataView::Tag;
   if (absl::holds_alternative<gpu::GpuDiskCacheGlShaderHandle>(handle))
     return Tag::kGlShaderHandle;
-  DCHECK(absl::holds_alternative<gpu::GpuDiskCacheDawnWebGPUHandle>(handle));
-  return Tag::kDawnWebgpuHandle;
+  if (absl::holds_alternative<gpu::GpuDiskCacheDawnWebGPUHandle>(handle)) {
+    return Tag::kDawnWebgpuHandle;
+  }
+  DCHECK(absl::holds_alternative<gpu::GpuDiskCacheDawnGraphiteHandle>(handle));
+  return Tag::kDawnGraphiteHandle;
 }
 
 // static
@@ -103,4 +117,11 @@
   return absl::get<gpu::GpuDiskCacheDawnWebGPUHandle>(handle);
 }
 
+// static
+gpu::GpuDiskCacheDawnGraphiteHandle
+UnionTraits<gpu::mojom::GpuDiskCacheHandleDataView, gpu::GpuDiskCacheHandle>::
+    dawn_graphite_handle(const gpu::GpuDiskCacheHandle& handle) {
+  return absl::get<gpu::GpuDiskCacheDawnGraphiteHandle>(handle);
+}
+
 }  // namespace mojo
diff --git a/gpu/ipc/common/gpu_disk_cache_type_mojom_traits.h b/gpu/ipc/common/gpu_disk_cache_type_mojom_traits.h
index 1711e35..5fcb88a 100644
--- a/gpu/ipc/common/gpu_disk_cache_type_mojom_traits.h
+++ b/gpu/ipc/common/gpu_disk_cache_type_mojom_traits.h
@@ -52,6 +52,13 @@
           gpu::GpuDiskCacheDawnWebGPUHandle> {};
 
 template <>
+struct StructTraits<gpu::mojom::GpuDiskCacheDawnGraphiteHandleDataView,
+                    gpu::GpuDiskCacheDawnGraphiteHandle>
+    : public internal::GpuDiskCacheHandleMojomTraitsHelper<
+          gpu::mojom::GpuDiskCacheDawnGraphiteHandleDataView,
+          gpu::GpuDiskCacheDawnGraphiteHandle> {};
+
+template <>
 struct GPU_EXPORT UnionTraits<gpu::mojom::GpuDiskCacheHandleDataView,
                               gpu::GpuDiskCacheHandle> {
   static bool IsNull(const gpu::GpuDiskCacheHandle& handle);
@@ -65,6 +72,8 @@
       const gpu::GpuDiskCacheHandle& handle);
   static gpu::GpuDiskCacheDawnWebGPUHandle dawn_webgpu_handle(
       const gpu::GpuDiskCacheHandle& handle);
+  static gpu::GpuDiskCacheDawnGraphiteHandle dawn_graphite_handle(
+      const gpu::GpuDiskCacheHandle& handle);
 };
 
 }  // namespace mojo
diff --git a/gpu/ipc/host/gpu_disk_cache.cc b/gpu/ipc/host/gpu_disk_cache.cc
index 1deaca0..f89b41b1 100644
--- a/gpu/ipc/host/gpu_disk_cache.cc
+++ b/gpu/ipc/host/gpu_disk_cache.cc
@@ -471,6 +471,9 @@
     case GpuDiskCacheType::kDawnWebGPU:
       handle = GpuDiskCacheDawnWebGPUHandle(raw_handle);
       break;
+    case GpuDiskCacheType::kDawnGraphite:
+      handle = GpuDiskCacheDawnGraphiteHandle(raw_handle);
+      break;
   }
   handle_to_path_map_[handle] = path;
   path_to_handle_map_[path] = handle;
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index 3149cc0..91af876 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -579,7 +579,8 @@
       // different handles).
       break;
     }
-    case gpu::GpuDiskCacheType::kDawnWebGPU: {
+    case gpu::GpuDiskCacheType::kDawnWebGPU:
+    case gpu::GpuDiskCacheType::kDawnGraphite: {
 #if BUILDFLAG(USE_DAWN)
       dawn_caching_interface_factory()->ReleaseHandle(handle);
 #endif
@@ -614,7 +615,8 @@
         program_cache()->LoadProgram(key, data);
       break;
     }
-    case gpu::GpuDiskCacheType::kDawnWebGPU: {
+    case gpu::GpuDiskCacheType::kDawnWebGPU:
+    case gpu::GpuDiskCacheType::kDawnGraphite: {
 #if BUILDFLAG(USE_DAWN) || BUILDFLAG(SKIA_USE_DAWN)
       std::unique_ptr<gpu::webgpu::DawnCachingInterface>
           dawn_caching_interface =
diff --git a/gpu/ipc/service/shared_image_stub.cc b/gpu/ipc/service/shared_image_stub.cc
index 3f4d5db4..09918375 100644
--- a/gpu/ipc/service/shared_image_stub.cc
+++ b/gpu/ipc/service/shared_image_stub.cc
@@ -509,6 +509,7 @@
 #if BUILDFLAG(IS_WIN)
 void SharedImageStub::OnCopyToGpuMemoryBuffer(const Mailbox& mailbox,
                                               uint32_t release_id) {
+  TRACE_EVENT0("gpu", "SharedImageStub::OnCopyToGpuMemoryBuffer");
   if (!mailbox.IsSharedImage()) {
     DLOG(ERROR) << "SharedImageStub: Trying to access a SharedImage with a "
                    "non-SharedImage mailbox.";
@@ -584,6 +585,7 @@
 void SharedImageStub::OnRegisterDxgiFence(const Mailbox& mailbox,
                                           gfx::DXGIHandleToken dxgi_token,
                                           gfx::GpuFenceHandle fence_handle) {
+  TRACE_EVENT0("gpu", "SharedImageStub::OnRegisterDxgiFence");
   if (!mailbox.IsSharedImage()) {
     LOG(ERROR)
         << "SharedImageStub: Trying to register a fence handle in SharedImage "
@@ -616,6 +618,7 @@
 void SharedImageStub::OnUpdateDxgiFence(const Mailbox& mailbox,
                                         gfx::DXGIHandleToken dxgi_token,
                                         uint64_t fence_value) {
+  TRACE_EVENT0("gpu", "SharedImageStub::OnUpdateDxgiFence");
   if (!mailbox.IsSharedImage()) {
     LOG(ERROR)
         << "SharedImageStub: Trying to register a fence handle in SharedImage "
@@ -657,6 +660,7 @@
 
 void SharedImageStub::OnUnregisterDxgiFence(const Mailbox& mailbox,
                                             gfx::DXGIHandleToken dxgi_token) {
+  TRACE_EVENT0("gpu", "SharedImageStub::OnUnregisterDxgiFence");
   auto mailbox_fences_it = registered_dxgi_fences_.find(mailbox);
   if (mailbox_fences_it == registered_dxgi_fences_.end()) {
     LOG(ERROR) << "Trying to unregister a fence on shared image with no "
diff --git a/gpu/ipc/service/stream_texture_android.cc b/gpu/ipc/service/stream_texture_android.cc
index d4b46b8..1c05285 100644
--- a/gpu/ipc/service/stream_texture_android.cc
+++ b/gpu/ipc/service/stream_texture_android.cc
@@ -101,9 +101,11 @@
     mojo::PendingAssociatedReceiver<mojom::StreamTexture> receiver,
     scoped_refptr<SharedContextState> context_state)
     : RefCountedLockHelperDrDc(CreateDrDcLockIfNeeded()),
-      texture_owner_(TextureOwner::Create(GetTextureOwnerMode(),
-                                          context_state,
-                                          GetDrDcLock())),
+      texture_owner_(
+          TextureOwner::Create(GetTextureOwnerMode(),
+                               context_state,
+                               GetDrDcLock(),
+                               TextureOwnerCodecType::kStreamTexture)),
       has_pending_frame_(false),
       channel_(channel),
       route_id_(route_id),
diff --git a/gpu/vulkan/vulkan_util.cc b/gpu/vulkan/vulkan_util.cc
index 972fdc1d..abe59a2 100644
--- a/gpu/vulkan/vulkan_util.cc
+++ b/gpu/vulkan/vulkan_util.cc
@@ -176,8 +176,7 @@
   return false;
 }
 
-bool IsVulkanV2Enabled(const GPUInfo& gpu_info,
-                       base::StringPiece experiment_arm) {
+bool IsVulkanV2Allowed() {
   const auto* build_info = base::android::BuildInfo::GetInstance();
   // We require at least android T deqp test to pass for v2.
   constexpr int32_t kVulkanDEQPAndroidT = 0x07E60301;
@@ -185,6 +184,15 @@
     return false;
   }
 
+  return true;
+}
+
+bool IsVulkanV2Enabled(const GPUInfo& gpu_info,
+                       base::StringPiece experiment_arm) {
+  if (!IsVulkanV2Allowed()) {
+    return false;
+  }
+
   if (!base::FeatureList::IsEnabled(kVulkanV2)) {
     return false;
   }
@@ -263,6 +271,13 @@
 bool IsVulkanV3EnabledForAdreno(
     const GPUInfo& gpu_info,
     const VulkanPhysicalDeviceProperties& device_properties) {
+  // If IsVulkanV2Allowed(), this device is part of VulkanV2 finch and we should
+  // not make decision again. This is to prevent VulkanV2 control group to get
+  // Vulkan enabled by getting into VulkanV3 enabled group.
+  if (IsVulkanV2Allowed()) {
+    return false;
+  }
+
   std::vector<const char*> slow_gpus_for_v3 = {
       "Adreno (TM) 2??",
       "Adreno (TM) 3??",
diff --git a/headless/lib/browser/DEPS b/headless/lib/browser/DEPS
index 676ddd2..69604a59 100644
--- a/headless/lib/browser/DEPS
+++ b/headless/lib/browser/DEPS
@@ -32,7 +32,7 @@
     "+device/bluetooth",
   ],
   "headless_browser_impl.*": [
-    "+services/device/public/cpp/geolocation/system_geolocation_source_mac.h",
+    "+services/device/public/cpp/geolocation/system_geolocation_source_apple.h",
     "+services/device/public/cpp/geolocation/geolocation_system_permission_manager.h",
     "+components/policy",
     "+components/prefs",
diff --git a/headless/lib/browser/headless_browser_impl_mac.mm b/headless/lib/browser/headless_browser_impl_mac.mm
index 29018f7..4acb7446 100644
--- a/headless/lib/browser/headless_browser_impl_mac.mm
+++ b/headless/lib/browser/headless_browser_impl_mac.mm
@@ -12,7 +12,7 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
-#include "services/device/public/cpp/geolocation/system_geolocation_source_mac.h"
+#include "services/device/public/cpp/geolocation/system_geolocation_source_apple.h"
 #import "ui/base/cocoa/base_view.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
 
@@ -71,8 +71,8 @@
 void HeadlessBrowserImpl::PlatformInitialize() {
   if (!geolocation_system_permission_manager_) {
     geolocation_system_permission_manager_ =
-        device::SystemGeolocationSourceMac::
-            CreateGeolocationSystemPermissionManagerOnMac();
+        device::SystemGeolocationSourceApple::
+            CreateGeolocationSystemPermissionManager();
   }
   screen_ = std::make_unique<display::ScopedNativeScreen>();
   HeadlessPopUpMethods::Init();
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index 7ed04c46..b2cf259 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -307,16 +307,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 124.0.6365.0',
+    'description': 'Run with ash-chrome version 124.0.6366.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v124.0.6365.0',
-          'revision': 'version:124.0.6365.0',
+          'location': 'lacros_version_skew_tests_v124.0.6366.0',
+          'revision': 'version:124.0.6366.0',
         },
       ],
     },
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json
index 3b59918a..f3c6316 100644
--- a/infra/config/targets/lacros-version-skew-variants.json
+++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -1,16 +1,16 @@
 {
   "LACROS_VERSION_SKEW_CANARY": {
     "args": [
-      "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+      "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
     ],
-    "description": "Run with ash-chrome version 124.0.6365.0",
+    "description": "Run with ash-chrome version 124.0.6366.0",
     "identifier": "Lacros version skew testing ash canary",
     "swarming": {
       "cipd_packages": [
         {
           "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-          "location": "lacros_version_skew_tests_v124.0.6365.0",
-          "revision": "version:124.0.6365.0"
+          "location": "lacros_version_skew_tests_v124.0.6366.0",
+          "revision": "version:124.0.6366.0"
         }
       ]
     }
diff --git a/internal b/internal
index b022b4a..1a7212d 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit b022b4a9b353aa1edd9fa7e4e752f719c6a39929
+Subproject commit 1a7212d3a146d3b55a8d03acbe71ffb5f738b0cb
diff --git a/ios/chrome/browser/autofill/model/authentication/BUILD.gn b/ios/chrome/browser/autofill/model/authentication/BUILD.gn
new file mode 100644
index 0000000..65f4e31
--- /dev/null
+++ b/ios/chrome/browser/autofill/model/authentication/BUILD.gn
@@ -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.
+
+group("unit_tests") {
+  testonly = true
+  deps = [ ":card_unmask_challenge_option_ios_unittest" ]
+}
+
+source_set("card_unmask_challenge_option_ios") {
+  sources = [
+    "card_unmask_challenge_option_ios.h",
+    "card_unmask_challenge_option_ios.mm",
+  ]
+  public_deps = [
+    "//base",
+    "//components/autofill/core/browser/payments:card_unmask_challenge_option",
+  ]
+}
+
+source_set("card_unmask_challenge_option_ios_unittest") {
+  testonly = true
+  sources = [ "card_unmask_challenge_option_ios_unittest.mm" ]
+  deps = [
+    ":card_unmask_challenge_option_ios",
+    "//testing/gtest",
+  ]
+}
diff --git a/ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios.h b/ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios.h
new file mode 100644
index 0000000..7a5be4c
--- /dev/null
+++ b/ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios.h
@@ -0,0 +1,40 @@
+// 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_MODEL_AUTHENTICATION_CARD_UNMASK_CHALLENGE_OPTION_IOS_H_
+#define IOS_CHROME_BROWSER_AUTOFILL_MODEL_AUTHENTICATION_CARD_UNMASK_CHALLENGE_OPTION_IOS_H_
+
+#import "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
+
+#import <Foundation/Foundation.h>
+
+// Represents a challenge option on the authentication selection prompt.
+@interface CardUnmaskChallengeOptionIOS : NSObject
+
+// The identifier of the option.
+@property(nonatomic, readonly)
+    autofill::CardUnmaskChallengeOption::ChallengeOptionId id;
+
+// The option's title.
+@property(nonatomic, readonly) NSString* modeLabel;
+
+// Optional additional information regarding this challenge option, for example,
+// the truncated phone number on an SMS challenge option.
+@property(nonatomic, readonly) NSString* challengeInfo;
+
+// Creates the Challenge option given all its properties.
+- (instancetype)initWithId:
+                    (autofill::CardUnmaskChallengeOption::ChallengeOptionId)id
+                 modeLabel:(NSString*)modeLabel
+             challengeInfo:(NSString*)challengInfo;
+
+// Convert this option from the autofill C++ equivalent,
+// CardUnmaskChallengeOption.
++ (instancetype)convertFrom:
+                    (const autofill::CardUnmaskChallengeOption&)autofillOption
+                  modeLabel:(const std::u16string&)modeLabel;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_AUTOFILL_MODEL_AUTHENTICATION_CARD_UNMASK_CHALLENGE_OPTION_IOS_H_
diff --git a/ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios.mm b/ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios.mm
new file mode 100644
index 0000000..ed90f8d
--- /dev/null
+++ b/ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios.mm
@@ -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.
+
+#import "ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios.h"
+
+#import "base/hash/hash.h"
+#import "base/strings/sys_string_conversions.h"
+
+@implementation CardUnmaskChallengeOptionIOS
+
+- (instancetype)initWithId:
+                    (autofill::CardUnmaskChallengeOption::ChallengeOptionId)id
+                 modeLabel:(NSString*)modeLabel
+             challengeInfo:(NSString*)challengeInfo {
+  self = [super init];
+  if (self) {
+    _id = id;
+    _modeLabel = modeLabel;
+    _challengeInfo = challengeInfo;
+  }
+  return self;
+}
+
+// Convert this option from the autofill C++ equivalent,
+// CardUnmaskChallengeOption.
++ (instancetype)convertFrom:
+                    (const autofill::CardUnmaskChallengeOption&)autofillOption
+                  modeLabel:(const std::u16string&)modeLabel {
+  return [[CardUnmaskChallengeOptionIOS alloc]
+         initWithId:autofillOption.id
+          modeLabel:base::SysUTF16ToNSString(modeLabel)
+      challengeInfo:base::SysUTF16ToNSString(autofillOption.challenge_info)];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+
+  if (![other isMemberOfClass:[CardUnmaskChallengeOptionIOS class]]) {
+    return NO;
+  }
+
+  return [self isEqualToChallengeOptionIOS:other];
+}
+
+- (BOOL)isEqualToChallengeOptionIOS:(CardUnmaskChallengeOptionIOS*)other {
+  return self->_id == other->_id &&
+         [self->_modeLabel isEqualToString:other->_modeLabel] &&
+         [self->_challengeInfo isEqualToString:other->_challengeInfo];
+}
+
+- (NSUInteger)hash {
+  return base::Hash(self->_id.value()) ^ [self->_modeLabel hash] ^
+         [self->_challengeInfo hash];
+}
+
+@end
diff --git a/ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios_unittest.mm b/ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios_unittest.mm
new file mode 100644
index 0000000..92a6ba0
--- /dev/null
+++ b/ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios_unittest.mm
@@ -0,0 +1,92 @@
+// 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/autofill/model/authentication/card_unmask_challenge_option_ios.h"
+#import "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
+
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+
+using ChallengeOptionId =
+    autofill::CardUnmaskChallengeOption::ChallengeOptionId;
+
+typedef PlatformTest CardUnmaskChallengeOptionIOSTest;
+
+TEST_F(CardUnmaskChallengeOptionIOSTest, ConvertFrom_SetsProperties) {
+  autofill::CardUnmaskChallengeOption autofill_option(
+      ChallengeOptionId("option_id"),
+      autofill::CardUnmaskChallengeOptionType::kEmailOtp,
+      /*challenge_info=*/u"with your email, somebody@example.test",
+      /*challenge_input_length=*/0);
+
+  CardUnmaskChallengeOptionIOS* ios_option =
+      [CardUnmaskChallengeOptionIOS convertFrom:autofill_option
+                                      modeLabel:u"mode label"];
+
+  EXPECT_NSEQ([[CardUnmaskChallengeOptionIOS alloc]
+                     initWithId:ChallengeOptionId("option_id")
+                      modeLabel:@"mode label"
+                  challengeInfo:@"with your email, somebody@example.test"],
+              ios_option);
+}
+
+TEST_F(CardUnmaskChallengeOptionIOSTest, IsEqual_FalseWithNil) {
+  EXPECT_FALSE([[[CardUnmaskChallengeOptionIOS alloc]
+         initWithId:ChallengeOptionId("option_id")
+          modeLabel:@"mode_label"
+      challengeInfo:@"challenge_info"] isEqual:nil]);
+}
+
+TEST_F(CardUnmaskChallengeOptionIOSTest, IsEqual_True) {
+  CardUnmaskChallengeOptionIOS* left = [[CardUnmaskChallengeOptionIOS alloc]
+         initWithId:ChallengeOptionId("option_id")
+          modeLabel:@"mode_label"
+      challengeInfo:@"challenge_info"];
+  CardUnmaskChallengeOptionIOS* right = [[CardUnmaskChallengeOptionIOS alloc]
+         initWithId:ChallengeOptionId("option_id")
+          modeLabel:@"mode_label"
+      challengeInfo:@"challenge_info"];
+  EXPECT_NSEQ(left, right);
+  EXPECT_EQ([left hash], [right hash]);
+}
+
+TEST_F(CardUnmaskChallengeOptionIOSTest, IsEqual_FalseOnOptionId) {
+  CardUnmaskChallengeOptionIOS* left = [[CardUnmaskChallengeOptionIOS alloc]
+         initWithId:ChallengeOptionId("option_id")
+          modeLabel:@""
+      challengeInfo:@""];
+  CardUnmaskChallengeOptionIOS* right = [[CardUnmaskChallengeOptionIOS alloc]
+         initWithId:ChallengeOptionId("option_id2")
+          modeLabel:@""
+      challengeInfo:@""];
+  EXPECT_NSNE(left, right);
+  EXPECT_NE([left hash], [right hash]);
+}
+
+TEST_F(CardUnmaskChallengeOptionIOSTest, IsEqual_FalseOnModeLabel) {
+  CardUnmaskChallengeOptionIOS* left =
+      [[CardUnmaskChallengeOptionIOS alloc] initWithId:ChallengeOptionId("")
+                                             modeLabel:@"mode_label"
+                                         challengeInfo:@""];
+  CardUnmaskChallengeOptionIOS* right =
+      [[CardUnmaskChallengeOptionIOS alloc] initWithId:ChallengeOptionId("")
+                                             modeLabel:@"mode_label2"
+                                         challengeInfo:@""];
+  EXPECT_NSNE(left, right);
+  EXPECT_NE([left hash], [right hash]);
+}
+
+TEST_F(CardUnmaskChallengeOptionIOSTest, IsEqual_FalseOnChallengeInfo) {
+  CardUnmaskChallengeOptionIOS* left =
+      [[CardUnmaskChallengeOptionIOS alloc] initWithId:ChallengeOptionId("")
+                                             modeLabel:@""
+                                         challengeInfo:@"challenge_info"];
+  CardUnmaskChallengeOptionIOS* right =
+      [[CardUnmaskChallengeOptionIOS alloc] initWithId:ChallengeOptionId("")
+                                             modeLabel:@""
+                                         challengeInfo:@"challenge_info2"];
+  EXPECT_NSNE(left, right);
+  EXPECT_NE([left hash], [right hash]);
+}
diff --git a/ios/chrome/browser/commerce/model/push_notification/BUILD.gn b/ios/chrome/browser/commerce/model/push_notification/BUILD.gn
index 9950eb0..ffdb0b8 100644
--- a/ios/chrome/browser/commerce/model/push_notification/BUILD.gn
+++ b/ios/chrome/browser/commerce/model/push_notification/BUILD.gn
@@ -46,6 +46,7 @@
     "//components/commerce/core:shopping_service_test_support",
     "//components/optimization_guide/proto:optimization_guide_proto",
     "//components/session_proto_db:session_proto_db",
+    "//components/sync_bookmarks",
     "//ios/chrome/app/application_delegate:app_state_header",
     "//ios/chrome/browser/bookmarks/model:model",
     "//ios/chrome/browser/commerce/model:session_proto_db",
diff --git a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client_unittest.mm b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client_unittest.mm
index d6cb9f03..db86d38 100644
--- a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client_unittest.mm
+++ b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client_unittest.mm
@@ -24,10 +24,12 @@
 #import "components/optimization_guide/proto/push_notification.pb.h"
 #import "components/session_proto_db/session_proto_db.h"
 #import "components/sync/base/features.h"
+#import "components/sync_bookmarks/bookmark_sync_service.h"
 #import "ios/chrome/app/application_delegate/app_state.h"
 #import "ios/chrome/browser/bookmarks/model/bookmark_model_factory.h"
 #import "ios/chrome/browser/bookmarks/model/legacy_bookmark_model.h"
 #import "ios/chrome/browser/bookmarks/model/local_or_syncable_bookmark_model_factory.h"
+#import "ios/chrome/browser/bookmarks/model/local_or_syncable_bookmark_sync_service_factory.h"
 #import "ios/chrome/browser/commerce/model/session_proto_db_factory.h"
 #import "ios/chrome/browser/commerce/model/shopping_service_factory.h"
 #import "ios/chrome/browser/optimization_guide/model/optimization_guide_service.h"
@@ -204,6 +206,13 @@
               chrome_browser_state_.get());
     }
     bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model_);
+    // Pretend sync is on and bookmarks have been downloaded from the server,
+    // required for price tracking.
+    // TODO(crbug.com/326185948): This is questionable because it means the test
+    // is effectively turning on sync-the-feature.
+    ios::LocalOrSyncableBookmarkSyncServiceFactory::GetForBrowserState(
+        chrome_browser_state_.get())
+        ->SetIsTrackingMetadataForTesting();
     shopping_service_ = static_cast<commerce::MockShoppingService*>(
         commerce::ShoppingServiceFactory::GetForBrowserState(
             chrome_browser_state_.get()));
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index ca41fe6..7a8f75e 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -311,14 +311,9 @@
         {kMagicStackMostVisitedModuleParam, "true"},
         {kReducedSpaceParam, "-80"},
         {kHideIrrelevantModulesParam, "true"}};
-const FeatureEntry::FeatureParam kMagicStackReducedNTPTopSpace[] = {
+const FeatureEntry::FeatureParam kMagicStackHidIrrelevantModules[] = {
     {kMagicStackMostVisitedModuleParam, "false"},
-    {kReducedSpaceParam, "20"}};
-const FeatureEntry::FeatureParam
-    kMagicStackReducedNTPTopSpaceHidIrrelevantModules[] = {
-        {kMagicStackMostVisitedModuleParam, "false"},
-        {kReducedSpaceParam, "20"},
-        {kHideIrrelevantModulesParam, "true"}};
+    {kHideIrrelevantModulesParam, "true"}};
 
 const FeatureEntry::FeatureVariation kMagicStackVariations[]{
     {"Most Visited Tiles in Magic Stack", kMagicStackMostVisitedModule,
@@ -326,11 +321,8 @@
     {"Most Visited Tiles in Magic Stack and hide irrelevant modules",
      kMagicStackMostVisitedModuleHideIrrelevantModules,
      std::size(kMagicStackMostVisitedModuleHideIrrelevantModules), nullptr},
-    {"Magic Stack with less NTP Top Space", kMagicStackReducedNTPTopSpace,
-     std::size(kMagicStackReducedNTPTopSpace), nullptr},
-    {"Magic Stack with less NTP Top Space and hide irrelevant modules",
-     kMagicStackReducedNTPTopSpaceHidIrrelevantModules,
-     std::size(kMagicStackReducedNTPTopSpaceHidIrrelevantModules), nullptr},
+    {"Hide irrelevant modules", kMagicStackHidIrrelevantModules,
+     std::size(kMagicStackHidIrrelevantModules), nullptr},
 };
 
 const FeatureEntry::FeatureParam kEnableDefaultModel[] = {
@@ -1594,6 +1586,12 @@
     {"omnibox-color-icons", flag_descriptions::kOmniboxColorIconsName,
      flag_descriptions::kOmniboxColorIconsDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kOmniboxColorIcons)},
+    {"enable-color-lens-and-voice-icons-in-home-screen-widget",
+     flag_descriptions::kEnableColorLensAndVoiceIconsInHomeScreenWidgetName,
+     flag_descriptions::
+         kEnableColorLensAndVoiceIconsInHomeScreenWidgetDescription,
+     flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kEnableColorLensAndVoiceIconsInHomeScreenWidget)},
     {"minor-mode-restrictions-for-history-sync-opt-in",
      flag_descriptions::kMinorModeRestrictionsForHistorySyncOptInName,
      flag_descriptions::kMinorModeRestrictionsForHistorySyncOptInDescription,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 7bee31ae..c9c71c0 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -321,6 +321,11 @@
 const char kDiscoverFeedSportCardDescription[] =
     "Enables the live sport card in the NTP's Discover feed";
 
+const char kEnableColorLensAndVoiceIconsInHomeScreenWidgetName[] =
+    "Enable color Lens and voice icons in home screen widget.";
+const char kEnableColorLensAndVoiceIconsInHomeScreenWidgetDescription[] =
+    "Shows the color icons, rather than the monochrome icons.";
+
 const char kEnableDiscoverFeedTopSyncPromoName[] =
     "Enables the top of feed sync promo.";
 const char kEnableDiscoverFeedTopSyncPromoDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 73bfc2d..d742b8c0 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -267,6 +267,11 @@
 extern const char kDiscoverFeedSportCardName[];
 extern const char kDiscoverFeedSportCardDescription[];
 
+// Title and description for the flag to enable the color Lens and voice icons
+// in the home screen widget.
+extern const char kEnableColorLensAndVoiceIconsInHomeScreenWidgetName[];
+extern const char kEnableColorLensAndVoiceIconsInHomeScreenWidgetDescription[];
+
 // Title and description for the flag to enable the muting of compromised
 // passwords in the Password Manager.
 extern const char kEnableCompromisedPasswordsMutingName[];
diff --git a/ios/chrome/browser/ntp/model/set_up_list_unittest.mm b/ios/chrome/browser/ntp/model/set_up_list_unittest.mm
index 3686aa4..96d294b 100644
--- a/ios/chrome/browser/ntp/model/set_up_list_unittest.mm
+++ b/ios/chrome/browser/ntp/model/set_up_list_unittest.mm
@@ -379,7 +379,7 @@
 
 // Tests that the Set Up List item order is correct with kMagicStack enabled.
 TEST_F(SetUpListTest, MagicStackItemOrder) {
-  feature_list_.InitWithFeatures({kMagicStack, kIOSTipsNotifications}, {});
+  feature_list_.InitWithFeatures({kIOSTipsNotifications}, {});
   BuildSetUpList();
 
   EXPECT_EQ(GetItemIndex(SetUpListItemType::kDefaultBrowser), 0u);
diff --git a/ios/chrome/browser/shared/public/features/features.h b/ios/chrome/browser/shared/public/features/features.h
index ee1195c..d36b042 100644
--- a/ios/chrome/browser/shared/public/features/features.h
+++ b/ios/chrome/browser/shared/public/features/features.h
@@ -137,6 +137,10 @@
 // Feature flag to enable the Lens entrypoint in the home screen widget.
 BASE_DECLARE_FEATURE(kEnableLensInHomeScreenWidget);
 
+// Feature flag to enable the color Lens and voice icons in the home screen
+// widget.
+BASE_DECLARE_FEATURE(kEnableColorLensAndVoiceIconsInHomeScreenWidget);
+
 // Feature flag to enable the Lens entrypoint in the keyboard.
 BASE_DECLARE_FEATURE(kEnableLensInKeyboard);
 
@@ -533,9 +537,6 @@
 extern const char kTabResumptionAllTabsParam[];
 extern const char kTabResumptionAllTabsOneDayThresholdParam[];
 
-// Whether the Magic Stack should be shown.
-bool IsMagicStackEnabled();
-
 // Whether the feed is contained in a Home module.
 bool IsFeedContainmentEnabled();
 
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm
index cdee2c73..fcc16a9f 100644
--- a/ios/chrome/browser/shared/public/features/features.mm
+++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -130,6 +130,10 @@
              "kIOSNewOmniboxImplementation",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kEnableColorLensAndVoiceIconsInHomeScreenWidget,
+             "kEnableColorLensAndVoiceIconsInHomeScreenWidget",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kEnableLensInOmniboxCopiedImage,
              "EnableLensInOmniboxCopiedImage",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -662,13 +666,8 @@
 const char kTabResumptionAllTabsOneDayThresholdParam[] =
     "tab-resumption-all-tabs-one-day-threshold";
 
-bool IsMagicStackEnabled() {
-  return base::FeatureList::IsEnabled(kMagicStack);
-}
-
 bool IsFeedContainmentEnabled() {
-  return IsMagicStackEnabled() &&
-         base::FeatureList::IsEnabled(kEnableFeedContainment);
+  return base::FeatureList::IsEnabled(kEnableFeedContainment);
 }
 
 int HomeModuleMinimumPadding() {
@@ -677,7 +676,7 @@
 }
 
 bool IsTabResumptionEnabled() {
-  return IsMagicStackEnabled() && base::FeatureList::IsEnabled(kTabResumption);
+  return base::FeatureList::IsEnabled(kTabResumption);
 }
 
 bool IsTabResumptionEnabledForMostRecentTabOnly() {
diff --git a/ios/chrome/browser/tips_notifications/eg_test/tips_notifications_egtest.mm b/ios/chrome/browser/tips_notifications/eg_test/tips_notifications_egtest.mm
index 60ec47fd..ec1b13b 100644
--- a/ios/chrome/browser/tips_notifications/eg_test/tips_notifications_egtest.mm
+++ b/ios/chrome/browser/tips_notifications/eg_test/tips_notifications_egtest.mm
@@ -83,7 +83,6 @@
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config;
   //  config.features_enabled.push_back(kIOSTipsNotifications);
-  config.features_enabled.push_back(kMagicStack);
 
   // Enable Tips Notifications with 1s trigger time.
   std::string enableFeatures = base::StringPrintf(
diff --git a/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm b/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm
index ef2c0d4..90cbe1c 100644
--- a/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm
+++ b/ios/chrome/browser/ui/authentication/cells/signin_promo_view.mm
@@ -308,9 +308,7 @@
       self.imageView.image = nil;
       self.imageView.backgroundColor =
           [UIColor colorNamed:kPrimaryBackgroundColor];
-      // TODO(b/287118358): Cleanup IsMagicStackEnabled() code from the sync
-      // promo after experiment.
-      if (IsMagicStackEnabled() && !IsFeedContainmentEnabled()) {
+      if (!IsFeedContainmentEnabled()) {
         self.imageView.backgroundColor = [UIColor colorNamed:kGrey100Color];
       }
       self.imageView.layer.cornerRadius = kNonProfileIconCornerRadius;
@@ -697,9 +695,7 @@
       self.textLabel.textColor = [UIColor colorNamed:kGrey800Color];
       self.primaryButton.backgroundColor =
           [UIColor colorNamed:kBackgroundColor];
-      // TODO(b/287118358): Cleanup IsMagicStackEnabled() code from the sync
-      // promo after experiment.
-      if (IsMagicStackEnabled() && !IsFeedContainmentEnabled()) {
+      if (!IsFeedContainmentEnabled()) {
         self.primaryButton.backgroundColor =
             [UIColor colorNamed:kBlueHaloColor];
       }
diff --git a/ios/chrome/browser/ui/autofill/authentication/BUILD.gn b/ios/chrome/browser/ui/autofill/authentication/BUILD.gn
index 1c18e84c..b9d56f1 100644
--- a/ios/chrome/browser/ui/autofill/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/authentication/BUILD.gn
@@ -28,6 +28,14 @@
   frameworks = [ "UIKit.framework" ]
 }
 
+group("unit_tests") {
+  testonly = true
+  deps = [
+    ":card_unmask_authentication_selection_mediator_unittest",
+    ":otp_input_dialog_mediator_unittest",
+  ]
+}
+
 source_set("card_unmask_authentication_coordinator") {
   sources = [
     "card_unmask_authentication_coordinator.h",
@@ -45,6 +53,7 @@
     "card_unmask_authentication_selection_coordinator.mm",
   ]
   deps = [
+    ":card_unmask_authentication_selection_mediator",
     "//components/autofill/core/browser",
     "//ios/chrome/browser/autofill/model/bottom_sheet",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
@@ -53,7 +62,35 @@
   ]
 }
 
-source_set("unit_tests") {
+source_set("card_unmask_authentication_selection_mediator") {
+  sources = [
+    "card_unmask_authentication_selection_mediator.h",
+    "card_unmask_authentication_selection_mediator.mm",
+  ]
+  deps = [ "//components/autofill/core/browser" ]
+  public_deps = [ ":card_unmask_authentication_selection_consumer" ]
+}
+
+source_set("card_unmask_authentication_selection_mediator_unittest") {
+  testonly = true
+  sources = [ "card_unmask_authentication_selection_mediator_unittest.mm" ]
+  deps = [
+    ":card_unmask_authentication_selection_mediator",
+    "//components/autofill/core/browser",
+    "//components/strings:components_strings_grit",
+    "//testing/gtest",
+    "//third_party/ocmock",
+    "//ui/base",
+  ]
+}
+
+source_set("card_unmask_authentication_selection_consumer") {
+  sources = [ "card_unmask_authentication_selection_consumer.h" ]
+  deps = [ "//base" ]
+  public_deps = [ "//ios/chrome/browser/autofill/model/authentication:card_unmask_challenge_option_ios" ]
+}
+
+source_set("otp_input_dialog_mediator_unittest") {
   testonly = true
   sources = [ "otp_input_dialog_mediator_unittest.mm" ]
   deps = [
diff --git a/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_consumer.h b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_consumer.h
new file mode 100644
index 0000000..09ba654
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_consumer.h
@@ -0,0 +1,38 @@
+// 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_AUTOFILL_AUTHENTICATION_CARD_UNMASK_AUTHENTICATION_SELECTION_CONSUMER_H_
+#define IOS_CHROME_BROWSER_UI_AUTOFILL_AUTHENTICATION_CARD_UNMASK_AUTHENTICATION_SELECTION_CONSUMER_H_
+
+#import "ios/chrome/browser/autofill/model/authentication/card_unmask_challenge_option_ios.h"
+
+// Consumer for card unmask authentication selection prompts.
+@protocol CardUnmaskAuthenticationSelectionConsumer
+
+// Sets the primary message typically the largest text on the prompt.
+- (void)setHeaderTitle:(NSString*)headerTitle;
+
+// Sets a longer description complementing the header title. Typically shown in
+// smaller text.
+- (void)setHeaderText:(NSString*)headerText;
+
+// Sets additional text shown after the authentication selection options.
+- (void)setFooterText:(NSString*)footerText;
+
+// Sets the label for accepting the challenge option. For example this may be a
+// button labelled "Send" when an OTP challenge is selected, or "Continue" when
+// CVC is selected.
+- (void)setChallengeAcceptanceLabel:(NSString*)challengeAcceptanceLabel;
+
+// Sets the available card unmask options.
+- (void)setCardUnmaskOptions:
+    (NSArray<CardUnmaskChallengeOptionIOS*>*)cardUnmaskChallengeOptions;
+
+// Sets the pending state. This happens while waiting on the payment server
+// after selecting an option.
+- (void)enterPendingState;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_AUTHENTICATION_CARD_UNMASK_AUTHENTICATION_SELECTION_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_coordinator.mm b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_coordinator.mm
index c94857d..fd1e1967f 100644
--- a/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_coordinator.mm
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator.h"
 
 @implementation CardUnmaskAuthenticationSelectionCoordinator {
   // A reference to the base view controller with UINavigationController type.
@@ -23,6 +24,8 @@
   std::unique_ptr<
       autofill::CardUnmaskAuthenticationSelectionDialogControllerImpl>
       _modelController;
+
+  std::unique_ptr<CardUnmaskAuthenticationSelectionMediator> _mediator;
 }
 
 - (instancetype)initWithBaseNavigationController:
@@ -45,6 +48,9 @@
   // TODO(crbug.com/40282545): Remove placeholder view and implement card
   // unmask authentication.
   UIViewController* selectionViewController = [[UIViewController alloc] init];
+  _mediator = std::make_unique<CardUnmaskAuthenticationSelectionMediator>(
+      _modelController->GetWeakPtr(),
+      /*consumer=*/nil);
   selectionViewController.view.backgroundColor = [UIColor redColor];
   selectionViewController.title = @"TODO";
   _selectionViewController = selectionViewController;
diff --git a/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator.h b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator.h
new file mode 100644
index 0000000..39e43e1
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator.h
@@ -0,0 +1,53 @@
+// 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_AUTOFILL_AUTHENTICATION_CARD_UNMASK_AUTHENTICATION_SELECTION_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_UI_AUTOFILL_AUTHENTICATION_CARD_UNMASK_AUTHENTICATION_SELECTION_MEDIATOR_H_
+
+#import "base/memory/weak_ptr.h"
+#import "components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog.h"
+#import "ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_consumer.h"
+
+namespace autofill {
+class CardUnmaskAuthenticationSelectionDialogControllerImpl;
+}  // namespace autofill
+
+// CardUnmaskAuthenticationSelectionMediator mediates between
+// CardUnmaskAuthenticationSelectionDialogControllerImpl and view controllers
+// via the CardUnmaskAuthenticationSelectionConsumer interface.
+class CardUnmaskAuthenticationSelectionMediator
+    : public autofill::CardUnmaskAuthenticationSelectionDialog {
+ public:
+  CardUnmaskAuthenticationSelectionMediator(
+      base::WeakPtr<
+          autofill::CardUnmaskAuthenticationSelectionDialogControllerImpl>
+          model_controller,
+      id<CardUnmaskAuthenticationSelectionConsumer> consumer);
+
+  // Non-copyable.
+  CardUnmaskAuthenticationSelectionMediator(
+      CardUnmaskAuthenticationSelectionMediator&) = delete;
+  const CardUnmaskAuthenticationSelectionMediator& operator=(
+      CardUnmaskAuthenticationSelectionMediator&) = delete;
+
+  virtual ~CardUnmaskAuthenticationSelectionMediator();
+
+  // Handles selecting a challenge option.
+  void DidSelectChallengeOption(CardUnmaskChallengeOptionIOS* option);
+
+  // autofill::CardUnmaskAuthenticationSelectionDialog
+  void Dismiss(bool user_closed_dialog, bool server_success) override;
+  void UpdateContent() override;
+
+ private:
+  base::WeakPtr<autofill::CardUnmaskAuthenticationSelectionDialogControllerImpl>
+      model_controller_;
+  id<CardUnmaskAuthenticationSelectionConsumer> consumer_;
+
+  // Converts the autofill challenge options to ios challenge options destined
+  // for the CardUnmaskAuthenticationSelectionConsumer.
+  NSArray<CardUnmaskChallengeOptionIOS*>* ConvertChallengeOptions();
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_AUTHENTICATION_CARD_UNMASK_AUTHENTICATION_SELECTION_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator.mm b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator.mm
new file mode 100644
index 0000000..cea1d98
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator.mm
@@ -0,0 +1,81 @@
+// 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/ui/autofill/authentication/card_unmask_authentication_selection_mediator.h"
+
+#import "base/memory/weak_ptr.h"
+#import "base/strings/sys_string_conversions.h"
+#import "components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.h"
+
+namespace {
+autofill::CardUnmaskAuthenticationSelectionDialog*
+ReturnMediatorIgnoreControllerArg(
+    CardUnmaskAuthenticationSelectionMediator* mediator,
+    autofill::CardUnmaskAuthenticationSelectionDialogController*
+        _unused_controller) {
+  return mediator;
+}
+}  // namespace
+
+CardUnmaskAuthenticationSelectionMediator::
+    CardUnmaskAuthenticationSelectionMediator(
+        base::WeakPtr<
+            autofill::CardUnmaskAuthenticationSelectionDialogControllerImpl>
+            model_controller,
+        id<CardUnmaskAuthenticationSelectionConsumer> consumer)
+    : model_controller_(model_controller), consumer_(consumer) {
+  model_controller_->ShowDialog(
+      base::BindOnce(&ReturnMediatorIgnoreControllerArg, this));
+  [consumer_ setHeaderTitle:base::SysUTF16ToNSString(
+                                model_controller_->GetWindowTitle())];
+  [consumer_ setHeaderText:base::SysUTF16ToNSString(
+                               model_controller_->GetContentHeaderText())];
+  [consumer_ setCardUnmaskOptions:ConvertChallengeOptions()];
+  [consumer_ setFooterText:base::SysUTF16ToNSString(
+                               model_controller_->GetContentFooterText())];
+}
+
+CardUnmaskAuthenticationSelectionMediator::
+    ~CardUnmaskAuthenticationSelectionMediator() = default;
+
+void CardUnmaskAuthenticationSelectionMediator::DidSelectChallengeOption(
+    CardUnmaskChallengeOptionIOS* option) {
+  model_controller_->SetSelectedChallengeOptionId(option.id);
+  // The done button label changes in response.
+  [consumer_
+      setChallengeAcceptanceLabel:base::SysUTF16ToNSString(
+                                      model_controller_->GetOkButtonLabel())];
+}
+
+// Implemention of autofill::CardUnmaskAuthenticationSelectionDialog follows:
+
+void CardUnmaskAuthenticationSelectionMediator::Dismiss(bool user_closed_dialog,
+                                                        bool server_success) {
+  // TODO(crbug.com/40282545): Implement dismissal of the authentication
+  // selection view by delegating to the coordinator.
+  model_controller_->OnDialogClosed(user_closed_dialog, server_success);
+}
+
+void CardUnmaskAuthenticationSelectionMediator::UpdateContent() {
+  [consumer_ enterPendingState];
+}
+
+// TODO(crbug.com/40282545): Once the ViewController is implemented, handle
+// cancelling and pushing ok by implementing the view controller's mutator.
+
+NSArray<CardUnmaskChallengeOptionIOS*>*
+CardUnmaskAuthenticationSelectionMediator::ConvertChallengeOptions() {
+  const std::vector<autofill::CardUnmaskChallengeOption>& autofill_options =
+      model_controller_->GetChallengeOptions();
+  NSMutableArray* ios_options =
+      [NSMutableArray arrayWithCapacity:autofill_options.size()];
+  for (auto option : autofill_options) {
+    [ios_options
+        addObject:[CardUnmaskChallengeOptionIOS
+                      convertFrom:option
+                        modeLabel:model_controller_->GetAuthenticationModeLabel(
+                                      option)]];
+  }
+  return ios_options;
+}
diff --git a/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator_unittest.mm b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator_unittest.mm
new file mode 100644
index 0000000..6f07fa94
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_mediator_unittest.mm
@@ -0,0 +1,142 @@
+// 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/ui/autofill/authentication/card_unmask_authentication_selection_mediator.h"
+
+#import "base/functional/callback_helpers.h"
+#import "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
+#import "components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.h"
+#import "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/ui/autofill/authentication/card_unmask_authentication_selection_consumer.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
+#import "ui/base/l10n/l10n_util.h"
+
+using ChallengeOptionId =
+    autofill::CardUnmaskChallengeOption::ChallengeOptionId;
+
+class CardUnmaskAuthenticationSelectionMediatorTest : public PlatformTest {
+ public:
+  void SetUp() override {
+    PlatformTest::SetUp();
+    consumer_ =
+        OCMProtocolMock(@protocol(CardUnmaskAuthenticationSelectionConsumer));
+  }
+
+  void TearDown() override {
+    if (consumer_) {
+      EXPECT_OCMOCK_VERIFY((id)consumer_);
+    }
+  }
+
+  CardUnmaskAuthenticationSelectionMediator* InitializeMediator(
+      const std::vector<autofill::CardUnmaskChallengeOption>&
+          challenge_options) {
+    CHECK(!mediator_) << "Only initialize the mediator once in tests.";
+    controller_ = std::make_unique<
+        autofill::CardUnmaskAuthenticationSelectionDialogControllerImpl>(
+        challenge_options, base::DoNothing(), base::DoNothing());
+    mediator_ = std::make_unique<CardUnmaskAuthenticationSelectionMediator>(
+        controller_->GetWeakPtr(), consumer_);
+    return mediator_.get();
+  }
+
+  id<CardUnmaskAuthenticationSelectionConsumer> consumer() { return consumer_; }
+
+  autofill::CardUnmaskAuthenticationSelectionDialogControllerImpl*
+  controller() {
+    return controller_.get();
+  }
+
+  // An SMS autofill challenge option. Matching the IOS version below.
+  autofill::CardUnmaskChallengeOption SmsAutofillChallengeOption() {
+    return autofill::CardUnmaskChallengeOption(
+        ChallengeOptionId("id1"),
+        autofill::CardUnmaskChallengeOptionType::kSmsOtp,
+        /*challenge_info=*/u"challenge_info1",
+        /*challenge_length_input=*/1U);
+  }
+  // An SMS challenge option. Matching the autofill version above.
+  CardUnmaskChallengeOptionIOS* SmsIOSChallengeOption() {
+    return [[CardUnmaskChallengeOptionIOS alloc]
+           initWithId:ChallengeOptionId("id1")
+            modeLabel:l10n_util::GetNSString(
+                          IDS_AUTOFILL_AUTHENTICATION_MODE_GET_TEXT_MESSAGE)
+        challengeInfo:@"challenge_info1"];
+  }
+
+  // A CVC autofill challenge option. Matching the IOS version below.
+  autofill::CardUnmaskChallengeOption CvcAutofillChallengeOption() {
+    return autofill::CardUnmaskChallengeOption(
+        ChallengeOptionId("id2"), autofill::CardUnmaskChallengeOptionType::kCvc,
+        /*challenge_info=*/u"challenge_info2",
+        /*challenge_length_input=*/2U);
+  }
+  // A CVC challenge option. Matching the autofill version above.
+  CardUnmaskChallengeOptionIOS* CvcIOSChallengeOption() {
+    return [[CardUnmaskChallengeOptionIOS alloc]
+           initWithId:ChallengeOptionId("id2")
+            modeLabel:l10n_util::GetNSString(
+                          IDS_AUTOFILL_AUTHENTICATION_MODE_SECURITY_CODE)
+        challengeInfo:@"challenge_info2"];
+  }
+
+ private:
+  id<CardUnmaskAuthenticationSelectionConsumer> consumer_;
+  // Mediator listed first to destruct the controller (which holds a reference
+  // to the mediator) before the mediator.
+  std::unique_ptr<CardUnmaskAuthenticationSelectionMediator> mediator_;
+  std::unique_ptr<
+      autofill::CardUnmaskAuthenticationSelectionDialogControllerImpl>
+      controller_;
+};
+
+TEST_F(CardUnmaskAuthenticationSelectionMediatorTest,
+       SetsHeaderTitleAndHeaderTextAndOptionsAndFooter) {
+  OCMExpect([consumer()
+      setHeaderTitle:
+          l10n_util::GetNSString(
+              IDS_AUTOFILL_CARD_AUTH_SELECTION_DIALOG_TITLE_MULTIPLE_OPTIONS)]);
+  OCMExpect([consumer()
+      setHeaderText:
+          l10n_util::GetNSString(
+              IDS_AUTOFILL_CARD_UNMASK_AUTHENTICATION_SELECTION_DIALOG_ISSUER_CONFIRMATION_TEXT)]);
+  NSArray<CardUnmaskChallengeOptionIOS*>* ios_challenge_options =
+      @[ SmsIOSChallengeOption(), CvcIOSChallengeOption() ];
+  OCMExpect([consumer() setCardUnmaskOptions:ios_challenge_options]);
+  OCMExpect([consumer()
+      setFooterText:
+          l10n_util::GetNSString(
+              IDS_AUTOFILL_CARD_UNMASK_AUTHENTICATION_SELECTION_DIALOG_CURRENT_INFO_NOT_SEEN_TEXT)]);
+
+  InitializeMediator(
+      {SmsAutofillChallengeOption(), CvcAutofillChallengeOption()});
+}
+
+TEST_F(CardUnmaskAuthenticationSelectionMediatorTest,
+       OnDidSelectChallengeOption_SetsButtonLabel) {
+  CardUnmaskAuthenticationSelectionMediator* mediator =
+      InitializeMediator({SmsAutofillChallengeOption()});
+
+  OCMExpect([consumer()
+      setChallengeAcceptanceLabel:
+          l10n_util::GetNSString(
+              IDS_AUTOFILL_CARD_UNMASK_AUTHENTICATION_SELECTION_DIALOG_OK_BUTTON_LABEL_SEND)]);
+  mediator->DidSelectChallengeOption(SmsIOSChallengeOption());
+}
+
+TEST_F(CardUnmaskAuthenticationSelectionMediatorTest,
+       OnUpdateContent_CallsEnterPendingState) {
+  // The interface method CardUnmaskAuthenticationSelectionDialog::UpdateContent
+  // is called to set the pending state.
+  CardUnmaskAuthenticationSelectionMediator* mediator =
+      InitializeMediator({SmsAutofillChallengeOption()});
+
+  OCMExpect([consumer() enterPendingState]);
+
+  mediator->UpdateContent();
+}
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
index 07caa29a..81cde51f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
@@ -104,10 +104,8 @@
 UIColor* FakeboxIconColor() {
   if (IsIOSLargeFakeboxEnabled()) {
     return [UIColor colorNamed:kGrey700Color];
-  } else if (IsMagicStackEnabled()) {
-    return [UIColor colorNamed:@"fake_omnibox_placeholder_color"];
   }
-  return [UIColor colorNamed:kTextfieldPlaceholderColor];
+  return [UIColor colorNamed:@"fake_omnibox_placeholder_color"];
 }
 
 // Sets up fakebox button with a symbol and a round background.
@@ -356,10 +354,8 @@
 UIColor* SearchHintLabelColor() {
   if (IsIOSLargeFakeboxEnabled()) {
     return [UIColor colorNamed:kGrey800Color];
-  } else if (IsMagicStackEnabled()) {
-    return [UIColor colorNamed:@"fake_omnibox_placeholder_color"];
   }
-  return [UIColor colorNamed:kTextfieldPlaceholderColor];
+  return [UIColor colorNamed:@"fake_omnibox_placeholder_color"];
 }
 
 int SetUpListTitleStringID() {
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 95c8fecd..0b3c292 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -330,21 +330,19 @@
     [moduleMediators addObject:_safetyCheckMediator];
   }
 
-  if (IsMagicStackEnabled()) {
-    _magicStackRankingModel = [[MagicStackRankingModel alloc]
-        initWithSegmentationService:
-            segmentation_platform::SegmentationPlatformServiceFactory::
-                GetForBrowserState(self.browser->GetBrowserState())
-                        prefService:prefs
-                         localState:GetApplicationContext()->GetLocalState()
-                    moduleMediators:moduleMediators];
-    _magicStackRankingModel.contentSuggestionsMetricsRecorder =
-        self.contentSuggestionsMetricsRecorder;
-    self.contentSuggestionsMediator.magicStackRankingModel =
-        _magicStackRankingModel;
-    if (IsIOSMagicStackCollectionViewEnabled()) {
-      _magicStackRankingModel.delegate = self.contentSuggestionsMediator;
-    }
+  _magicStackRankingModel = [[MagicStackRankingModel alloc]
+      initWithSegmentationService:
+          segmentation_platform::SegmentationPlatformServiceFactory::
+              GetForBrowserState(self.browser->GetBrowserState())
+                      prefService:prefs
+                       localState:GetApplicationContext()->GetLocalState()
+                  moduleMediators:moduleMediators];
+  _magicStackRankingModel.contentSuggestionsMetricsRecorder =
+      self.contentSuggestionsMetricsRecorder;
+  self.contentSuggestionsMediator.magicStackRankingModel =
+      _magicStackRankingModel;
+  if (IsIOSMagicStackCollectionViewEnabled()) {
+    _magicStackRankingModel.delegate = self.contentSuggestionsMediator;
   }
 
   self.contentSuggestionsViewController =
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
index 38dd005..7a0b2631 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -109,9 +109,7 @@
   if (!self.consumer) {
     return;
   }
-  if (IsMagicStackEnabled()) {
-    [self.magicStackRankingModel fetchLatestMagicStackRanking];
-  }
+  [self.magicStackRankingModel fetchLatestMagicStackRanking];
   if (!ShouldPutMostVisitedSitesInMagicStack() &&
       self.mostVisitedTilesMediator.mostVisitedConfig) {
     [self.consumer setMostVisitedTilesConfig:self.mostVisitedTilesMediator
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index dd37033..60339d2 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -67,9 +67,6 @@
 
 namespace {
 
-// The bottom padding for the vertical stack view.
-const float kBottomStackViewPadding = 6.0f;
-
 // The corner radius of the Magic Stack.
 const float kMagicStackCornerRadius = 16.0f;
 
@@ -144,11 +141,7 @@
   self.dragDropHandler.dropDelegate = self;
   [self.view addInteraction:[[UIDropInteraction alloc]
                                 initWithDelegate:self.dragDropHandler]];
-  if (IsMagicStackEnabled()) {
-    self.view.backgroundColor = [UIColor clearColor];
-  } else {
-    self.view.backgroundColor = ntp_home::NTPBackgroundColor();
-  }
+  self.view.backgroundColor = [UIColor clearColor];
   self.view.accessibilityIdentifier = kContentSuggestionsCollectionIdentifier;
 
   self.verticalStackView = [[UIStackView alloc] init];
@@ -161,17 +154,6 @@
   self.verticalStackView.distribution = UIStackViewDistributionFill;
   [self.view addSubview:self.verticalStackView];
 
-  // Add bottom spacing to the last module by applying it after
-  // `_verticalStackView`. If `IsContentSuggestionsUIModuleRefreshEnabled()` is
-  // YES, and ShouldMinimizeSpacingForModuleRefresh() is YES, then no space is
-  // added after the last module. Otherwise we add kModuleVerticalSpacing. If
-  // `IsContentSuggestionsUIModuleRefreshEnabled()` is NO, then we add
-  // `kBottomStackViewPadding`
-  CGFloat bottomSpacing = kBottomStackViewPadding;
-  if (IsMagicStackEnabled()) {
-    // Add more spacing between magic stack and feed header.
-    bottomSpacing = kBottomMagicStackPadding;
-  }
   [NSLayoutConstraint activateConstraints:@[
     [self.verticalStackView.leadingAnchor
         constraintEqualToAnchor:self.view.leadingAnchor],
@@ -182,7 +164,7 @@
                        constant:content_suggestions::HeaderBottomPadding()],
     [self.verticalStackView.bottomAnchor
         constraintEqualToAnchor:self.view.bottomAnchor
-                       constant:-bottomSpacing]
+                       constant:-kBottomMagicStackPadding]
   ]];
 
   if (_mostVisitedTileConfig && !ShouldPutMostVisitedSitesInMagicStack()) {
@@ -191,7 +173,7 @@
 
   // Only Create Magic Stack if the ranking has been received. It can be delayed
   // to after -viewDidLoad if fecthing from Segmentation Platform.
-  if (IsMagicStackEnabled() && !IsIOSMagicStackCollectionViewEnabled()) {
+  if (!IsIOSMagicStackCollectionViewEnabled()) {
     [self createMagicStack];
     if (_magicStackRankReceived) {
       [self populateMagicStack];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_unittest.mm
index 5631b3c..e57b9be 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_unittest.mm
@@ -181,14 +181,6 @@
 // only after the initial view construction.
 TEST_F(ContentSuggestionsViewControllerTest,
        TestMagicStackTopImpressionMetricSegmentation) {
-  scoped_feature_list_.Reset();
-  scoped_feature_list_.InitWithFeaturesAndParameters(
-      {{segmentation_platform::features::kSegmentationPlatformFeature, {}},
-       {segmentation_platform::features::kSegmentationPlatformIosModuleRanker,
-        {{segmentation_platform::kDefaultModelEnabledParam, "true"}}},
-       {kMagicStack, {}}},
-      {});
-
   [view_controller_ setShortcutTilesConfig:ShortcutsConfigWithBookmark()];
   histogram_tester_->ExpectBucketCount(kMagicStackTopModuleImpressionHistogram,
                                        ContentSuggestionsModuleType::kShortcuts,
@@ -409,14 +401,6 @@
 // Magic Stack order available yet, and that the placeholders are replaced with
 // the real modules once the Magic Stack order is received.
 TEST_F(ContentSuggestionsViewControllerTest, TestMagicStackPlaceholder) {
-  scoped_feature_list_.Reset();
-  scoped_feature_list_.InitWithFeaturesAndParameters(
-      {{segmentation_platform::features::kSegmentationPlatformFeature, {}},
-       {segmentation_platform::features::kSegmentationPlatformIosModuleRanker,
-        {{segmentation_platform::kDefaultModelEnabledParam, "true"}}},
-       {kMagicStack, {}}},
-      {});
-
   [view_controller_ setShortcutTilesConfig:ShortcutsConfigWithBookmark()];
 
   [view_controller_ loadViewIfNeeded];
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm
index 2e6d581..a9bfc20 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm
@@ -195,8 +195,7 @@
 
 - (void)tabResumptionHelperDidReceiveItem {
   CHECK(IsTabResumptionEnabled());
-  if (!self.consumer ||
-      tab_resumption_prefs::IsTabResumptionDisabled(_localState)) {
+  if (tab_resumption_prefs::IsTabResumptionDisabled(_localState)) {
     return;
   }
 
@@ -204,8 +203,7 @@
 }
 
 - (void)tabResumptionHelperDidReplaceItem:(TabResumptionItem*)oldItem {
-  if (!self.consumer ||
-      tab_resumption_prefs::IsTabResumptionDisabled(_localState)) {
+  if (tab_resumption_prefs::IsTabResumptionDisabled(_localState)) {
     return;
   }
 
@@ -576,6 +574,9 @@
     return;
   }
 
+  if (!self.consumer) {
+    return;
+  }
   _latestMagicStackOrder =
       base::FeatureList::IsEnabled(
           segmentation_platform::features::kSegmentationPlatformIosModuleRanker)
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_mediator_unittest.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_mediator_unittest.mm
index 8e2914c..76af980 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_mediator_unittest.mm
@@ -27,9 +27,8 @@
 class MagicStackHalfSheetMediatorTest : public PlatformTest {
  public:
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures({kMagicStack, kSafetyCheckMagicStack,
-                                           kTabResumption, kIOSParcelTracking},
-                                          {});
+    scoped_feature_list_.InitWithFeatures(
+        {kSafetyCheckMagicStack, kTabResumption, kIOSParcelTracking}, {});
 
     // Necessary set up for kIOSSetUpList.
     local_state_.Get()->ClearPref(set_up_list_prefs::kDisabled);
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller_unittest.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller_unittest.mm
index 548afa74..812fff1 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller_unittest.mm
@@ -18,9 +18,8 @@
 class MagicStackHalfSheetTableViewControllerUnittest : public PlatformTest {
  public:
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures({kMagicStack, kSafetyCheckMagicStack,
-                                           kTabResumption, kIOSParcelTracking},
-                                          {});
+    scoped_feature_list_.InitWithFeatures(
+        {kSafetyCheckMagicStack, kTabResumption, kIOSParcelTracking}, {});
 
     view_controller_ = [[MagicStackHalfSheetTableViewController alloc] init];
   }
diff --git a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view_egtest.mm b/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view_egtest.mm
index 21e50af2..55a8411 100644
--- a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view_egtest.mm
@@ -87,8 +87,6 @@
 
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config;
-
-  config.features_enabled.push_back(kMagicStack);
   config.features_enabled.push_back(kSafetyCheckMagicStack);
   config.features_enabled.push_back(
       password_manager::features::kIOSPasswordAuthOnEntryV2);
@@ -98,7 +96,13 @@
 
 // Tests that long pressing the Safety Check view displays a context menu; tests
 // the Safety Check view is properly hidden via the context menu.
-- (void)testLongPressAndHide {
+// TODO(crbug.com/330173089): Test fails on device.
+#if !TARGET_IPHONE_SIMULATOR
+#define MAYBE_testLongPressAndHide DISABLED_testLongPressAndHide
+#else
+#define MAYBE_testLongPressAndHide testLongPressAndHide
+#endif
+- (void)MAYBE_testLongPressAndHide {
   [[[EarlGrey
       selectElementWithMatcher:grey_allOf(grey_accessibilityID(
                                               safety_check::kSafetyCheckViewID),
@@ -124,7 +128,15 @@
 
 // Tests that the Password Checkup view is dismissed when there are no saved
 // passwords.
-- (void)testPasswordCheckupDismissedAfterAllPasswordsGone {
+// TODO(crbug.com/330173089): Test fails on device.
+#if !TARGET_IPHONE_SIMULATOR
+#define MAYBE_testPasswordCheckupDismissedAfterAllPasswordsGone \
+  DISABLED_testPasswordCheckupDismissedAfterAllPasswordsGone
+#else
+#define MAYBE_testPasswordCheckupDismissedAfterAllPasswordsGone \
+  testPasswordCheckupDismissedAfterAllPasswordsGone
+#endif
+- (void)MAYBE_testPasswordCheckupDismissedAfterAllPasswordsGone {
   password_manager_test_utils::SavePasswordFormToProfileStore();
 
   [[[EarlGrey
@@ -180,7 +192,15 @@
 
 // Tests that the Password Checkup view is dismissed when the user doesn't pass
 // Local Authentication.
-- (void)testPasswordCheckupDismissedAfterFailedAuthentication {
+// TODO(crbug.com/330173089): Test fails on device.
+#if !TARGET_IPHONE_SIMULATOR
+#define MAYBE_testPasswordCheckupDismissedAfterFailedAuthentication \
+  DISABLED_testPasswordCheckupDismissedAfterFailedAuthentication
+#else
+#define MAYBE_testPasswordCheckupDismissedAfterFailedAuthentication \
+  testPasswordCheckupDismissedAfterFailedAuthentication
+#endif
+- (void)MAYBE_testPasswordCheckupDismissedAfterFailedAuthentication {
   password_manager_test_utils::SavePasswordFormToProfileStore();
 
   [[[EarlGrey
diff --git a/ios/chrome/browser/ui/lens/lens_coordinator.mm b/ios/chrome/browser/ui/lens/lens_coordinator.mm
index 206b690..78e05b0 100644
--- a/ios/chrome/browser/ui/lens/lens_coordinator.mm
+++ b/ios/chrome/browser/ui/lens/lens_coordinator.mm
@@ -470,6 +470,17 @@
       !base::FeatureList::IsEnabled(kDisableLensCamera) &&
       ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TABLET;
   [sharedDefaults setBool:enableLensInWidget forKey:enableLensInWidgetKey];
+
+  // If the Lens entrypoint is shown, determine whether to show the color or
+  // monochrome icons.
+  NSString* enableColorLensAndVoiceIconsInHomeScreenWidgetKey =
+      base::SysUTF8ToNSString(
+          app_group::kChromeAppGroupEnableColorLensAndVoiceIconsInWidget);
+  const bool enableColorLensAndVoiceIconsInHomeScreenWidget =
+      base::FeatureList::IsEnabled(
+          kEnableColorLensAndVoiceIconsInHomeScreenWidget);
+  [sharedDefaults setBool:enableColorLensAndVoiceIconsInHomeScreenWidget
+                   forKey:enableColorLensAndVoiceIconsInHomeScreenWidgetKey];
 }
 
 // Sets the app shortcut item for either the QR code scanner or Lens.
diff --git a/ios/chrome/browser/ui/ntp/feed_header_view_controller.mm b/ios/chrome/browser/ui/ntp/feed_header_view_controller.mm
index 1c32f914..bb88a028 100644
--- a/ios/chrome/browser/ui/ntp/feed_header_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/feed_header_view_controller.mm
@@ -118,14 +118,9 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  // Applies an opacity to the background. If ReduceTransparency is enabled,
-  // then this replaces the blur effect.
-  // With the Magic Stack enabled, the background color will
-  // be clear for continuity with the overall NTP gradient view.
-  self.view.backgroundColor = IsMagicStackEnabled()
-                                  ? [UIColor clearColor]
-                                  : [[UIColor colorNamed:kBackgroundColor]
-                                        colorWithAlphaComponent:0.95];
+  // The background color will be clear for continuity with the overall NTP
+  // gradient view.
+  self.view.backgroundColor = [UIColor clearColor];
 
   self.container = [[UIView alloc] init];
 
@@ -819,10 +814,8 @@
 - (UIColor*)backgroundColorForBlurredState:(BOOL)blurred {
   if (blurred) {
     return [[UIColor colorNamed:kBackgroundColor] colorWithAlphaComponent:0.1];
-  } else if (IsMagicStackEnabled()) {
-    return [UIColor clearColor];
   } else {
-    return [[UIColor colorNamed:kBackgroundColor] colorWithAlphaComponent:0.95];
+    return [UIColor clearColor];
   }
 }
 
diff --git a/ios/chrome/browser/ui/ntp/feed_top_section/feed_top_section_view_controller.mm b/ios/chrome/browser/ui/ntp/feed_top_section/feed_top_section_view_controller.mm
index 89c0fe5..812cd9f 100644
--- a/ios/chrome/browser/ui/ntp/feed_top_section/feed_top_section_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/feed_top_section/feed_top_section_view_controller.mm
@@ -111,9 +111,7 @@
   self.promoViewContainer.translatesAutoresizingMaskIntoConstraints = NO;
   self.promoViewContainer.backgroundColor = [UIColor colorNamed:kGrey100Color];
 
-  // TODO(b/287118358): Cleanup IsMagicStackEnabled() code from the sync promo
-  // after experiment.
-  if (IsMagicStackEnabled() && !IsFeedContainmentEnabled()) {
+  if (!IsFeedContainmentEnabled()) {
     self.promoViewContainer.backgroundColor =
         [UIColor colorNamed:kBackgroundColor];
   }
diff --git a/ios/chrome/browser/ui/ntp/feed_top_section/notifications_promo_view.mm b/ios/chrome/browser/ui/ntp/feed_top_section/notifications_promo_view.mm
index 5e46831..a11cb7b 100644
--- a/ios/chrome/browser/ui/ntp/feed_top_section/notifications_promo_view.mm
+++ b/ios/chrome/browser/ui/ntp/feed_top_section/notifications_promo_view.mm
@@ -168,9 +168,7 @@
       button.pointerStyleProvider = CreateOpaqueButtonPointerStyleProvider();
 
       button.backgroundColor = [UIColor colorNamed:kBackgroundColor];
-      // TODO(b/287118358): Cleanup IsMagicStackEnabled() code from the sync
-      // promo after experiment.
-      if (IsMagicStackEnabled() && !IsFeedContainmentEnabled()) {
+      if (!IsFeedContainmentEnabled()) {
         button.backgroundColor = [UIColor colorNamed:kBlueHaloColor];
       }
       // Button layout and constraints.
diff --git a/ios/chrome/browser/ui/ntp/feed_wrapper_view_controller.mm b/ios/chrome/browser/ui/ntp/feed_wrapper_view_controller.mm
index 122bc41..b85b2ac 100644
--- a/ios/chrome/browser/ui/ntp/feed_wrapper_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/feed_wrapper_view_controller.mm
@@ -87,9 +87,7 @@
   [emptyCollectionView setShowsVerticalScrollIndicator:NO];
   [self.view addSubview:emptyCollectionView];
   self.contentCollectionView = emptyCollectionView;
-  self.contentCollectionView.backgroundColor =
-      IsMagicStackEnabled() ? [UIColor clearColor]
-                            : ntp_home::NTPBackgroundColor();
+  self.contentCollectionView.backgroundColor = [UIColor clearColor];
   self.contentCollectionView.translatesAutoresizingMaskIntoConstraints = NO;
   AddSameConstraints(self.contentCollectionView, self.view.safeAreaLayoutGuide);
 }
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm
index bde6f89..12c7a7e6 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm
@@ -92,28 +92,22 @@
 
 // Returns the top color of the Fakebox's gradient background.
 UIColor* FakeboxTopColor() {
-  if (IsMagicStackEnabled()) {
-    if (IsIOSLargeFakeboxEnabled()) {
-      return UIAccessibilityIsReduceTransparencyEnabled()
-                 ? [UIColor colorNamed:@"fake_omnibox_solid_background_color"]
-                 : [UIColor colorNamed:@"fake_omnibox_top_gradient_color"];
-    }
-    return [UIColor colorNamed:@"fake_omnibox_background_color"];
+  if (IsIOSLargeFakeboxEnabled()) {
+    return UIAccessibilityIsReduceTransparencyEnabled()
+               ? [UIColor colorNamed:@"fake_omnibox_solid_background_color"]
+               : [UIColor colorNamed:@"fake_omnibox_top_gradient_color"];
   }
-  return [UIColor colorNamed:kTextfieldBackgroundColor];
+  return [UIColor colorNamed:@"fake_omnibox_background_color"];
 }
 
 // Returns the bottom color of the Fakebox's gradient background.
 UIColor* FakeboxBottomColor() {
-  if (IsMagicStackEnabled()) {
-    if (IsIOSLargeFakeboxEnabled()) {
-      return UIAccessibilityIsReduceTransparencyEnabled()
-                 ? [UIColor colorNamed:@"fake_omnibox_solid_background_color"]
-                 : [UIColor colorNamed:@"fake_omnibox_bottom_gradient_color"];
-    }
-    return [UIColor colorNamed:@"fake_omnibox_background_color"];
+  if (IsIOSLargeFakeboxEnabled()) {
+    return UIAccessibilityIsReduceTransparencyEnabled()
+               ? [UIColor colorNamed:@"fake_omnibox_solid_background_color"]
+               : [UIColor colorNamed:@"fake_omnibox_bottom_gradient_color"];
   }
-  return [UIColor colorNamed:kTextfieldBackgroundColor];
+  return [UIColor colorNamed:@"fake_omnibox_background_color"];
 }
 
 // Returns the background color for the NTP Header view. This is the color
@@ -121,10 +115,8 @@
 UIColor* HeaderBackgroundColor(id<UITraitEnvironment> environment) {
   if (IsIOSLargeFakeboxEnabled() && IsSplitToolbarMode(environment)) {
     return [UIColor colorNamed:kBackgroundColor];
-  } else if (IsMagicStackEnabled()) {
-    return [UIColor colorNamed:@"ntp_background_color"];
   } else {
-    return ntp_home::NTPBackgroundColor();
+    return [UIColor colorNamed:@"ntp_background_color"];
   }
 }
 
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
index 82011d2..27dbffa 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
@@ -216,21 +216,17 @@
               action:@selector(handleSingleTapInView:)];
   singleTapRecognizer.delegate = self;
   [self.view addGestureRecognizer:singleTapRecognizer];
-  if (IsMagicStackEnabled()) {
-    if (!base::FeatureList::IsEnabled(kMagicStackRemoveGradientView)) {
-      _backgroundGradientView = [[GradientView alloc]
-          initWithTopColor:[UIColor colorNamed:kSecondaryBackgroundColor]
-               bottomColor:[UIColor colorNamed:kPrimaryBackgroundColor]];
-      _backgroundGradientView.translatesAutoresizingMaskIntoConstraints = NO;
-      [self.view addSubview:_backgroundGradientView];
-      AddSameConstraints(_backgroundGradientView, self.view);
-    }
-    [self updateModularHomeBackgroundColorForUserInterfaceStyle:
-              self.traitCollection.userInterfaceStyle];
-    self.view.backgroundColor = [UIColor colorNamed:@"ntp_background_color"];
-  } else {
-    self.view.backgroundColor = ntp_home::NTPBackgroundColor();
+  if (!base::FeatureList::IsEnabled(kMagicStackRemoveGradientView)) {
+    _backgroundGradientView = [[GradientView alloc]
+        initWithTopColor:[UIColor colorNamed:kSecondaryBackgroundColor]
+             bottomColor:[UIColor colorNamed:kPrimaryBackgroundColor]];
+    _backgroundGradientView.translatesAutoresizingMaskIntoConstraints = NO;
+    [self.view addSubview:_backgroundGradientView];
+    AddSameConstraints(_backgroundGradientView, self.view);
   }
+  [self updateModularHomeBackgroundColorForUserInterfaceStyle:
+            self.traitCollection.userInterfaceStyle];
+  self.view.backgroundColor = [UIColor colorNamed:@"ntp_background_color"];
 
   [self registerNotifications];
 
@@ -393,10 +389,8 @@
 
   if (previousTraitCollection.userInterfaceStyle !=
       self.traitCollection.userInterfaceStyle) {
-    if (IsMagicStackEnabled()) {
-      [self updateModularHomeBackgroundColorForUserInterfaceStyle:
-                self.traitCollection.userInterfaceStyle];
-    }
+    [self updateModularHomeBackgroundColorForUserInterfaceStyle:
+              self.traitCollection.userInterfaceStyle];
   }
 
   if (previousTraitCollection.horizontalSizeClass !=
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm
index 0960640e..ffd1d08 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm
@@ -901,9 +901,7 @@
   _style = style;
   switch (self.style) {
     case OverscrollStyle::NTP_NON_INCOGNITO:
-      self.backgroundColor = IsMagicStackEnabled()
-                                 ? [UIColor clearColor]
-                                 : ntp_home::NTPBackgroundColor();
+      self.backgroundColor = [UIColor clearColor];
       break;
     case OverscrollStyle::NTP_INCOGNITO:
       self.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
diff --git a/ios/chrome/browser/ui/price_notifications/price_notifications_price_tracking_mediator_unittest.mm b/ios/chrome/browser/ui/price_notifications/price_notifications_price_tracking_mediator_unittest.mm
index e14b014..24eecf6 100644
--- a/ios/chrome/browser/ui/price_notifications/price_notifications_price_tracking_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/price_notifications/price_notifications_price_tracking_mediator_unittest.mm
@@ -104,14 +104,11 @@
 
 }  // namespace
 
-class PriceNotificationsPriceTrackingMediatorTest
-    : public PlatformTest,
-      public testing::WithParamInterface<bool> {
+class PriceNotificationsPriceTrackingMediatorTest : public PlatformTest {
  public:
   PriceNotificationsPriceTrackingMediatorTest() {
-    scoped_feature_list_.InitWithFeatureState(
-        syncer::kReplaceSyncPromosWithSignInPromos,
-        ShouldEnablekReplaceSyncPromosWithSignInPromos());
+    scoped_feature_list_.InitAndEnableFeature(
+        syncer::kReplaceSyncPromosWithSignInPromos);
 
     TestChromeBrowserState::Builder builder;
     builder.AddTestingFactory(ios::BookmarkModelFactory::GetInstance(),
@@ -153,15 +150,12 @@
       bookmark_model_ = ios::BookmarkModelFactory::
           GetModelForBrowserStateIfUnificationEnabledOrDie(
               test_chrome_browser_state.get());
-    } else if (ShouldEnablekReplaceSyncPromosWithSignInPromos()) {
+    } else {
       bookmark_model_ = ios::AccountBookmarkModelFactory::
           GetDedicatedUnderlyingModelForBrowserStateIfUnificationDisabledOrDie(
               test_chrome_browser_state.get());
-    } else {
-      bookmark_model_ = ios::LocalOrSyncableBookmarkModelFactory::
-          GetDedicatedUnderlyingModelForBrowserStateIfUnificationDisabledOrDie(
-              test_chrome_browser_state.get());
     }
+
     bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model_);
 
     shopping_service_ = static_cast<commerce::MockShoppingService*>(
@@ -182,10 +176,6 @@
                                     push_notification_service_.get()];
   }
 
-  bool ShouldEnablekReplaceSyncPromosWithSignInPromos() const {
-    return GetParam();
-  }
-
  protected:
   base::test::ScopedFeatureList scoped_feature_list_;
   web::WebTaskEnvironment task_environment_;
@@ -202,7 +192,7 @@
       [[TestPriceNotificationsConsumer alloc] init];
 };
 
-TEST_P(PriceNotificationsPriceTrackingMediatorTest,
+TEST_F(PriceNotificationsPriceTrackingMediatorTest,
        TrackableItemIsEmptyWhenUserIsViewingProductWebpageAndProduct) {
   PrepareSubscription(shopping_service_, true);
   mediator_.consumer = consumer_;
@@ -217,7 +207,7 @@
   EXPECT_EQ(consumer_.isCurrentlyTrackingVisibleProduct, YES);
 }
 
-TEST_P(
+TEST_F(
     PriceNotificationsPriceTrackingMediatorTest,
     TrackableItemExistsWhenUserUntracksProductFromWebpageIsCurrentlyViewing) {
   commerce::ProductInfo product_info;
@@ -256,7 +246,3 @@
 
   EXPECT_EQ(consumer_.trackableItem.title, product.title);
 }
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         PriceNotificationsPriceTrackingMediatorTest,
-                         ::testing::Bool());
diff --git a/ios/chrome/browser/ui/push_notification/notifications_opt_in_egtest.mm b/ios/chrome/browser/ui/push_notification/notifications_opt_in_egtest.mm
index 5a1184d..60af2388 100644
--- a/ios/chrome/browser/ui/push_notification/notifications_opt_in_egtest.mm
+++ b/ios/chrome/browser/ui/push_notification/notifications_opt_in_egtest.mm
@@ -51,7 +51,6 @@
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config;
   config.features_enabled.push_back(kIOSTipsNotifications);
-  config.features_enabled.push_back(kMagicStack);
   config.features_enabled.push_back(kContentPushNotifications);
   return config;
 }
diff --git a/ios/chrome/browser/ui/push_notification/push_notification_egtest.mm b/ios/chrome/browser/ui/push_notification/push_notification_egtest.mm
index 1a32abf..390d30f 100644
--- a/ios/chrome/browser/ui/push_notification/push_notification_egtest.mm
+++ b/ios/chrome/browser/ui/push_notification/push_notification_egtest.mm
@@ -64,7 +64,6 @@
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config;
   config.features_enabled.push_back(kIOSTipsNotifications);
-  config.features_enabled.push_back(kMagicStack);
   return config;
 }
 
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 543b7b5..37c5138 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
@@ -366,7 +366,7 @@
 // completes its run.
 TEST_F(SafetyCheckMediatorTest, TimestampSetForLatestRun) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({kMagicStack, kSafetyCheckMagicStack}, {});
+  feature_list.InitWithFeatures({kSafetyCheckMagicStack}, {});
 
   mediator_.checkDidRun = true;
 
diff --git a/ios/chrome/browser/ui/tabs/tab_pickup/tab_pickup_egtest.mm b/ios/chrome/browser/ui/tabs/tab_pickup/tab_pickup_egtest.mm
index 88af485..53c95f2 100644
--- a/ios/chrome/browser/ui/tabs/tab_pickup/tab_pickup_egtest.mm
+++ b/ios/chrome/browser/ui/tabs/tab_pickup/tab_pickup_egtest.mm
@@ -88,7 +88,6 @@
   if ([self isRunningTest:@selector
             (testBannerNotDisplayedOnNTPWhenTabResumptionEnbaled)]) {
     config.features_enabled.push_back(kTabResumption);
-    config.features_enabled.push_back(kMagicStack);
 
   } else {
     config.features_disabled.push_back(kTabResumption);
diff --git a/ios/chrome/common/app_group/app_group_constants.h b/ios/chrome/common/app_group/app_group_constants.h
index dc9b986..02ef2d9 100644
--- a/ios/chrome/common/app_group/app_group_constants.h
+++ b/ios/chrome/common/app_group/app_group_constants.h
@@ -128,6 +128,10 @@
 // provider.
 extern const char kChromeAppGroupEnableLensInWidget[];
 
+// The key of a preference containing whether the home screen widget should show
+// the color Lens and voice icons if Lens is shown.
+extern const char kChromeAppGroupEnableColorLensAndVoiceIconsInWidget[];
+
 // The key of a preference containing Chrome client ID reported in the metrics
 // client ID. If the user does not opt in, this value must be cleared from the
 // shared user defaults.
diff --git a/ios/chrome/common/app_group/app_group_constants.mm b/ios/chrome/common/app_group/app_group_constants.mm
index 588a280a..754bc779 100644
--- a/ios/chrome/common/app_group/app_group_constants.mm
+++ b/ios/chrome/common/app_group/app_group_constants.mm
@@ -47,6 +47,8 @@
 const char kChromeAppGroupIsGoogleDefaultSearchEngine[] =
     "isGoogleDefaultSearchEngine";
 const char kChromeAppGroupEnableLensInWidget[] = "enableLensInWidget";
+const char kChromeAppGroupEnableColorLensAndVoiceIconsInWidget[] =
+    "enableColorLensAndVoiceIconsInWidget";
 
 const char kChromeAppClientID[] = "ClientID";
 const char kUserMetricsEnabledDate[] = "UserMetricsEnabledDate";
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index bef829c..74ad3eb7 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -199,6 +199,7 @@
     "//ios/chrome/app/startup:unit_tests",
     "//ios/chrome/browser/app_launcher/model:unit_tests",
     "//ios/chrome/browser/autofill/model:unit_tests",
+    "//ios/chrome/browser/autofill/model/authentication:unit_tests",
     "//ios/chrome/browser/autofill/model/bottom_sheet:unit_tests",
     "//ios/chrome/browser/bookmarks/model:unit_tests",
     "//ios/chrome/browser/bring_android_tabs/model:unit_tests",
diff --git a/ios/chrome/widget_kit_extension/BUILD.gn b/ios/chrome/widget_kit_extension/BUILD.gn
index 62e6bf7..f73313e0 100644
--- a/ios/chrome/widget_kit_extension/BUILD.gn
+++ b/ios/chrome/widget_kit_extension/BUILD.gn
@@ -66,6 +66,7 @@
     "resources:widget_search_bar_color",
     "resources:widget_separator_color",
     "resources:widget_text_color",
+    "resources:widget_voice_icon",
     "//ios/chrome/common/app_group:helper",
     "//ios/chrome/common/ntp_tile",
     "//ui/base:base",
diff --git a/ios/chrome/widget_kit_extension/quick_actions_widget.swift b/ios/chrome/widget_kit_extension/quick_actions_widget.swift
index 4934d3a..b31d207 100644
--- a/ios/chrome/widget_kit_extension/quick_actions_widget.swift
+++ b/ios/chrome/widget_kit_extension/quick_actions_widget.swift
@@ -9,11 +9,12 @@
 struct ConfigureQuickActionsWidgetEntry: TimelineEntry {
   let date: Date
   let useLens: Bool
+  let useColorLensAndVoiceIcons: Bool
 }
 
 struct ConfigureQuickActionsWidgetEntryProvider: TimelineProvider {
   func placeholder(in context: Context) -> ConfigureQuickActionsWidgetEntry {
-    ConfigureQuickActionsWidgetEntry(date: Date(), useLens: false)
+    ConfigureQuickActionsWidgetEntry(date: Date(), useLens: false, useColorLensAndVoiceIcons: false)
   }
 
   func shouldUseLens() -> Bool {
@@ -26,13 +27,24 @@
     return useLens
   }
 
+  func shouldUseColorLensAndVoiceIcons() -> Bool {
+    guard shouldUseLens() else { return false }
+
+    let sharedDefaults: UserDefaults = AppGroupHelper.groupUserDefaults()
+    let useColorLensAndVoiceIcons: Bool =
+      sharedDefaults.bool(
+        forKey: WidgetConstants.QuickActionsWidget.enableColorLensAndVoiceIconsInWidgetKey)
+    return useColorLensAndVoiceIcons
+  }
+
   func getSnapshot(
     in context: Context,
     completion: @escaping (ConfigureQuickActionsWidgetEntry) -> Void
   ) {
     let entry = ConfigureQuickActionsWidgetEntry(
       date: Date(),
-      useLens: shouldUseLens()
+      useLens: shouldUseLens(),
+      useColorLensAndVoiceIcons: shouldUseColorLensAndVoiceIcons()
     )
     completion(entry)
   }
@@ -43,7 +55,8 @@
   ) {
     let entry = ConfigureQuickActionsWidgetEntry(
       date: Date(),
-      useLens: shouldUseLens()
+      useLens: shouldUseLens(),
+      useColorLensAndVoiceIcons: shouldUseColorLensAndVoiceIcons()
     )
     let entries: [ConfigureQuickActionsWidgetEntry] = [entry]
     let timeline: Timeline = Timeline(entries: entries, policy: .never)
@@ -75,6 +88,7 @@
 
 struct QuickActionsWidgetEntryView: View {
   var entry: ConfigureQuickActionsWidgetEntry
+  @Environment(\.colorScheme) var colorScheme: ColorScheme
   @Environment(\.redactionReasons) var redactionReasons
   private let searchAreaHeight: CGFloat = 92
   private let separatorHeight: CGFloat = 32
@@ -148,7 +162,11 @@
             Link(
               destination: WidgetConstants.QuickActionsWidget.voiceSearchUrl
             ) {
-              symbolWithName(symbolName: "mic", system: true)
+              symbolWithName(symbolName: "widget_voice_icon", system: false)
+                .symbolRenderingMode(
+                  (colorScheme == .light && entry.useColorLensAndVoiceIcons)
+                    ? .multicolor : .monochrome
+                )
                 .frame(minWidth: 0, maxWidth: .infinity)
             }
             .accessibility(label: Text(voiceSearchA11yLabel))
@@ -156,6 +174,10 @@
             if entry.useLens {
               Link(destination: WidgetConstants.QuickActionsWidget.lensUrl) {
                 symbolWithName(symbolName: "widget_lens_icon", system: false)
+                  .symbolRenderingMode(
+                    (colorScheme == .light && entry.useColorLensAndVoiceIcons)
+                      ? .multicolor : .monochrome
+                  )
                   .frame(minWidth: 0, maxWidth: .infinity)
               }
               .accessibility(label: Text(lensA11yLabel))
diff --git a/ios/chrome/widget_kit_extension/resources/BUILD.gn b/ios/chrome/widget_kit_extension/resources/BUILD.gn
index 4ab7aad9..0372684 100644
--- a/ios/chrome/widget_kit_extension/resources/BUILD.gn
+++ b/ios/chrome/widget_kit_extension/resources/BUILD.gn
@@ -118,3 +118,10 @@
 colorset("widget_background_color") {
   sources = [ "widget_background_color.colorset/Contents.json" ]
 }
+
+symbolset("widget_voice_icon") {
+  sources = [
+    "widget_voice_icon.symbolset/Contents.json",
+    "widget_voice_icon.symbolset/voice.cr.svg",
+  ]
+}
diff --git a/ios/chrome/widget_kit_extension/resources/widget_voice_icon.symbolset/Contents.json b/ios/chrome/widget_kit_extension/resources/widget_voice_icon.symbolset/Contents.json
new file mode 100644
index 0000000..a46450b
--- /dev/null
+++ b/ios/chrome/widget_kit_extension/resources/widget_voice_icon.symbolset/Contents.json
@@ -0,0 +1,12 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  },
+  "symbols" : [
+    {
+      "filename" : "voice.cr.svg",
+      "idiom" : "universal"
+    }
+  ]
+}
diff --git a/ios/chrome/widget_kit_extension/resources/widget_voice_icon.symbolset/voice.cr.svg b/ios/chrome/widget_kit_extension/resources/widget_voice_icon.symbolset/voice.cr.svg
new file mode 100644
index 0000000..d7f27df
--- /dev/null
+++ b/ios/chrome/widget_kit_extension/resources/widget_voice_icon.symbolset/voice.cr.svg
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--Generator: Apple Native CoreSVG 232.5-->
+<!DOCTYPE svg
+PUBLIC "-//W3C//DTD SVG 1.1//EN"
+       "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="3300" height="2200">
+ <!--glyph: "", point size: 100.0, font version: "19.0d7e1", template writer version: "128"-->
+ <style>.monochrome-0 {}
+.monochrome-1 {}
+.monochrome-2 {}
+.monochrome-3 {}
+
+.multicolor-0:custom {fill:#34A853}
+.multicolor-1:custom {fill:#EA4335}
+.multicolor-2:custom {fill:#FBBC04}
+.multicolor-3:custom {fill:#4183F0}
+
+.hierarchical-0:primary {}
+.hierarchical-1:primary {}
+.hierarchical-2:primary {}
+.hierarchical-3:primary {}
+
+.SFSymbolsPreview34A853 {fill:#34A853;opacity:1.0}
+.SFSymbolsPreview4183F0 {fill:#4183F0;opacity:1.0}
+.SFSymbolsPreviewEA4335 {fill:#EA4335;opacity:1.0}
+.SFSymbolsPreviewFBBC04 {fill:#FBBC04;opacity:1.0}
+</style>
+ <g id="Notes">
+  <rect height="2200" id="artboard" style="fill:white;opacity:1" width="3300" x="0" y="0"/>
+  <line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="292" y2="292"/>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 322)">Weight/Scale Variations</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 559.711 322)">Ultralight</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 856.422 322)">Thin</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1153.13 322)">Light</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1449.84 322)">Regular</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1746.56 322)">Medium</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2043.27 322)">Semibold</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2339.98 322)">Bold</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2636.69 322)">Heavy</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2933.4 322)">Black</text>
+  <line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1903" y2="1903"/>
+  <g transform="matrix(0.2 0 0 0.2 263 1933)">
+   <path d="m46.2402 4.15039c21.5332 0 39.4531-17.8711 39.4531-39.4043s-17.9688-39.4043-39.502-39.4043c-21.4844 0-39.3555 17.8711-39.3555 39.4043s17.9199 39.4043 39.4043 39.4043Zm0-7.42188c-17.7246 0-31.8848-14.209-31.8848-31.9824s14.1113-31.9824 31.8359-31.9824c17.7734 0 32.0312 14.209 32.0312 31.9824s-14.209 31.9824-31.9824 31.9824Zm-17.9688-31.9824c0 2.14844 1.51367 3.61328 3.75977 3.61328h10.498v10.5957c0 2.19727 1.46484 3.71094 3.61328 3.71094 2.24609 0 3.71094-1.51367 3.71094-3.71094v-10.5957h10.5957c2.19727 0 3.71094-1.46484 3.71094-3.61328 0-2.19727-1.51367-3.71094-3.71094-3.71094h-10.5957v-10.5469c0-2.24609-1.46484-3.75977-3.71094-3.75977-2.14844 0-3.61328 1.51367-3.61328 3.75977v10.5469h-10.498c-2.24609 0-3.75977 1.51367-3.75977 3.71094Z"/>
+  </g>
+  <g transform="matrix(0.2 0 0 0.2 281.506 1933)">
+   <path d="m58.5449 14.5508c27.2461 0 49.8047-22.6074 49.8047-49.8047 0-27.2461-22.6074-49.8047-49.8535-49.8047-27.1973 0-49.7559 22.5586-49.7559 49.8047 0 27.1973 22.6074 49.8047 49.8047 49.8047Zm0-8.30078c-23.0469 0-41.4551-18.457-41.4551-41.5039s18.3594-41.5039 41.4062-41.5039 41.5527 18.457 41.5527 41.5039-18.457 41.5039-41.5039 41.5039Zm-22.6562-41.5039c0 2.39258 1.66016 4.00391 4.15039 4.00391h14.3555v14.4043c0 2.44141 1.66016 4.15039 4.05273 4.15039 2.44141 0 4.15039-1.66016 4.15039-4.15039v-14.4043h14.4043c2.44141 0 4.15039-1.61133 4.15039-4.00391 0-2.44141-1.70898-4.15039-4.15039-4.15039h-14.4043v-14.3555c0-2.49023-1.70898-4.19922-4.15039-4.19922-2.39258 0-4.05273 1.70898-4.05273 4.19922v14.3555h-14.3555c-2.49023 0-4.15039 1.70898-4.15039 4.15039Z"/>
+  </g>
+  <g transform="matrix(0.2 0 0 0.2 304.924 1933)">
+   <path d="m74.8535 28.3203c34.8145 0 63.623-28.8086 63.623-63.5742 0-34.8145-28.8574-63.623-63.6719-63.623-34.7656 0-63.5254 28.8086-63.5254 63.623 0 34.7656 28.8086 63.5742 63.5742 63.5742Zm0-9.08203c-30.1758 0-54.4434-24.3164-54.4434-54.4922 0-30.2246 24.2188-54.4922 54.3945-54.4922 30.2246 0 54.541 24.2676 54.541 54.4922 0 30.1758-24.2676 54.4922-54.4922 54.4922Zm-28.8574-54.4922c0 2.58789 1.85547 4.39453 4.58984 4.39453h19.7266v19.7754c0 2.68555 1.85547 4.58984 4.44336 4.58984 2.68555 0 4.54102-1.85547 4.54102-4.58984v-19.7754h19.7754c2.68555 0 4.58984-1.80664 4.58984-4.39453 0-2.73438-1.85547-4.58984-4.58984-4.58984h-19.7754v-19.7266c0-2.73438-1.85547-4.63867-4.54102-4.63867-2.58789 0-4.44336 1.9043-4.44336 4.63867v19.7266h-19.7266c-2.73438 0-4.58984 1.85547-4.58984 4.58984Z"/>
+  </g>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 1953)">Design Variations</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1971)">Symbols are supported in up to nine weights and three scales.</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1989)">For optimal layout with text and other symbols, vertically align</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 2007)">symbols with the adjacent text.</text>
+  <line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="776" x2="776" y1="1919" y2="1933"/>
+  <g transform="matrix(0.2 0 0 0.2 776 1933)">
+   <path d="m16.5527 0.78125c2.58789 0 3.85742-0.976562 4.78516-3.71094l6.29883-17.2363h28.8086l6.29883 17.2363c0.927734 2.73438 2.19727 3.71094 4.73633 3.71094 2.58789 0 4.24805-1.5625 4.24805-4.00391 0-0.830078-0.146484-1.61133-0.537109-2.63672l-22.9004-60.9863c-1.12305-2.97852-3.125-4.49219-6.25-4.49219-3.02734 0-5.07812 1.46484-6.15234 4.44336l-22.9004 61.084c-0.390625 1.02539-0.537109 1.80664-0.537109 2.63672 0 2.44141 1.5625 3.95508 4.10156 3.95508Zm13.4766-28.3691 11.8652-32.8613h0.244141l11.8652 32.8613Z"/>
+  </g>
+  <line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="792.836" x2="792.836" y1="1919" y2="1933"/>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 776 1953)">Margins</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1971)">Leading and trailing margins on the left and right side of each symbol</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1989)">can be adjusted by modifying the x-location of the margin guidelines.</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2007)">Modifications are automatically applied proportionally to all</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2025)">scales and weights.</text>
+  <g transform="matrix(0.2 0 0 0.2 1289 1933)">
+   <path d="m14.209 9.32617 8.49609 8.54492c4.29688 4.3457 9.22852 4.05273 13.8672-1.07422l53.4668-58.9355-4.83398-4.88281-53.0762 58.3984c-1.75781 2.00195-3.41797 2.49023-5.76172 0.146484l-5.85938-5.81055c-2.34375-2.29492-1.80664-4.00391 0.195312-5.81055l57.373-54.0039-4.88281-4.83398-57.959 54.4434c-4.93164 4.58984-5.32227 9.47266-1.02539 13.8184Zm32.0801-90.9668c-2.09961 2.05078-2.24609 4.93164-1.07422 6.88477 1.17188 1.80664 3.4668 2.97852 6.68945 2.14844 7.32422-1.70898 14.9414-2.00195 22.0703 2.68555l-2.92969 7.27539c-1.70898 4.15039-0.830078 7.08008 1.85547 9.81445l11.4746 11.5723c2.44141 2.44141 4.49219 2.53906 7.32422 2.05078l5.32227-0.976562 3.32031 3.36914-0.195312 2.7832c-0.195312 2.49023 0.439453 4.39453 2.88086 6.78711l3.80859 3.71094c2.39258 2.39258 5.46875 2.53906 7.8125 0.195312l14.5508-14.5996c2.34375-2.34375 2.24609-5.32227-0.146484-7.71484l-3.85742-3.80859c-2.39258-2.39258-4.24805-3.17383-6.64062-2.97852l-2.88086 0.244141-3.22266-3.17383 1.2207-5.61523c0.634766-2.83203-0.146484-5.0293-3.07617-7.95898l-10.9863-10.9375c-16.6992-16.6016-38.8672-16.2109-53.3203-1.75781Zm7.4707 1.85547c12.1582-8.88672 28.6133-7.37305 39.7461 3.75977l12.1582 12.0605c1.17188 1.17188 1.36719 2.09961 1.02539 3.80859l-1.61133 7.42188 7.51953 7.42188 4.93164-0.292969c1.26953-0.0488281 1.66016 0.0488281 2.63672 1.02539l2.88086 2.88086-12.207 12.207-2.88086-2.88086c-0.976562-0.976562-1.12305-1.36719-1.07422-2.68555l0.341797-4.88281-7.4707-7.42188-7.61719 1.26953c-1.61133 0.341797-2.34375 0.195312-3.56445-0.976562l-10.0098-10.0098c-1.26953-1.17188-1.41602-2.00195-0.634766-3.85742l4.39453-10.4492c-7.8125-7.27539-17.9688-10.4004-28.125-7.42188-0.78125 0.195312-1.07422-0.439453-0.439453-0.976562Z"/>
+  </g>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 1289 1953)">Exporting</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1971)">Symbols should be outlined when exporting to ensure the</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1989)">design is preserved when submitting to Xcode.</text>
+  <text id="template-version" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1933)">Template v.4.0</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1951)">Requires Xcode 14 or greater</text>
+  <text id="descriptive-name" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1969)">Generated from voice.cr</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1987)">Typeset at 100.0 points</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 726)">Small</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1156)">Medium</text>
+  <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1586)">Large</text>
+ </g>
+ <g id="Guides">
+  <g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 696)">
+   <path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
+  </g>
+  <line id="Baseline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="696" y2="696"/>
+  <line id="Capline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="625.541" y2="625.541"/>
+  <g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1126)">
+   <path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
+  </g>
+  <line id="Baseline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1126" y2="1126"/>
+  <line id="Capline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1055.54" y2="1055.54"/>
+  <g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1556)">
+   <path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
+  </g>
+  <line id="Baseline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1556" y2="1556"/>
+  <line id="Capline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1485.54" y2="1485.54"/>
+  <line id="left-margin-Regular-M" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1400.1" x2="1400.1" y1="1030.79" y2="1150.12"/>
+  <line id="right-margin-Regular-M" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1499.59" x2="1499.59" y1="1030.79" y2="1150.12"/>
+ </g>
+ <g id="Symbols">
+  <g id="Black-L" transform="matrix(1 0 0 1 2862.89 1556)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M80.05 4.59L80.05 28.85C80.05 33.68 76.13 37.61 71.29 37.61C66.45 37.61 62.53 33.68 62.53 28.85L62.53 4.59C62.53-0.25 66.45-4.17 71.29-4.17C76.13-4.17 80.05-0.25 80.05 4.59Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M119.1-50.76C123.28-50.76 126.67-47.37 126.67-43.19L126.67-43.19C126.67-12.39 101.71 12.57 70.91 12.57C40.12 12.57 15.16-12.39 15.16-43.19L15.16-43.19C15.16-47.37 18.55-50.76 22.73-50.76L25.08-50.76C29.27-50.76 32.66-47.37 32.66-43.19L32.66-43.19C32.66-22.06 49.78-4.93 70.91-4.93C92.04-4.93 109.17-22.06 109.17-43.19L109.17-43.19C109.17-47.37 112.56-50.76 116.74-50.76L119.1-50.76Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M27.82-7.8C19.9-17.43 15.15-29.75 15.15-43.19L15.15-43.76C15.15-47.62 18.29-50.76 22.15-50.76L25.65-50.76C29.52-50.76 32.65-47.62 32.65-43.76L32.65-43.19C32.65-34.5 35.54-26.5 40.42-20.08L27.82-7.8Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M46.09-100.8C46.09-114.31 57.03-125.25 70.53-125.25C84.03-125.25 94.98-114.31 94.98-100.8L94.98-43.18C94.98-29.68 84.03-18.74 70.53-18.74C57.03-18.74 46.09-29.68 46.09-43.18L46.09-100.8Z"/>
+  </g>
+  <g id="Heavy-L" transform="matrix(1 0 0 1 2568.3 1556)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M75.43 14.5L75.43 26.5C75.43 30.0899 72.5199 33 68.93 33L68.93 33C65.3401 33 62.43 30.0899 62.43 26.5L62.43 2.5C62.43-1.08985 65.3401-4 68.93-4L68.93-4C72.5199-4 75.43-1.08985 75.43 2.5Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M115.18-51.8C119.18-51.8 122.43-48.56 122.43-44.55L122.43-44.35C122.43-14.78 98.46 9.18 68.9 9.18C39.33 9.18 15.37-14.78 15.37-44.35L15.37-44.55C15.37-48.56 18.61-51.8 22.62-51.8L22.62-51.8C26.62-51.8 29.87-48.56 29.87-44.55L29.87-44.35C29.87-22.79 47.34-5.32 68.9-5.32C90.46-5.32 107.93-22.79 107.93-44.35L107.93-44.55C107.93-48.56 111.18-51.8 115.18-51.8L115.18-51.8Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M27.23-10.46C19.68-19.69 15.15-31.49 15.15-44.35L15.15-44.8C15.15-48.67 18.28-51.8 22.15-51.8L22.65-51.8C26.51-51.8 29.65-48.67 29.65-44.8L29.65-44.35C29.65-35.43 32.64-27.21 37.67-20.64L27.23-10.46Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M44.43-101.08C44.43-114.43 55.25-125.25 68.6-125.25C81.94-125.25 92.76-114.43 92.76-101.08L92.76-44.35C92.76-31.01 81.94-20.19 68.6-20.19C55.25-20.19 44.43-31.01 44.43-44.35L44.43-101.08Z"/>
+  </g>
+  <g id="Bold-L" transform="matrix(1 0 0 1 2276.47 1556)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M70.94 2.57L70.94 24.51C70.94 28.2 67.95 31.19 64.25 31.19C60.56 31.19 57.57 28.2 57.57 24.51L57.57 2.57C57.57-1.12 60.56-4.12 64.25-4.12C67.95-4.12 70.94-1.12 70.94 2.57Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M106.42-47.48C109.87-47.48 112.67-44.69 112.67-41.23L112.67-40.64C112.67-13.71 90.84 8.12 63.91 8.12C36.98 8.12 15.15-13.71 15.15-40.64L15.15-41.23C15.15-44.69 17.95-47.48 21.4-47.48L21.4-47.48C24.85-47.48 27.65-44.69 27.65-41.23L27.65-40.64C27.65-20.61 43.88-4.38 63.91-4.38C83.94-4.38 100.17-20.61 100.17-40.64L100.17-41.23C100.17-44.69 102.97-47.48 106.42-47.48L106.42-47.48Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M26.14-9.8C19.27-18.2 15.15-28.94 15.15-40.64L15.15-41.23C15.15-44.69 17.95-47.48 21.4-47.48C24.85-47.48 27.65-44.69 27.65-41.23L27.65-40.64C27.65-32.33 30.44-24.68 35.13-18.57L26.14-9.8Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M40.86-92.75C40.86-105.29 51.03-115.45 63.57-115.45C76.11-115.45 86.27-105.29 86.27-92.75L86.27-40.64C86.27-28.1 76.11-17.93 63.57-17.93C51.03-17.93 40.86-28.1 40.86-40.64L40.86-92.75Z"/>
+  </g>
+  <g id="Semibold-L" transform="matrix(1 0 0 1 1981.6 1556)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M68.08 1.23L68.08 22.68C68.08 25.81 65.54 28.35 62.41 28.35C59.27 28.35 56.74 25.81 56.74 22.68L56.74 1.23C56.74-1.9 59.27-4.44 62.41-4.44C65.54-4.44 68.08-1.9 68.08 1.23Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M103.62-47.68C106.59-47.68 108.99-45.27 108.99-42.3L108.99-40.99C108.99-15.07 87.98 5.93 62.07 5.93C36.16 5.93 15.15-15.07 15.15-40.99L15.15-42.3C15.15-45.27 17.56-47.68 20.52-47.68L20.52-47.68C23.49-47.68 25.9-45.27 25.9-42.3L25.9-40.99C25.9-21.01 42.09-4.82 62.07-4.82C82.05-4.82 98.24-21.01 98.24-40.99L98.24-42.3C98.24-45.27 100.65-47.68 103.62-47.68L103.62-47.68Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M25.68-11.36C19.1-19.44 15.15-29.75 15.15-40.99L15.15-42.3C15.15-45.27 17.56-47.68 20.52-47.68C23.49-47.68 25.9-45.27 25.9-42.3L25.9-40.99C25.9-32.67 28.7-25.02 33.42-18.91L25.68-11.36Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M39.4-91.92C39.4-104.25 49.4-114.25 61.74-114.25C74.07-114.25 84.07-104.25 84.07-91.92L84.07-40.99C84.07-28.66 74.07-18.66 61.74-18.66C49.4-18.66 39.4-28.66 39.4-40.99L39.4-91.92Z"/>
+  </g>
+  <g id="Medium-L" transform="matrix(1 0 0 1 1686.52 1556)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M65.92 1.5L65.92 22.42C65.92 25.27 63.61 27.57 60.77 27.57C57.92 27.57 55.61 25.27 55.61 22.42L55.61 1.5C55.61-1.35 57.92-3.66 60.77-3.66C63.61-3.66 65.92-1.35 65.92 1.5Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M100.98-46.23C103.6-46.23 105.73-44.11 105.73-41.48L105.73-39.7C105.73-14.69 85.45 5.58 60.44 5.58C35.43 5.58 15.15-14.69 15.15-39.7L15.15-41.48C15.15-44.11 17.28-46.23 19.9-46.23L19.9-46.23C22.53-46.23 24.65-44.11 24.65-41.48L24.65-39.7C24.65-19.94 40.68-3.92 60.44-3.92C80.21-3.92 96.23-19.94 96.23-39.7L96.23-41.48C96.23-44.11 98.36-46.23 100.98-46.23L100.98-46.23Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M25.29-11.15C18.95-18.94 15.15-28.88 15.15-39.7L15.15-41.48C15.15-44.11 17.28-46.23 19.9-46.23C22.53-46.23 24.65-44.11 24.65-41.48L24.65-39.7C24.65-31.46 27.44-23.86 32.13-17.81L25.29-11.15Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M38.17-89.39C38.17-101.51 48-111.34 60.11-111.34C72.23-111.34 82.06-101.51 82.06-89.39L82.06-39.7C82.06-27.58 72.23-17.76 60.11-17.76C48-17.76 38.17-27.58 38.17-39.7L38.17-89.39Z"/>
+  </g>
+  <g id="Regular-L" transform="matrix(1 0 0 1 1392.35 1556)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M62.84 0.87L62.84 19.62C62.84 22.18 60.77 24.25 58.21 24.25C55.66 24.25 53.59 22.18 53.59 19.62L53.59 0.87C53.59-1.68 55.66-3.75 58.21-3.75C60.77-3.75 62.84-1.68 62.84 0.87Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M96.65-46C98.86-46 100.65-44.21 100.65-42L100.65-39.76C100.65-16.15 81.51 2.99 57.9 2.99C34.29 2.99 15.15-16.15 15.15-39.76L15.15-42C15.15-44.21 16.94-46 19.15-46L19.15-46C21.36-46 23.15-44.21 23.15-42L23.15-39.76C23.15-20.57 38.71-5.01 57.9-5.01C77.09-5.01 92.65-20.57 92.65-39.76L92.65-42C92.65-44.21 94.44-46 96.65-46L96.65-46Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M24.69-12.84C18.73-20.19 15.15-29.56 15.15-39.76L15.15-42C15.15-44.21 16.94-46 19.15-46C21.36-46 23.15-44.21 23.15-42L23.15-39.76C23.15-31.73 25.87-24.34 30.45-18.45L24.69-12.84Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M36.59-87.26C36.59-98.86 45.99-108.26 57.59-108.26C69.19-108.26 78.59-98.86 78.59-87.26L78.59-39.76C78.59-28.16 69.19-18.76 57.59-18.76C45.99-18.76 36.59-28.16 36.59-39.76L36.59-87.26Z"/>
+  </g>
+  <g id="Light-L" transform="matrix(1 0 0 1 1096.41 1556)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M61.07 0.12L61.07 18.98C61.07 20.98 59.45 22.61 57.44 22.61C55.44 22.61 53.82 20.98 53.82 18.98L53.82 0.12C53.82-1.88 55.44-3.51 57.44-3.51C59.45-3.51 61.07-1.88 61.07 0.12Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M96.11-47.03C97.76-47.03 99.11-45.69 99.11-44.03L99.11-40.75C99.11-17.57 80.31 1.22 57.13 1.22C33.95 1.22 15.15-17.57 15.15-40.75L15.15-44.03C15.15-45.69 16.5-47.03 18.15-47.03L18.15-47.03C19.81-47.03 21.15-45.69 21.15-44.03L21.15-40.75C21.15-20.88 37.26-4.78 57.13-4.78C77-4.78 93.11-20.88 93.11-40.75L93.11-44.03C93.11-45.69 94.45-47.03 96.11-47.03L96.11-47.03Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M24.46-14.39C18.64-21.6 15.15-30.77 15.15-40.75L15.15-44.03C15.15-45.69 16.5-47.03 18.15-47.03C19.81-47.03 21.15-45.69 21.15-44.03L21.15-40.75C21.15-32.4 24-24.71 28.78-18.6L24.46-14.39Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M35.73-88.53C35.73-100.18 45.17-109.62 56.82-109.62C68.46-109.62 77.91-100.18 77.91-88.53L77.91-40.75C77.91-29.11 68.46-19.66 56.82-19.66C45.17-19.66 35.73-29.11 35.73-40.75L35.73-88.53Z"/>
+  </g>
+  <g id="Thin-L" transform="matrix(1 0 0 1 801.469 1556)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M58.771-0.25L58.771 17.99C58.771 19.71 57.379 21.1 55.662 21.1C53.946 21.1 52.554 19.71 52.554 17.99L52.554-0.25C52.554-1.97 53.946-3.36 55.662-3.36C57.379-3.36 58.771-1.97 58.771-0.25Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M93.067-45.87C94.448-45.87 95.567-44.75 95.567-43.37L95.567-39.79C95.567-17.59 77.565 0.41 55.359 0.41C33.153 0.41 15.152-17.59 15.152-39.79L15.152-43.37C15.152-44.75 16.271-45.87 17.652-45.87L17.652-45.87C19.032-45.87 20.152-44.75 20.152-43.37L20.152-39.79C20.152-20.35 35.915-4.59 55.359-4.59C74.804-4.59 90.567-20.35 90.567-39.79L90.567-43.37C90.567-44.75 91.686-45.87 93.067-45.87L93.067-45.87Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M24.04-14.57C18.479-21.47 15.15-30.24 15.15-39.79L15.15-43.37C15.15-44.75 16.269-45.87 17.65-45.87C19.031-45.87 20.15-44.75 20.15-43.37L20.15-39.79C20.15-31.6 22.947-24.06 27.639-18.08L24.04-14.57Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M34.459-86.01C34.459-97.39 43.68-106.61 55.055-106.61C66.43-106.61 75.652-97.39 75.652-86.01L75.652-39.79C75.652-28.42 66.43-19.19 55.055-19.19C43.68-19.19 34.459-28.42 34.459-39.79L34.459-86.01Z"/>
+  </g>
+  <g id="Ultralight-L" transform="matrix(1 0 0 1 506.565 1556)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M56.431-1.66L56.431 15.95C56.431 17.38 55.273 18.54 53.844 18.54C52.415 18.54 51.257 17.38 51.257 15.95L51.257-1.66C51.257-3.09 52.415-4.25 53.844-4.25C55.273-4.25 56.431-3.09 56.431-1.66Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M89.952-45.7C91.057-45.7 91.952-44.81 91.952-43.7L91.952-39.84C91.952-18.63 74.76-1.44 53.551-1.44C32.343-1.44 15.15-18.63 15.15-39.84L15.15-43.7C15.15-44.81 16.046-45.7 17.15-45.7L17.15-45.7C18.255-45.7 19.15-44.81 19.15-43.7L19.15-39.84C19.15-20.84 34.552-5.44 53.551-5.44C72.551-5.44 87.952-20.84 87.952-39.84L87.952-43.7C87.952-44.81 88.848-45.7 89.952-45.7L89.952-45.7Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M23.617-15.78C18.321-22.37 15.151-30.73 15.151-39.84L15.151-43.7C15.151-44.81 16.046-45.7 17.151-45.7C18.255-45.7 19.151-44.81 19.151-43.7L19.151-39.84C19.151-31.82 21.895-24.44 26.496-18.59L23.617-15.78Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M33.668-84.46C33.668-95.28 42.439-104.05 53.259-104.05C64.079-104.05 72.85-95.28 72.85-84.46L72.85-39.84C72.85-29.02 64.079-20.24 53.259-20.24C42.439-20.24 33.668-29.02 33.668-39.84L33.668-84.46Z"/>
+  </g>
+  <g id="Black-M" transform="matrix(1 0 0 1 2877 1126)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M64.63-1.45L64.63 16.06C64.63 20.23 61.25 23.6 57.08 23.6C52.92 23.6 49.54 20.23 49.54 16.06L49.54-1.45C49.54-5.61 52.92-8.99 57.08-8.99C61.25-8.99 64.63-5.61 64.63-1.45Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M93.01-41.38C96.03-41.38 98.47-38.93 98.47-35.91L98.47-35.91C98.47-12.9 79.82 5.75 56.81 5.75C33.8 5.75 15.15-12.9 15.15-35.91L15.15-35.91C15.15-38.93 17.59-41.38 20.61-41.38L25.19-41.38C28.2-41.38 30.65-38.93 30.65-35.91L30.65-35.91C30.65-21.46 42.36-9.75 56.81-9.75C71.26-9.75 82.97-21.46 82.97-35.91L82.97-35.91C82.97-38.93 85.42-41.38 88.44-41.38L93.01-41.38Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M24.69-9.38C18.73-16.58 15.15-25.83 15.15-35.91C15.15-38.93 17.59-41.38 20.61-41.38L25.18-41.38C28.2-41.38 30.65-38.93 30.65-35.91C30.65-30.04 32.58-24.62 35.85-20.25L24.69-9.38Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M38.66-77.48C38.66-87.36 46.66-95.36 56.54-95.36C66.41-95.36 74.41-87.36 74.41-77.48L74.41-35.91C74.41-26.04 66.41-18.04 56.54-18.04C46.66-18.04 38.66-26.04 38.66-35.91L38.66-77.48Z"/>
+  </g>
+  <g id="Heavy-M" transform="matrix(1 0 0 1 2581.53 1126)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M62.39-1.19L62.39 16.44C62.39 20.06 59.45 22.99 55.84 22.99C52.22 22.99 49.29 20.06 49.29 16.44L49.29-1.19C49.29-4.81 52.22-7.74 55.84-7.74C59.45-7.74 62.39-4.81 62.39-1.19Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M90.47-41.41C93.51-41.41 95.97-38.94 95.97-35.9L95.97-35.9C95.97-13.59 77.88 4.51 55.56 4.51C33.24 4.51 15.15-13.59 15.15-35.9L15.15-35.9C15.15-38.94 17.62-41.41 20.65-41.41L22.15-41.41C25.19-41.41 27.65-38.94 27.65-35.9L27.65-35.9C27.65-20.49 40.15-7.99 55.56-7.99C70.98-7.99 83.47-20.49 83.47-35.9L83.47-35.9C83.47-38.94 85.93-41.41 88.97-41.41L90.47-41.41Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M24.32-10.26C18.59-17.24 15.15-26.17 15.15-35.9C15.15-38.94 17.61-41.41 20.65-41.41L22.15-41.41C25.19-41.41 27.65-38.94 27.65-35.9C27.65-29.57 29.76-23.72 33.32-19.04L24.32-10.26Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M37.31-77.78C37.31-87.7 45.36-95.75 55.29-95.75C65.21-95.75 73.26-87.7 73.26-77.78L73.26-35.9C73.26-25.98 65.21-17.93 55.29-17.93C45.36-17.93 37.31-25.98 37.31-35.9L37.31-77.78Z"/>
+  </g>
+  <g id="Bold-M" transform="matrix(1 0 0 1 2283.57 1126)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M64.63-1.45L64.63 16.06C64.63 20.23 61.25 23.6 57.08 23.6C52.92 23.6 49.54 20.23 49.54 16.06L49.54-1.45C49.54-5.61 52.92-8.99 57.08-8.99C61.25-8.99 64.63-5.61 64.63-1.45Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M93.01-41.38C96.03-41.38 98.47-38.93 98.47-35.91L98.47-35.91C98.47-12.9 79.82 5.75 56.81 5.75C33.8 5.75 15.15-12.9 15.15-35.91L15.15-35.91C15.15-38.93 17.59-41.38 20.61-41.38L25.19-41.38C28.2-41.38 30.65-38.93 30.65-35.91L30.65-35.91C30.65-21.46 42.36-9.75 56.81-9.75C71.26-9.75 82.97-21.46 82.97-35.91L82.97-35.91C82.97-38.93 85.42-41.38 88.44-41.38L93.01-41.38Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M24.69-9.38C18.73-16.58 15.15-25.83 15.15-35.91C15.15-38.93 17.59-41.38 20.61-41.38L25.18-41.38C28.2-41.38 30.65-38.93 30.65-35.91C30.65-30.04 32.58-24.62 35.85-20.25L24.69-9.38Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M38.66-77.48C38.66-87.36 46.66-95.36 56.54-95.36C66.41-95.36 74.41-87.36 74.41-77.48L74.41-35.91C74.41-26.04 66.41-18.04 56.54-18.04C46.66-18.04 38.66-26.04 38.66-35.91L38.66-77.48Z"/>
+  </g>
+  <g id="Semibold-M" transform="matrix(1 0 0 1 1992.14 1126)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M57.28-5.5L57.28 10.5C57.28 13.54 54.82 16 51.78 16C48.74 16 46.28 13.54 46.28 10.5L46.28-5.5C46.28-8.54 48.74-11 51.78-11C54.82-11 57.28-8.54 57.28-5.5Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M82.91-42C85.67-42 87.91-39.76 87.91-37.01L87.91-37.01C87.91-16.92 71.62-0.63 51.53-0.63C31.44-0.63 15.16-16.92 15.16-37.01L15.16-37.01C15.16-39.76 17.39-42 20.15-42L20.91-42C23.67-42 25.91-39.76 25.91-37.01L25.91-37.01C25.91-22.85 37.38-11.38 51.53-11.38C65.68-11.38 77.16-22.85 77.16-37.01L77.16-37.01C77.16-39.76 79.39-42 82.15-42L82.91-42Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M23.4-13.95C18.25-20.22 15.15-28.25 15.15-37.01C15.15-39.76 17.39-42 20.15-42L20.91-42C23.67-42 25.9-39.76 25.9-37.01C25.9-31.17 27.85-25.8 31.13-21.49L23.4-13.95Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M34.53-75.01C34.53-84.26 42.03-91.76 51.28-91.76C60.53-91.76 68.03-84.26 68.03-75.01L68.03-37.01C68.03-27.76 60.53-20.26 51.28-20.26C42.03-20.26 34.53-27.76 34.53-37.01L34.53-75.01Z"/>
+  </g>
+  <g id="Medium-M" transform="matrix(1 0 0 1 1696.06 1126)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M56.15-7.5L56.15 8.5C56.15 11.26 53.91 13.5 51.15 13.5C48.39 13.5 46.15 11.26 46.15 8.5L46.15-7.5C46.15-10.26 48.39-12.5 51.15-12.5C53.91-12.5 56.15-10.26 56.15-7.5Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M81.9-44C84.52-44 86.65-41.87 86.65-39.25L86.65-39.01C86.65-19.26 70.64-3.26 50.9-3.26C31.16-3.26 15.15-19.26 15.15-39.01L15.15-39.25C15.15-41.87 17.28-44 19.9-44L19.9-44C22.52-44 24.65-41.87 24.65-39.25L24.65-39.01C24.65-24.51 36.4-12.76 50.9-12.76C65.4-12.76 77.15-24.51 77.15-39.01L77.15-39.25C77.15-41.87 79.28-44 81.9-44L81.9-44Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M23.22-16.38C18.17-22.55 15.15-30.42 15.15-39.01L15.15-39.25C15.15-41.87 17.28-44 19.9-44C22.52-44 24.65-41.87 24.65-39.25L24.65-39.01C24.65-33 26.66-27.47 30.05-23.05L23.22-16.38Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M33.9-77.01C33.9-86.26 41.4-93.76 50.65-93.76C59.9-93.76 67.4-86.26 67.4-77.01L67.4-39.01C67.4-29.76 59.9-22.26 50.65-22.26C41.4-22.26 33.9-29.76 33.9-39.01L33.9-77.01Z"/>
+  </g>
+  <g id="Regular-M" transform="matrix(1 0 0 1 1400.1 1126)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M54.9-4.5L54.9 10.5C54.9 12.99 52.89 15 50.4 15C47.91 15 45.9 12.99 45.9 10.5L45.9-4.5C45.9-6.99 47.91-9 50.4-9C52.89-9 54.9-6.99 54.9-4.5Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M81.15-42C83.36-42 85.15-40.21 85.15-38L85.15-37.01C85.15-17.68 69.48-2.01 50.15-2.01C30.82-2.01 15.15-17.68 15.15-37.01L15.15-38C15.15-40.21 16.94-42 19.15-42L19.15-42C21.36-42 23.15-40.21 23.15-38L23.15-37.01C23.15-22.09 35.24-10.01 50.15-10.01C65.06-10.01 77.15-22.09 77.15-37.01L77.15-38C77.15-40.21 78.94-42 81.15-42L81.15-42Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M23.01-14.91C18.09-20.94 15.15-28.63 15.15-37.01L15.15-38C15.15-40.21 16.94-42 19.15-42C21.36-42 23.15-40.21 23.15-38L23.15-37.01C23.15-30.8 25.24-25.08 28.76-20.52L23.01-14.91Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M33.4-75.01C33.4-84.12 40.79-91.51 49.9-91.51C59.01-91.51 66.4-84.12 66.4-75.01L66.4-37.01C66.4-27.9 59.01-20.51 49.9-20.51C40.79-20.51 33.4-27.9 33.4-37.01L33.4-75.01Z"/>
+  </g>
+  <g id="Light-M" transform="matrix(1 0 0 1 1104.39 1126)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M52.9-5.5L52.9 9.5C52.9 11.43 51.33 13 49.4 13C47.47 13 45.9 11.43 45.9 9.5L45.9-5.5C45.9-7.43 47.47-9 49.4-9C51.33-9 52.9-7.43 52.9-5.5Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M80.15-43C81.81-43 83.15-41.66 83.15-40L83.15-38.01C83.15-19.23 67.93-4.01 49.15-4.01C30.37-4.01 15.15-19.23 15.15-38.01L15.15-40C15.15-41.66 16.49-43 18.15-43L18.15-43C19.81-43 21.15-41.66 21.15-40L21.15-38.01C21.15-22.54 33.69-10.01 49.15-10.01C64.61-10.01 77.15-22.54 77.15-38.01L77.15-40C77.15-41.66 78.49-43 80.15-43L80.15-43Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M22.72-16.61C17.99-22.45 15.15-29.9 15.15-38.01L15.15-40C15.15-41.66 16.49-43 18.15-43C19.81-43 21.15-41.66 21.15-40L21.15-38.01C21.15-31.53 23.35-25.56 27.04-20.82L22.72-16.61Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M32.4-76.01C32.4-85.12 39.79-92.51 48.9-92.51C58.01-92.51 65.4-85.12 65.4-76.01L65.4-38.01C65.4-28.9 58.01-21.51 48.9-21.51C39.79-21.51 32.4-28.9 32.4-38.01L32.4-76.01Z"/>
+  </g>
+  <g id="Thin-M" transform="matrix(1 0 0 1 808.177 1126)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M51.9-5.5L51.9 9.5C51.9 11.16 50.557 12.5 48.9 12.5C47.244 12.5 45.9 11.16 45.9 9.5L45.9-5.5C45.9-7.16 47.244-8.5 48.9-8.5C50.557-8.5 51.9-7.16 51.9-5.5Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M79.651-43C81.032-43 82.151-41.88 82.151-40.5L82.151-38.01C82.151-19.5 67.153-4.51 48.651-4.51C30.15-4.51 15.151-19.5 15.151-38.01L15.151-40.5C15.151-41.88 16.271-43 17.651-43L17.651-43C19.032-43 20.151-41.88 20.151-40.5L20.151-38.01C20.151-22.27 32.911-9.51 48.651-9.51C64.392-9.51 77.151-22.27 77.151-38.01L77.151-40.5C77.151-41.88 78.271-43 79.651-43L79.651-43Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M22.584-16.96C17.935-22.71 15.15-30.03 15.15-38.01L15.15-40.5C15.15-41.88 16.269-43 17.65-43C19.031-43 20.15-41.88 20.15-40.5L20.15-38.01C20.15-31.39 22.403-25.31 26.183-20.47L22.584-16.96Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M31.901-76.01C31.901-85.12 39.289-92.51 48.401-92.51C57.514-92.51 64.901-85.12 64.901-76.01L64.901-38.01C64.901-28.9 57.514-21.51 48.401-21.51C39.289-21.51 31.901-28.9 31.901-38.01L31.901-76.01Z"/>
+  </g>
+  <g id="Ultralight-M" transform="matrix(1 0 0 1 511.965 1126)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M50.901-7.5L50.901 7.5C50.901 8.88 49.782 10 48.401 10C47.02 10 45.901 8.88 45.901 7.5L45.901-7.5C45.901-8.88 47.02-10 48.401-10C49.782-10 50.901-8.88 50.901-7.5Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M79.152-45C80.257-45 81.152-44.1 81.152-43L81.152-40.01C81.152-21.78 66.377-7.01 48.152-7.01C29.927-7.01 15.152-21.78 15.152-40.01L15.152-43C15.152-44.1 16.047-45 17.152-45L17.152-45C18.257-45 19.152-44.1 19.152-43L19.152-40.01C19.152-23.99 32.136-11.01 48.152-11.01C64.168-11.01 77.152-23.99 77.152-40.01L77.152-43C77.152-44.1 78.047-45 79.152-45L79.152-45Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M22.444-19.31C17.882-24.97 15.15-32.17 15.15-40.01L15.15-43C15.15-44.1 16.046-45 17.15-45C18.255-45 19.15-44.1 19.15-43L19.15-40.01C19.15-33.26 21.456-27.05 25.323-22.12L22.444-19.31Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M31.402-78.01C31.402-87.12 38.789-94.51 47.902-94.51C57.015-94.51 64.402-87.12 64.402-78.01L64.402-40.01C64.402-30.9 57.015-23.51 47.902-23.51C38.789-23.51 31.402-30.9 31.402-40.01L31.402-78.01Z"/>
+  </g>
+  <g id="Black-S" transform="matrix(1 0 0 1 2883.31 696)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M57.2-10.492L57.2 4.527C57.2 8.1 54.31 10.996 50.73 10.996C47.16 10.996 44.26 8.1 44.26 4.527L44.26-10.492C44.26-14.065 47.16-16.961 50.73-16.961C54.31-16.961 57.2-14.065 57.2-10.492Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M81.16-44.758C83.75-44.758 85.85-42.659 85.85-40.07L85.85-40.07C85.85-20.548 70.02-4.722 50.5-4.722C30.98-4.722 15.15-20.548 15.15-40.07L15.15-40.07C15.15-42.659 17.25-44.758 19.84-44.758L22.96-44.758C25.55-44.758 27.65-42.659 27.65-40.07L27.65-40.07C27.65-27.452 37.88-17.222 50.5-17.222C63.12-17.222 73.35-27.451 73.35-40.07L73.35-40.07C73.35-42.659 75.45-44.758 78.04-44.758L81.16-44.758Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M23.22-17.581C18.18-23.692 15.15-31.527 15.15-40.07C15.15-42.659 17.25-44.758 19.84-44.758L22.96-44.758C25.55-44.758 27.65-42.659 27.65-40.07C27.65-34.923 29.35-30.173 32.22-26.353L23.22-17.581Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M34.25-75.736C34.25-84.58 41.42-91.75 50.27-91.75C59.11-91.75 66.28-84.58 66.28-75.736L66.28-40.067C66.28-31.223 59.11-24.053 50.27-24.053C41.42-24.053 34.25-31.223 34.25-40.067L34.25-75.736Z"/>
+  </g>
+  <g id="Heavy-S" transform="matrix(1 0 0 1 2587.22 696)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M56.56-10.158L56.56 4.534C56.56 8.101 53.67 10.993 50.1 10.993C46.53 10.993 43.64 8.101 43.64 4.534L43.64-10.158C43.64-13.725 46.53-16.617 50.1-16.617C53.67-16.617 56.56-13.725 56.56-10.158Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M80-43.672C82.53-43.672 84.59-41.619 84.59-39.086L84.59-39.086C84.59-19.913 69.04-4.371 49.87-4.371C30.7-4.371 15.15-19.913 15.15-39.086L15.15-39.086C15.15-41.619 17.21-43.672 19.74-43.672L23.07-43.672C25.6-43.672 27.65-41.619 27.65-39.086L27.65-39.086C27.65-26.817 37.6-16.871 49.87-16.871C62.14-16.871 72.09-26.817 72.09-39.086L72.09-39.086C72.09-41.619 74.14-43.672 76.67-43.672L80-43.672Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M23.09-16.991C18.13-22.994 15.15-30.692 15.15-39.086C15.15-41.619 17.21-43.672 19.74-43.672L23.07-43.672C25.6-43.672 27.65-41.619 27.65-39.086C27.65-34.088 29.3-29.475 32.09-25.763L23.09-16.991Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M33.87-73.981C33.87-82.69 40.93-89.75 49.64-89.75C58.35-89.75 65.41-82.69 65.41-73.981L65.41-39.088C65.41-30.379 58.35-23.319 49.64-23.319C40.93-23.319 33.87-30.379 33.87-39.088L33.87-73.981Z"/>
+  </g>
+  <g id="Bold-S" transform="matrix(1 0 0 1 2292.65 696)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M53.39-10.483L53.39 3.556C53.39 6.56 50.95 8.995 47.95 8.995C44.95 8.995 42.51 6.56 42.51 3.556L42.51-10.483C42.51-13.487 44.95-15.922 47.95-15.922C50.95-15.922 53.39-13.487 53.39-10.483Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M75.92-42.508C78.34-42.508 80.31-40.546 80.31-38.126L80.31-38.126C80.31-20.135 65.72-5.551 47.73-5.551C29.74-5.551 15.16-20.135 15.16-38.126L15.16-38.126C15.16-40.546 17.12-42.508 19.54-42.508L21.52-42.508C23.94-42.508 25.91-40.546 25.91-38.126L25.91-38.126C25.91-26.072 35.68-16.301 47.73-16.301C59.78-16.301 69.56-26.072 69.56-38.126L69.56-38.126C69.56-40.546 71.52-42.508 73.94-42.508L75.92-42.508Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M22.57-17.431C17.94-23.058 15.15-30.267 15.15-38.126C15.15-40.546 17.11-42.508 19.53-42.508L21.52-42.508C23.94-42.508 25.9-40.546 25.9-38.126C25.9-33.187 27.54-28.631 30.31-24.974L22.57-17.431Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M32.23-71.471C32.23-79.909 39.07-86.75 47.51-86.75C55.95-86.75 62.79-79.909 62.79-71.471L62.79-38.128C62.79-29.69 55.95-22.849 47.51-22.849C39.07-22.849 32.23-29.69 32.23-38.128L32.23-71.471Z"/>
+  </g>
+  <g id="Semibold-S" transform="matrix(1 0 0 1 1997.83 696)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M50.79-3L50.79 2C50.79 4.76142 48.5514 7 45.79 7L45.79 7C43.0286 7 40.79 4.76142 40.79 2L40.79-8C40.79-10.7614 43.0286-13 45.79-13L45.79-13C48.5514-13 50.79-10.7614 50.79-8Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M72.35-42.344C74.65-42.344 76.53-40.473 76.53-38.166L76.53-38.166C76.53-21.219 62.79-7.481 45.84-7.481C28.89-7.481 15.15-21.219 15.15-38.166L15.15-38.166C15.15-40.473 17.03-42.344 19.33-42.344L20.48-42.344C22.78-42.344 24.65-40.473 24.65-38.166L24.65-38.166C24.65-26.465 34.14-16.981 45.84-16.981C57.54-16.981 67.03-26.465 67.03-38.166L67.03-38.166C67.03-40.473 68.9-42.344 71.2-42.344L72.35-42.344Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M22.12-18.697C17.77-23.994 15.15-30.775 15.15-38.166C15.15-40.473 17.02-42.344 19.33-42.344L20.48-42.344C22.78-42.344 24.65-40.473 24.65-38.166C24.65-33.355 26.26-28.919 28.96-25.363L22.12-18.697Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M30.84-69.961C30.84-78.129 37.46-84.75 45.63-84.75C53.8-84.75 60.42-78.129 60.42-69.961L60.42-38.169C60.42-30.001 53.8-23.38 45.63-23.38C37.46-23.38 30.84-30.001 30.84-38.169L30.84-69.961Z"/>
+  </g>
+  <g id="Medium-S" transform="matrix(1 0 0 1 1702.51 696)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M49.06-12.654L49.06-0.411C49.06 2.023 47.09 3.997 44.66 3.997C42.22 3.997 40.25 2.023 40.25-0.411L40.25-12.654C40.25-15.089 42.22-17.062 44.66-17.062C47.09-17.062 49.06-15.089 49.06-12.654Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M69.76-43.266C71.97-43.266 73.76-41.475 73.76-39.266L73.76-39.19C73.76-23.006 60.64-9.887 44.45-9.887C28.27-9.887 15.15-23.006 15.15-39.19L15.15-39.266C15.15-41.475 16.94-43.266 19.15-43.266L19.15-43.266C21.36-43.266 23.15-41.475 23.15-39.266L23.15-39.19C23.15-27.424 32.69-17.887 44.45-17.887C56.22-17.887 65.76-27.424 65.76-39.19L65.76-39.266C65.76-41.475 67.55-43.266 69.76-43.266L69.76-43.266Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M21.77-20.64C17.63-25.692 15.15-32.151 15.15-39.19L15.15-39.266C15.15-41.475 16.94-43.266 19.15-43.266C21.36-43.266 23.15-41.475 23.15-39.266L23.15-39.19C23.15-34.324 24.78-29.839 27.53-26.253L21.77-20.64Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M29.95-70.205C29.95-78.1 36.35-84.5 44.25-84.5C52.14-84.5 58.54-78.1 58.54-70.205L58.54-39.189C58.54-31.295 52.14-24.895 44.25-24.895C36.35-24.895 29.95-31.295 29.95-39.189L29.95-70.205Z"/>
+  </g>
+  <g id="Regular-S" transform="matrix(1 0 0 1 1405.53 696)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M48.36-13.29L48.36-0.435C48.36 1.459 46.83 2.994 44.93 2.994C43.04 2.994 41.51 1.459 41.51-0.435L41.51-13.29C41.51-15.184 43.04-16.719 44.93-16.719C46.83-16.719 48.36-15.184 48.36-13.29Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M71.29-45.43C72.95-45.43 74.29-44.087 74.29-42.43L74.29-41.15C74.29-24.82 61.05-11.582 44.72-11.582C28.39-11.582 15.15-24.82 15.15-41.15L15.15-42.43C15.15-44.087 16.5-45.43 18.15-45.43L18.15-45.43C19.81-45.43 21.15-44.087 21.15-42.43L21.15-41.15C21.15-28.134 31.7-17.582 44.72-17.582C57.74-17.582 68.29-28.134 68.29-41.15L68.29-42.43C68.29-44.087 69.63-45.43 71.29-45.43L71.29-45.43Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M21.76-22.513C17.63-27.599 15.15-34.085 15.15-41.15L15.15-42.43C15.15-44.087 16.49-45.43 18.15-45.43C19.81-45.43 21.15-44.087 21.15-42.43L21.15-41.15C21.15-35.715 22.99-30.709 26.08-26.722L21.76-22.513Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M29.72-73.716C29.72-81.881 36.34-88.5 44.51-88.5C52.67-88.5 59.29-81.881 59.29-73.716L59.29-41.149C59.29-32.983 52.67-26.364 44.51-26.364C36.34-26.364 29.72-32.983 29.72-41.149L29.72-73.716Z"/>
+  </g>
+  <g id="Light-S" transform="matrix(1 0 0 1 1111.85 696)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M44.77-13.026L44.77-1.395C44.77 0.199 43.48 1.492 41.88 1.492C40.29 1.492 39 0.199 39-1.395L39-13.026C39-14.621 40.29-15.914 41.88-15.914C43.48-15.914 44.77-14.621 44.77-13.026Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M65.73-42.102C67.11-42.102 68.23-40.982 68.23-39.602L68.23-38.229C68.23-23.573 56.35-11.692 41.69-11.692C27.04-11.692 15.15-23.573 15.15-38.229L15.15-39.602C15.15-40.982 16.27-42.102 17.65-42.102L17.65-42.102C19.03-42.102 20.15-40.982 20.15-39.602L20.15-38.229C20.15-26.335 29.8-16.692 41.69-16.692C53.59-16.692 63.23-26.335 63.23-38.229L63.23-39.602C63.23-40.982 64.35-42.102 65.73-42.102L65.73-42.102Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M21.07-21.518C17.37-26.08 15.15-31.895 15.15-38.229L15.15-39.602C15.15-40.982 16.27-42.102 17.65-42.102C19.03-42.102 20.15-40.982 20.15-39.602L20.15-38.229C20.15-33.254 21.84-28.672 24.67-25.025L21.07-21.518Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M27.69-67.695C27.69-75.319 33.87-81.5 41.5-81.5C49.12-81.5 55.3-75.319 55.3-67.695L55.3-38.23C55.3-30.606 49.12-24.425 41.5-24.425C33.87-24.425 27.69-30.606 27.69-38.23L27.69-67.695Z"/>
+  </g>
+  <g id="Thin-S" transform="matrix(1 0 0 1 816.273 696)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M43.121-12.708L43.121-1.384C43.121-0.07 42.056 0.994 40.743 0.994C39.43 0.994 38.366-0.07 38.366-1.384L38.366-12.708C38.366-14.021 39.43-15.086 40.743-15.086C42.056-15.086 43.121-14.021 43.121-12.708Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M63.959-41.023C65.064-41.023 65.959-40.128 65.959-39.023L65.959-37.253C65.959-23.222 54.585-11.848 40.555-11.848C26.524-11.848 15.15-23.222 15.15-37.253L15.15-39.023C15.15-40.128 16.045-41.023 17.15-41.023L17.15-41.023C18.254-41.023 19.15-40.128 19.15-39.023L19.15-37.253C19.15-25.432 28.733-15.848 40.555-15.848C52.376-15.848 61.959-25.432 61.959-37.253L61.959-39.023C61.959-40.128 62.855-41.023 63.959-41.023L63.959-41.023Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M20.795-21.286C17.265-25.649 15.151-31.204 15.151-37.253L15.151-39.023C15.151-40.128 16.046-41.023 17.151-41.023C18.255-41.023 19.151-40.128 19.151-39.023L19.151-37.253C19.151-32.291 20.839-27.722 23.674-24.092L20.795-21.286Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M26.806-65.94C26.806-73.429 32.877-79.5 40.366-79.5C47.855-79.5 53.926-73.429 53.926-65.94L53.926-37.25C53.926-29.761 47.855-23.69 40.366-23.69C32.877-23.69 26.806-29.761 26.806-37.25L26.806-65.94Z"/>
+  </g>
+  <g id="Ultralight-S" transform="matrix(1 0 0 1 519.562 696)">
+   <path class="monochrome-0 multicolor-0:custom hierarchical-0:primary SFSymbolsPreview34A853" d="M43.121-12.708L43.121-1.384C43.121-0.07 42.056 0.994 40.743 0.994C39.43 0.994 38.366-0.07 38.366-1.384L38.366-12.708C38.366-14.021 39.43-15.086 40.743-15.086C42.056-15.086 43.121-14.021 43.121-12.708Z"/>
+   <path class="monochrome-1 multicolor-1:custom hierarchical-1:primary SFSymbolsPreviewEA4335" d="M63.959-41.023C65.064-41.023 65.959-40.128 65.959-39.023L65.959-37.253C65.959-23.222 54.585-11.848 40.555-11.848C26.524-11.848 15.15-23.222 15.15-37.253L15.15-39.023C15.15-40.128 16.045-41.023 17.15-41.023L17.15-41.023C18.254-41.023 19.15-40.128 19.15-39.023L19.15-37.253C19.15-25.432 28.733-15.848 40.555-15.848C52.376-15.848 61.959-25.432 61.959-37.253L61.959-39.023C61.959-40.128 62.855-41.023 63.959-41.023L63.959-41.023Z"/>
+   <path class="monochrome-2 multicolor-2:custom hierarchical-2:primary SFSymbolsPreviewFBBC04" d="M20.795-21.286C17.265-25.649 15.151-31.204 15.151-37.253L15.151-39.023C15.151-40.128 16.046-41.023 17.151-41.023C18.255-41.023 19.151-40.128 19.151-39.023L19.151-37.253C19.151-32.291 20.839-27.722 23.674-24.092L20.795-21.286Z"/>
+   <path class="monochrome-3 multicolor-3:custom hierarchical-3:primary SFSymbolsPreview4183F0" d="M26.806-65.94C26.806-73.429 32.877-79.5 40.366-79.5C47.855-79.5 53.926-73.429 53.926-65.94L53.926-37.25C53.926-29.761 47.855-23.69 40.366-23.69C32.877-23.69 26.806-29.761 26.806-37.25L26.806-65.94Z"/>
+  </g>
+ </g>
+</svg>
diff --git a/ios/chrome/widget_kit_extension/widget_constants.swift b/ios/chrome/widget_kit_extension/widget_constants.swift
index f57a8846..7e7fd3f8 100644
--- a/ios/chrome/widget_kit_extension/widget_constants.swift
+++ b/ios/chrome/widget_kit_extension/widget_constants.swift
@@ -21,6 +21,7 @@
       URL(string: "chromewidgetkit://quick-actions-widget/lens")!
     static let isGoogleDefaultSearchEngineKey = "isGoogleDefaultSearchEngine"
     static let enableLensInWidgetKey = "enableLensInWidget"
+    static let enableColorLensAndVoiceIconsInWidgetKey = "enableColorLensAndVoiceIconsInWidget"
   }
   struct DinoGameWidget {
     static let url = URL(string: "chromewidgetkit://dino-game-widget/game")!
diff --git a/ios/third_party/earl_grey2/src b/ios/third_party/earl_grey2/src
index 6acfcc1..96b6332 160000
--- a/ios/third_party/earl_grey2/src
+++ b/ios/third_party/earl_grey2/src
@@ -1 +1 @@
-Subproject commit 6acfcc10d3cbbd19b428cf5e8fa26d86b383a1ea
+Subproject commit 96b6332c4a2a8881231e3e77a28483dfc228102d
diff --git a/media/capture/video/chromeos/camera_device_delegate.cc b/media/capture/video/chromeos/camera_device_delegate.cc
index a417aa7..a49a2a04 100644
--- a/media/capture/video/chromeos/camera_device_delegate.cc
+++ b/media/capture/video/chromeos/camera_device_delegate.cc
@@ -291,22 +291,24 @@
 CameraDeviceDelegate::CameraDeviceDelegate(
     VideoCaptureDeviceDescriptor device_descriptor,
     CameraHalDelegate* camera_hal_delegate,
-    scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner)
+    scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
     : device_descriptor_(device_descriptor),
       camera_hal_delegate_(camera_hal_delegate),
-      ipc_task_runner_(std::move(ipc_task_runner)) {}
-
-CameraDeviceDelegate::~CameraDeviceDelegate() {
-  if (camera_effect_observer_added_) {
-    // TODO(1446850): CameraDeviceDelegate should be removed from the
-    // CameraEffectObserver list when CameraDeviceDelegate::StopAndDeAllocate
-    // was called. Check the cases where StopAndDeallocate is not called before
-    // destructor.
-    CameraHalDispatcherImpl::GetInstance()->RemoveCameraEffectObserver(this);
-    camera_effect_observer_added_ = false;
+      ipc_task_runner_(std::move(ipc_task_runner)) {
+  if (!ash::features::IsVcWebApiEnabled()) {
+    return;
   }
+  camera_effects_observer_ = base::SequenceBound<CrosCameraEffectsObserver>(
+      ui_task_runner,
+      base::BindPostTask(
+          ipc_task_runner_,
+          base::BindRepeating(&CameraDeviceDelegate::OnCameraEffectsChanged,
+                              GetWeakPtr())));
 }
 
+CameraDeviceDelegate::~CameraDeviceDelegate() = default;
+
 void CameraDeviceDelegate::AllocateAndStart(
     const base::flat_map<ClientType, VideoCaptureParams>& params,
     CameraDeviceContext* device_context) {
@@ -334,7 +336,6 @@
   is_set_sharpness_ = false;
   is_set_tilt_ = false;
   is_set_zoom_ = false;
-  camera_effect_observer_added_ = false;
 
   chrome_capture_params_ = params;
   device_context_ = device_context;
@@ -403,10 +404,6 @@
     // In case of Mojo connection error the device may be stopped before
     // StopAndDeAllocate is called; in case of device open failure, the state
     // is set to kError and |request_manager_| is uninitialized.
-    if (camera_effect_observer_added_) {
-      CameraHalDispatcherImpl::GetInstance()->RemoveCameraEffectObserver(this);
-      camera_effect_observer_added_ = false;
-    }
     std::move(device_close_callback).Run();
     return;
   }
@@ -804,10 +801,6 @@
   if (request_manager_) {
     request_manager_->RemoveResultMetadataObserver(this);
   }
-  if (camera_effect_observer_added_) {
-    CameraHalDispatcherImpl::GetInstance()->RemoveCameraEffectObserver(this);
-    camera_effect_observer_added_ = false;
-  }
   ResetMojoInterface();
   device_context_ = nullptr;
   current_blob_resolution_.SetSize(0, 0);
@@ -890,16 +883,6 @@
       std::move(callback_ops),
       base::BindOnce(&CameraDeviceDelegate::OnInitialized, GetWeakPtr()));
   request_manager_->AddResultMetadataObserver(this);
-  // The callback passed to CameraHalDispatcherImpl will be called on a
-  // different thread inside CameraHalDispatcherImpl, so we need always
-  // post the callback onto current task runner.
-  if (ash::features::IsVcWebApiEnabled() && !camera_effect_observer_added_) {
-    CameraHalDispatcherImpl::GetInstance()->AddCameraEffectObserver(
-        this, base::BindPostTaskToCurrentDefault(base::BindOnce(
-                  &CameraDeviceDelegate::OnCameraEffectObserverAdded,
-                  weak_ptr_factory_.GetWeakPtr())));
-    camera_effect_observer_added_ = true;
-  }
 
   // For Intel IPU6 platform, set power mode to high quality for CCA and low
   // power mode for others.
@@ -1543,27 +1526,15 @@
   }
 }
 
-void CameraDeviceDelegate::OnCameraEffectObserverAdded(
-    cros::mojom::EffectsConfigPtr current_effects) {
+void CameraDeviceDelegate::OnCameraEffectsChanged(
+    cros::mojom::EffectsConfigPtr new_effects) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
-  current_effects_ = std::move(current_effects);
-}
 
-void CameraDeviceDelegate::OnCameraEffectChanged(
-    const cros::mojom::EffectsConfigPtr& new_effects) {
-  if (!ipc_task_runner_->BelongsToCurrentThread()) {
-    ipc_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&CameraDeviceDelegate::OnCameraEffectChanged,
-                                  GetWeakPtr(), new_effects.Clone()));
-    return;
-  }
-
-  DCHECK(ipc_task_runner_->BelongsToCurrentThread());
   if (!current_effects_.is_null() &&
       current_effects_->blur_enabled != new_effects->blur_enabled) {
     device_context_->OnCaptureConfigurationChanged();
   }
-  current_effects_ = new_effects.Clone();
+  current_effects_ = std::move(new_effects);
 }
 
 void CameraDeviceDelegate::DoGetPhotoState(
diff --git a/media/capture/video/chromeos/camera_device_delegate.h b/media/capture/video/chromeos/camera_device_delegate.h
index 38888c3a..e769b02 100644
--- a/media/capture/video/chromeos/camera_device_delegate.h
+++ b/media/capture/video/chromeos/camera_device_delegate.h
@@ -13,7 +13,9 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/threading/sequence_bound.h"
 #include "media/capture/video/chromeos/camera_device_context.h"
+#include "media/capture/video/chromeos/camera_effects_observer.h"
 #include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
 #include "media/capture/video/chromeos/capture_metadata_dispatcher.h"
 #include "media/capture/video/chromeos/mojom/camera3.mojom.h"
@@ -130,15 +132,15 @@
 // second client for recording stream.
 // The second client will be a virtual camera device which is only used in CCA.
 class CAPTURE_EXPORT CameraDeviceDelegate final
-    : public CaptureMetadataDispatcher::ResultMetadataObserver,
-      public media::CameraEffectObserver {
+    : public CaptureMetadataDispatcher::ResultMetadataObserver {
  public:
   CameraDeviceDelegate() = delete;
 
   CameraDeviceDelegate(
       VideoCaptureDeviceDescriptor device_descriptor,
       CameraHalDelegate* camera_hal_delegate,
-      scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
 
   CameraDeviceDelegate(const CameraDeviceDelegate&) = delete;
   CameraDeviceDelegate& operator=(const CameraDeviceDelegate&) = delete;
@@ -165,6 +167,8 @@
 
   base::WeakPtr<CameraDeviceDelegate> GetWeakPtr();
 
+  void OnCameraEffectsChanged(cros::mojom::EffectsConfigPtr new_effects);
+
  private:
   class StreamCaptureInterfaceImpl;
 
@@ -255,14 +259,6 @@
       uint32_t frame_number,
       const cros::mojom::CameraMetadataPtr& result_metadata) final;
 
-  // media::CameraEffectObserver AddObserver callback.
-  void OnCameraEffectObserverAdded(
-      cros::mojom::EffectsConfigPtr current_effects);
-
-  // media::CameraEffectObserver implementation.
-  void OnCameraEffectChanged(
-      const cros::mojom::EffectsConfigPtr& new_effects) final;
-
   void DoGetPhotoState(VideoCaptureDevice::GetPhotoStateCallback callback);
 
   // Gets the target frame rate range as std::pair<min, max>.
@@ -333,9 +329,6 @@
   bool is_set_tilt_;
   bool is_set_zoom_;
 
-  // Whether |this| is added to camera effect observer list.
-  bool camera_effect_observer_added_;
-
   std::vector<base::OnceClosure> get_photo_state_queue_;
   bool use_digital_zoom_;
   float ae_compensation_step_;
@@ -351,6 +344,8 @@
   ResultMetadata result_metadata_;
   gfx::Rect active_array_size_;
 
+  base::SequenceBound<CrosCameraEffectsObserver> camera_effects_observer_;
+
   base::WeakPtrFactory<CameraDeviceDelegate> weak_ptr_factory_{this};
 };
 
diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
index be8de7d..8af0d7b 100644
--- a/media/capture/video/chromeos/camera_device_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
@@ -142,7 +142,8 @@
  public:
   CameraDeviceDelegateTest()
       : mock_camera_device_receiver_(&mock_camera_device_),
-        device_delegate_thread_("DeviceDelegateThread") {}
+        device_delegate_thread_("DeviceDelegateThread"),
+        ui_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {}
 
   CameraDeviceDelegateTest(const CameraDeviceDelegateTest&) = delete;
   CameraDeviceDelegateTest& operator=(const CameraDeviceDelegateTest&) = delete;
@@ -150,9 +151,7 @@
   void SetUp() override {
     VideoCaptureDeviceFactoryChromeOS::SetGpuBufferManager(
         &mock_gpu_memory_buffer_manager_);
-    camera_hal_delegate_ = std::make_unique<CameraHalDelegate>(
-        base::ThreadPool::CreateSingleThreadTaskRunner(
-            {}, base::SingleThreadTaskRunnerThreadMode::DEDICATED));
+    camera_hal_delegate_ = std::make_unique<CameraHalDelegate>(ui_task_runner_);
     if (!camera_hal_delegate_->Init()) {
       LOG(ERROR) << "Failed to initialize CameraHalDelegate";
       camera_hal_delegate_.reset();
@@ -165,7 +164,11 @@
         mock_camera_module_.GetPendingRemote());
   }
 
-  void TearDown() override {}
+  void TearDown() override {
+    camera_device_delegate_.reset();
+    camera_hal_delegate_.reset();
+    task_environment_.RunUntilIdle();
+  }
 
   void AllocateDevice() {
     ASSERT_FALSE(device_delegate_thread_.IsRunning());
@@ -185,7 +188,7 @@
 
     camera_device_delegate_ = std::make_unique<CameraDeviceDelegate>(
         devices_info[0].descriptor, camera_hal_delegate_.get(),
-        device_delegate_thread_.task_runner());
+        device_delegate_thread_.task_runner(), ui_task_runner_);
   }
 
   void GetNumberOfFakeCameras(
@@ -538,6 +541,8 @@
 
   base::Thread device_delegate_thread_;
 
+  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+
   std::unique_ptr<CameraDeviceContext> device_context_;
   ClientType client_type_;
 
diff --git a/media/capture/video/chromeos/camera_hal_delegate.cc b/media/capture/video/chromeos/camera_hal_delegate.cc
index 9263120..24022b64 100644
--- a/media/capture/video/chromeos/camera_hal_delegate.cc
+++ b/media/capture/video/chromeos/camera_hal_delegate.cc
@@ -22,17 +22,18 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
-#include "base/system/system_monitor.h"
 #include "base/task/bind_post_task.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/unguessable_token.h"
-#include "chromeos/dbus/power/power_manager_client.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
 #include "components/device_event_log/device_event_log.h"
 #include "media/capture/video/chromeos/camera_buffer_factory.h"
 #include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
 #include "media/capture/video/chromeos/camera_metadata_utils.h"
+#include "media/capture/video/chromeos/mojom/system_event_monitor.mojom.h"
 #include "media/capture/video/chromeos/video_capture_device_chromeos_delegate.h"
 #include "media/capture/video/chromeos/video_capture_device_chromeos_halv3.h"
+#include "third_party/cros_system_api/mojo/service_constants.h"
 
 namespace media {
 
@@ -133,15 +134,6 @@
   });
 }
 
-void NotifyVideoCaptureDevicesChanged() {
-  base::SystemMonitor* monitor = base::SystemMonitor::Get();
-  // |monitor| might be nullptr in unittest.
-  if (monitor) {
-    monitor->ProcessDevicesChanged(
-        base::SystemMonitor::DeviceType::DEVTYPE_VIDEO_CAPTURE);
-  }
-}
-
 base::flat_set<int32_t> GetAvailableFramerates(
     const cros::mojom::CameraInfoPtr& camera_info) {
   base::flat_set<int32_t> candidates;
@@ -170,67 +162,86 @@
 
 }  // namespace
 
-class CameraHalDelegate::PowerManagerClientProxy
-    : public chromeos::PowerManagerClient::Observer {
+class CameraHalDelegate::SystemEventMonitorProxy
+    : public cros::mojom::CrosLidObserver {
  public:
-  PowerManagerClientProxy() = default;
-  PowerManagerClientProxy(const PowerManagerClientProxy&) = delete;
-  PowerManagerClientProxy& operator=(const PowerManagerClientProxy&) = delete;
-
-  void Init(scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
-    ui_task_runner_ = std::move(ui_task_runner);
-    ui_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&PowerManagerClientProxy::InitOnDBusThread,
-                                  GetWeakPtr()));
-  }
-
-  void Shutdown() {
+  explicit SystemEventMonitorProxy(
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
+      : ui_task_runner_(std::move(ui_task_runner)) {
     ui_task_runner_->PostTask(
         FROM_HERE,
-        base::BindOnce(&PowerManagerClientProxy::ShutdownOnDBusThread,
+        base::BindOnce(&SystemEventMonitorProxy::InitOnUIThread, GetWeakPtr()));
+  }
+
+  SystemEventMonitorProxy(const SystemEventMonitorProxy&) = delete;
+  SystemEventMonitorProxy& operator=(const SystemEventMonitorProxy&) = delete;
+
+  ~SystemEventMonitorProxy() override {
+    DCHECK(ui_task_runner_->BelongsToCurrentThread());
+  }
+
+  void NotifyVideoCaptureDevicesChanged() {
+    ui_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&SystemEventMonitorProxy::
+                           NotifyVideoCaptureDevicesChangedOnUIThread,
                        GetWeakPtr()));
   }
 
-  chromeos::PowerManagerClient::LidState GetLidState() { return lid_state_; }
-
- private:
-  void InitOnDBusThread() {
-    DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
-    chromeos::PowerManagerClient* power_manager_client =
-        chromeos::PowerManagerClient::Get();
-    // power_manager_client may be NULL in unittests.
-    if (power_manager_client)
-      power_manager_client->AddObserver(this);
-  }
-
-  void ShutdownOnDBusThread() {
-    DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
-    chromeos::PowerManagerClient* power_manager_client =
-        chromeos::PowerManagerClient::Get();
-    // power_manager_client may be NULL in unittests.
-    if (power_manager_client)
-      power_manager_client->RemoveObserver(this);
-  }
-
-  // chromeos::PowerManagerClient::Observer:
-  void LidEventReceived(chromeos::PowerManagerClient::LidState state,
-                        base::TimeTicks timestamp) final {
-    if (lid_state_ != state) {
-      lid_state_ = state;
-      NotifyVideoCaptureDevicesChanged();
+  void OnLidStateChanged(cros::mojom::LidState state) override {
+    bool is_lid_state_changed = false;
+    {
+      base::AutoLock lock(lid_lock_);
+      if (lid_state_ != state) {
+        lid_state_ = state;
+        is_lid_state_changed = true;
+      }
+    }
+    if (is_lid_state_changed) {
+      NotifyVideoCaptureDevicesChangedOnUIThread();
     }
   }
 
-  base::WeakPtr<CameraHalDelegate::PowerManagerClientProxy> GetWeakPtr() {
+  cros::mojom::LidState GetLidState() {
+    base::AutoLock lock(lid_lock_);
+    return lid_state_;
+  }
+
+ private:
+  void InitOnUIThread() {
+    DCHECK(ui_task_runner_->BelongsToCurrentThread());
+    if (!ash::mojo_service_manager::IsServiceManagerBound()) {
+      return;
+    }
+    ash::mojo_service_manager::GetServiceManagerProxy()->Request(
+        /*service_name=*/chromeos::mojo_services::kCrosSystemEventMonitor,
+        std::nullopt, monitor_.BindNewPipeAndPassReceiver().PassPipe());
+    monitor_->AddLidObserver(receiver_.BindNewPipeAndPassRemote());
+  }
+
+  void NotifyVideoCaptureDevicesChangedOnUIThread() {
+    DCHECK(ui_task_runner_->BelongsToCurrentThread());
+    if (!monitor_.is_bound()) {
+      return;
+    }
+    monitor_->NotifyDeviceChanged(cros::mojom::DeviceType::kVideoCapture);
+  }
+
+  base::WeakPtr<CameraHalDelegate::SystemEventMonitorProxy> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
   }
 
+  base::Lock lid_lock_;
+  cros::mojom::LidState lid_state_ GUARDED_BY(lid_lock_) =
+      cros::mojom::LidState::kNotPresent;
+
   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
 
-  chromeos::PowerManagerClient::LidState lid_state_ =
-      chromeos::PowerManagerClient::LidState::OPEN;
+  mojo::Remote<cros::mojom::CrosSystemEventMonitor> monitor_;
 
-  base::WeakPtrFactory<CameraHalDelegate::PowerManagerClientProxy>
+  mojo::Receiver<cros::mojom::CrosLidObserver> receiver_{this};
+
+  base::WeakPtrFactory<CameraHalDelegate::SystemEventMonitorProxy>
       weak_ptr_factory_{this};
 };
 
@@ -288,10 +299,9 @@
       camera_hal_ipc_thread_("CameraHalIpcThread"),
       camera_module_callbacks_(this),
       vcd_delegate_map_(new VideoCaptureDeviceDelegateMap()),
-      power_manager_client_proxy_(new PowerManagerClientProxy()),
+      system_event_monitor_proxy_(new SystemEventMonitorProxy(ui_task_runner)),
       ui_task_runner_(std::move(ui_task_runner)) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
-  power_manager_client_proxy_->Init(ui_task_runner_);
 }
 
 bool CameraHalDelegate::Init() {
@@ -322,9 +332,8 @@
   }
   camera_hal_ipc_thread_.Stop();
 
-  power_manager_client_proxy_->Shutdown();
   ui_task_runner_->DeleteSoon(FROM_HERE,
-                              std::move(power_manager_client_proxy_));
+                              std::move(system_event_monitor_proxy_));
 }
 
 bool CameraHalDelegate::RegisterCameraClient() {
@@ -558,6 +567,7 @@
   }
   // TODO(jcliang): Remove this after JS API supports query camera facing
   // (http://crbug.com/543997).
+  cros::mojom::LidState lid_state = system_event_monitor_proxy_->GetLidState();
   std::sort(
       devices_info.begin(), devices_info.end(),
       [&](const VideoCaptureDeviceInfo& a, const VideoCaptureDeviceInfo& b) {
@@ -565,9 +575,7 @@
           return vcd_info.descriptor.facing ==
                  VideoFacingMode::MEDIA_VIDEO_FACING_NONE;
         };
-        chromeos::PowerManagerClient::LidState lid_state =
-            power_manager_client_proxy_->GetLidState();
-        if (lid_state == chromeos::PowerManagerClient::LidState::CLOSED) {
+        if (lid_state == cros::mojom::LidState::kClosed) {
           if (IsExternalCamera(a) == IsExternalCamera(b))
             return a.descriptor < b.descriptor;
           return IsExternalCamera(a);
@@ -974,4 +982,8 @@
   return camera_module_has_been_set_.TimedWait(base::Seconds(10));
 }
 
+void CameraHalDelegate::NotifyVideoCaptureDevicesChanged() {
+  system_event_monitor_proxy_->NotifyVideoCaptureDevicesChanged();
+}
+
 }  // namespace media
diff --git a/media/capture/video/chromeos/camera_hal_delegate.h b/media/capture/video/chromeos/camera_hal_delegate.h
index 68f946df..a6eb1250 100644
--- a/media/capture/video/chromeos/camera_hal_delegate.h
+++ b/media/capture/video/chromeos/camera_hal_delegate.h
@@ -132,9 +132,11 @@
   bool WaitForCameraModuleReadyForTesting();
 
  private:
-  class PowerManagerClientProxy;
+  class SystemEventMonitorProxy;
   class VideoCaptureDeviceDelegateMap;
 
+  void NotifyVideoCaptureDevicesChanged();
+
   void OnRegisteredCameraHalClient(int32_t result);
 
   void GetSupportedFormats(const cros::mojom::CameraInfoPtr& camera_info,
@@ -270,8 +272,7 @@
 
   std::vector<std::unique_ptr<CameraClientObserver>> local_client_observers_;
 
-  // Proxy for communicating with PowerManagerClient.
-  std::unique_ptr<PowerManagerClientProxy> power_manager_client_proxy_;
+  std::unique_ptr<SystemEventMonitorProxy> system_event_monitor_proxy_;
 
   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
 };
diff --git a/media/capture/video/chromeos/camera_hal_delegate_unittest.cc b/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
index 48eea7c..24052f8 100644
--- a/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
@@ -48,8 +48,7 @@
     VideoCaptureDeviceFactoryChromeOS::SetGpuBufferManager(
         &mock_gpu_memory_buffer_manager_);
     camera_hal_delegate_ = std::make_unique<CameraHalDelegate>(
-        base::ThreadPool::CreateSingleThreadTaskRunner(
-            {}, base::SingleThreadTaskRunnerThreadMode::DEDICATED));
+        base::SingleThreadTaskRunner::GetCurrentDefault());
     if (!camera_hal_delegate_->Init()) {
       LOG(ERROR) << "Failed to initialize CameraHalDelegate";
       camera_hal_delegate_.reset();
@@ -59,7 +58,10 @@
         mock_camera_module_.GetPendingRemote());
   }
 
-  void TearDown() override {}
+  void TearDown() override {
+    camera_hal_delegate_.reset();
+    task_environment_.RunUntilIdle();
+  }
 
   void Wait() {
     run_loop_ = std::make_unique<base::RunLoop>();
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
index 72ec3e1c..cd1e429d 100644
--- a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
+++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
@@ -743,8 +743,14 @@
   const auto& type = client_observer->GetType();
   CAMERA_LOG(EVENT) << "Establishing server channel for " << type;
   camera_service_->GetCameraModule(
-      type, base::BindOnce(&CameraHalDispatcherImpl::OnGetCameraModule,
-                           base::Unretained(this), client_observer));
+      type,
+      base::BindOnce(
+          &CameraHalDispatcherImpl::OnGetCameraModule,
+          // TODO(b/322727099): client_observer may be a dangling pointer since
+          // lifetime of CameraClientObserver is shorter than
+          // CameraHalDispatcher. Check the lifetime issue during refactoring.
+          base::Unretained(this),
+          base::UnsafeDanglingUntriaged(client_observer)));
 }
 
 void CameraHalDispatcherImpl::OnGetCameraModule(
diff --git a/media/capture/video/chromeos/video_capture_device_chromeos_delegate.cc b/media/capture/video/chromeos/video_capture_device_chromeos_delegate.cc
index 6b787ab..1e8fce3 100644
--- a/media/capture/video/chromeos/video_capture_device_chromeos_delegate.cc
+++ b/media/capture/video/chromeos/video_capture_device_chromeos_delegate.cc
@@ -106,11 +106,12 @@
       rotation_(0),
       cleanup_callback_(std::move(cleanup_callback)),
       device_closed_(base::WaitableEvent::ResetPolicy::MANUAL,
-                     base::WaitableEvent::InitialState::NOT_SIGNALED) {
+                     base::WaitableEvent::InitialState::NOT_SIGNALED),
+      ui_task_runner_(ui_task_runner) {
   power_observer_ = base::SequenceBound<PowerObserver>(
-      ui_task_runner, weak_ptr_factory_.GetWeakPtr(), capture_task_runner_);
+      ui_task_runner_, weak_ptr_factory_.GetWeakPtr(), capture_task_runner_);
   screen_observer_delegate_ = base::SequenceBound<ScreenObserverDelegate>(
-      ui_task_runner,
+      ui_task_runner_,
       base::BindPostTask(
           capture_task_runner_,
           base::BindRepeating(&VideoCaptureDeviceChromeOSDelegate::SetRotation,
@@ -156,7 +157,7 @@
       capture_params_[client_type] = params;
       camera_device_delegate_ = std::make_unique<CameraDeviceDelegate>(
           device_descriptor_, camera_hal_delegate_,
-          camera_device_ipc_thread_.task_runner());
+          camera_device_ipc_thread_.task_runner(), ui_task_runner_);
       OpenDevice();
     }
     CameraAppDeviceBridgeImpl::GetInstance()->OnVideoCaptureDeviceCreated(
diff --git a/media/capture/video/chromeos/video_capture_device_chromeos_delegate.h b/media/capture/video/chromeos/video_capture_device_chromeos_delegate.h
index 780f498..3834033 100644
--- a/media/capture/video/chromeos/video_capture_device_chromeos_delegate.h
+++ b/media/capture/video/chromeos/video_capture_device_chromeos_delegate.h
@@ -106,6 +106,8 @@
 
   base::SequenceBound<ScreenObserverDelegate> screen_observer_delegate_;
 
+  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+
   base::WeakPtrFactory<VideoCaptureDeviceChromeOSDelegate> weak_ptr_factory_{
       this};
 };
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index a60eb60..213cb8d9 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -50,6 +50,7 @@
     "//remoting/codec:encoder",
     "//third_party/blink/renderer/modules:unit_tests",
     "//third_party/blink/renderer/modules:unit_tests_v2",
+    "//third_party/blink/renderer/modules/mediarecorder",
     "//ui/gl:gl_unittests",
 
     # TODO(crbug.com/1184041): Remove //remoting/host:common when the usage is removed.
@@ -386,9 +387,7 @@
       "vp9_svc_layers.h",
     ]
     configs += [ "//third_party/libvpx:libvpx_config" ]
-    deps += [
-      "//third_party/libvpx:libvpxrc",
-    ]
+    deps += [ "//third_party/libvpx:libvpxrc" ]
   }
 
   if (use_av1_hw_decoder) {
@@ -654,6 +653,7 @@
       "windows/d3d11_video_device_format_support_unittest.cc",
       "windows/d3d11_video_processor_proxy_unittest.cc",
       "windows/d3d12_helpers_unittest.cc",
+      "windows/mf_audio_encoder_unittest.cc",
       "windows/scoped_d3d_buffers_unittest.cc",
       "windows/supported_profile_helpers_unittest.cc",
     ]
diff --git a/media/gpu/android/video_frame_factory_impl.cc b/media/gpu/android/video_frame_factory_impl.cc
index 4d92df6..64ac053 100644
--- a/media/gpu/android/video_frame_factory_impl.cc
+++ b/media/gpu/android/video_frame_factory_impl.cc
@@ -63,9 +63,9 @@
     return;
   }
 
-  std::move(init_cb).Run(
-      gpu::TextureOwner::Create(GetTextureOwnerMode(overlay_mode),
-                                shared_context_state, std::move(drdc_lock)));
+  std::move(init_cb).Run(gpu::TextureOwner::Create(
+      GetTextureOwnerMode(overlay_mode), shared_context_state,
+      std::move(drdc_lock), gpu::TextureOwnerCodecType::kMediaCodec));
 }
 
 }  // namespace
diff --git a/media/gpu/chromeos/image_processor_perf_test.cc b/media/gpu/chromeos/image_processor_perf_test.cc
index 2d6f1b3..c4a4cb91 100644
--- a/media/gpu/chromeos/image_processor_perf_test.cc
+++ b/media/gpu/chromeos/image_processor_perf_test.cc
@@ -196,6 +196,29 @@
   return frame;
 }
 
+void WriteJsonResult(std::vector<std::pair<std::string, double>> data) {
+  base::Value::Dict metrics;
+  for (auto i : data) {
+    metrics.Set(i.first, i.second);
+  }
+
+  const auto output_folder_path = base::FilePath(g_output_directory);
+  std::string metrics_str;
+  ASSERT_TRUE(base::JSONWriter::WriteWithOptions(
+      metrics, base::JSONWriter::OPTIONS_PRETTY_PRINT, &metrics_str));
+  const base::FilePath metrics_file_path = output_folder_path.Append(
+      g_env->GetTestOutputFilePath().AddExtension(FILE_PATH_LITERAL(".json")));
+  // Make sure that the directory into which json is saved is created.
+  LOG_ASSERT(base::CreateDirectory(metrics_file_path.DirName()));
+  base::File metrics_output_file(
+      base::FilePath(metrics_file_path),
+      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  const int bytes_written = metrics_output_file.WriteAtCurrentPos(
+      metrics_str.data(), metrics_str.length());
+  ASSERT_EQ(bytes_written, static_cast<int>(metrics_str.length()));
+  LOG(INFO) << "Wrote performance metrics to: " << metrics_file_path;
+}
+
 class ImageProcessorPerfTest : public ::testing::Test {
  public:
   enum TestType {
@@ -281,30 +304,6 @@
     candidates_ = {candidate_};
   }
 
-  void WriteJsonResult(std::vector<std::pair<std::string, double>> data) {
-    base::Value::Dict metrics;
-    for (auto i : data) {
-      metrics.Set(i.first, i.second);
-    }
-
-    const auto output_folder_path = base::FilePath(g_output_directory);
-    std::string metrics_str;
-    ASSERT_TRUE(base::JSONWriter::WriteWithOptions(
-        metrics, base::JSONWriter::OPTIONS_PRETTY_PRINT, &metrics_str));
-    const base::FilePath metrics_file_path =
-        output_folder_path.Append(g_env->GetTestOutputFilePath().AddExtension(
-            FILE_PATH_LITERAL(".json")));
-    // Make sure that the directory into which json is saved is created.
-    LOG_ASSERT(base::CreateDirectory(metrics_file_path.DirName()));
-    base::File metrics_output_file(
-        base::FilePath(metrics_file_path),
-        base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-    const int bytes_written = metrics_output_file.WriteAtCurrentPos(
-        metrics_str.data(), metrics_str.length());
-    ASSERT_EQ(bytes_written, static_cast<int>(metrics_str.length()));
-    LOG(INFO) << "Wrote performance metrics to: " << metrics_file_path;
-  }
-
   gfx::Size test_image_size_;
   gfx::Rect test_image_visible_rect_;
   ImageProcessor::PixelLayoutCandidate candidate_{Fourcc(Fourcc::MM21),
@@ -864,7 +863,17 @@
 }
 
 #if BUILDFLAG(ENABLE_VULKAN)
-TEST_F(ImageProcessorPerfTest, VulkanImageProcessorPerfTest) {
+// TODO(b/330167382): Refactor these into parameterized tests.
+void BenchmarkVulkanImageProcessor(bool is_10bit) {
+  const size_t bpp_numerator = is_10bit ? 5 : 1;
+  const size_t bpp_denom = is_10bit ? 4 : 1;
+  const VideoPixelFormat out_video_format =
+      is_10bit ? VideoPixelFormat::PIXEL_FORMAT_XR30
+               : VideoPixelFormat::PIXEL_FORMAT_ARGB;
+  const viz::SharedImageFormat out_viz_format =
+      is_10bit ? viz::SinglePlaneFormat::kBGRA_1010102
+               : viz::SinglePlaneFormat::kRGBA_8888;
+
   // Initialize shared image infrastructure.
   auto share_group = base::MakeRefCounted<gl::GLShareGroup>();
   auto surface =
@@ -900,32 +909,35 @@
       viz::SharedImageFormat::ChannelFormat::k8);
   format_nv12.SetPrefersExternalSampler();
   for (size_t i = 0; i < kNumberOfTestFrames; i++) {
-    input_frames[i] =
-        CreateRandomMM21Frame(test_image_size, VideoFrame::STORAGE_DMABUFS);
+    input_frames[i] = CreateRandomMM21Frame(
+        gfx::Size(test_image_size.width(),
+                  test_image_size.height() * bpp_numerator / bpp_denom),
+        VideoFrame::STORAGE_DMABUFS);
     input_mailboxes[i] = gpu::Mailbox::GenerateForSharedImage();
     auto input_gmb = CreateGpuMemoryBufferHandle(input_frames[i].get());
     shared_image_factory.CreateSharedImage(
-        input_mailboxes[i], format_nv12, test_coded_size,
+        input_mailboxes[i], format_nv12, input_frames[i]->coded_size(),
         gfx::ColorSpace::CreateSRGB(), kTopLeft_GrSurfaceOrigin,
         kOpaque_SkAlphaType,
         gpu::SharedImageUsage::SHARED_IMAGE_USAGE_DISPLAY_READ, "TestLabel",
         std::move(input_gmb));
 
     output_frames[i] = CreateGpuMemoryBufferVideoFrame(
-        VideoPixelFormat::PIXEL_FORMAT_ARGB, test_coded_size,
-        gfx::Rect(test_image_size), test_coded_size, kNullTimestamp,
+        out_video_format, test_coded_size, gfx::Rect(test_image_size),
+        test_coded_size, kNullTimestamp,
         gfx::BufferUsage::SCANOUT_CPU_READ_WRITE);
     output_mailboxes[i] = gpu::Mailbox::GenerateForSharedImage();
     auto output_gmb = CreateGpuMemoryBufferHandle(output_frames[i].get());
     shared_image_factory.CreateSharedImage(
-        output_mailboxes[i], viz::SinglePlaneFormat::kRGBA_8888,
-        test_coded_size, gfx::ColorSpace::CreateSRGB(),
-        kTopLeft_GrSurfaceOrigin, kUnpremul_SkAlphaType,
+        output_mailboxes[i], out_viz_format, test_coded_size,
+        gfx::ColorSpace::CreateSRGB(), kTopLeft_GrSurfaceOrigin,
+        kUnpremul_SkAlphaType,
         gpu::SharedImageUsage::SHARED_IMAGE_USAGE_DISPLAY_WRITE, "TestLabel",
         std::move(output_gmb));
   }
 
-  auto vulkan_image_processor = VulkanImageProcessor::Create();
+  auto vulkan_image_processor =
+      VulkanImageProcessor::Create(is_10bit ? kMT2T : kMM21);
   ASSERT_TRUE(vulkan_image_processor);
 
   auto start_time = base::TimeTicks::Now();
@@ -975,6 +987,14 @@
                    {"TotalDurationMs", delta_time.InMicrosecondsF()},
                    {"FramesPerSecond", fps}});
 }
+
+TEST_F(ImageProcessorPerfTest, VulkanImageProcessorPerfTest) {
+  BenchmarkVulkanImageProcessor(/*is_10bit=*/false);
+}
+
+TEST_F(ImageProcessorPerfTest, VulkanMT2TImageProcessorPerfTest) {
+  BenchmarkVulkanImageProcessor(/*is_10bit=*/true);
+}
 #endif
 
 }  // namespace
diff --git a/media/gpu/chromeos/oop_video_decoder.cc b/media/gpu/chromeos/oop_video_decoder.cc
index f776f85..5f44d899 100644
--- a/media/gpu/chromeos/oop_video_decoder.cc
+++ b/media/gpu/chromeos/oop_video_decoder.cc
@@ -229,7 +229,9 @@
     oop_video_decoder_.QueryVersion(base::BindOnce(
         &OOPVideoDecoderSupportedConfigsManager::OnGetInterfaceVersion,
         base::Unretained(this)));
-    GetSupportedConfigs();
+    oop_video_decoder_->GetSupportedConfigs(base::BindOnce(
+        &OOPVideoDecoderSupportedConfigsManager::OnGetSupportedConfigs,
+        base::Unretained(this)));
 
     // Eventually, we need to call |cb|. We can't store |oop_video_decoder| here
     // because it's been taken over by the |oop_video_decoder_|. For now, we'll
@@ -264,9 +266,12 @@
   }
 
   void GetSupportedConfigs() {
-    oop_video_decoder_->GetSupportedConfigs(base::BindOnce(
-        &OOPVideoDecoderSupportedConfigsManager::OnGetSupportedConfigs,
-        base::Unretained(this)));
+    base::AutoLock lock(lock_);
+    if (!disconnected_) {
+      oop_video_decoder_->GetSupportedConfigs(base::BindOnce(
+          &OOPVideoDecoderSupportedConfigsManager::OnGetSupportedConfigs,
+          base::Unretained(this)));
+    }
   }
 
   void OnGetSupportedConfigs(const SupportedVideoDecoderConfigs& configs,
diff --git a/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.cc b/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.cc
index 3eb20aa6..9e6f765 100644
--- a/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.cc
+++ b/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
 #include "base/trace_event/trace_event.h"
+#include "media/base/media_switches.h"
 #include "media/gpu/chromeos/image_processor.h"
 #include "media/gpu/gpu_video_decode_accelerator_helpers.h"
 #include "media/gpu/macros.h"
@@ -65,6 +66,9 @@
 
 namespace media {
 // static
+base::AtomicRefCount V4L2StatelessVideoDecoder::num_decoder_instances_(0);
+
+// static
 std::unique_ptr<VideoDecoderMixin> V4L2StatelessVideoDecoder::Create(
     std::unique_ptr<MediaLog> media_log,
     scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
@@ -104,7 +108,7 @@
 V4L2StatelessVideoDecoder::~V4L2StatelessVideoDecoder() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
   DVLOGF(3);
-
+  num_decoder_instances_.Decrement();
   // There can be requests left in the queue if the decoder is torn down without
   // waiting for an end of stream which would trigger a flush.
   ClearPendingRequests(DecoderStatus::Codes::kAborted);
@@ -126,6 +130,19 @@
     return;
   }
 
+  // Verify there's still room for more decoders before querying whether
+  // |config| is supported.
+  static const auto decoder_instances_limit =
+      V4L2StatelessVideoDecoder::GetMaxNumDecoderInstances();
+  const bool can_create_decoder =
+      num_decoder_instances_.Increment() < decoder_instances_limit;
+  if (!can_create_decoder) {
+    num_decoder_instances_.Decrement();
+    LogError(media_log_, "Can't Initialize() decoder, maximum number reached");
+    std::move(init_cb).Run(DecoderStatus::Codes::kTooManyDecoders);
+    return;
+  }
+
   // The decoder should always start out with empty queues. Because the decoder
   // can be reinitialized they are explicitly cleared.
   output_queue_.reset();
@@ -145,7 +162,7 @@
     LogError(media_log_, "Video configuration is not supported: ",
              config.AsHumanReadableString());
     std::move(init_cb).Run(
-        DecoderStatus(DecoderStatus::Codes::kNotInitialized)
+        DecoderStatus(DecoderStatus::Codes::kUnsupportedConfig)
             .AddCause(
                 V4L2Status(V4L2Status::Codes::kFailedFileCapabilitiesCheck)));
     return;
@@ -795,4 +812,15 @@
   }
 }
 
+// static
+int V4L2StatelessVideoDecoder::GetMaxNumDecoderInstances() {
+  if (base::FeatureList::IsEnabled(media::kLimitConcurrentDecoderInstances)) {
+    // Legacy behaviour is to limit the number to 32 [1].
+    // [1] https://source.chromium.org/chromium/chromium/src/+/main:media/gpu/v4l2/v4l2_video_decoder.h;l=183-189;drc=90fa47c897b589bc4857fb7ccafab46a4be2e2ae
+    constexpr int kMaxNumSimultaneousDecoderInstances = 32;
+    return kMaxNumSimultaneousDecoderInstances;
+  }
+  return std::numeric_limits<int>::max();
+}
+
 }  // namespace media
diff --git a/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.h b/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.h
index 1cd7df1..130c7d6 100644
--- a/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.h
+++ b/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.h
@@ -9,6 +9,7 @@
 #include <queue>
 #include <vector>
 
+#include "base/atomic_ref_count.h"
 #include "base/containers/lru_cache.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
@@ -70,6 +71,10 @@
                    size_t size,
                    scoped_refptr<StatelessDecodeSurface> dec_surface) override;
 
+  static int GetMaxNumDecoderInstancesForTesting() {
+    return GetMaxNumDecoderInstances();
+  }
+
  private:
   V4L2StatelessVideoDecoder(
       std::unique_ptr<MediaLog> media_log,
@@ -142,6 +147,13 @@
   // need to be queued up as there may not be free input buffers available.
   void ServiceDecodeRequestQueue();
 
+  // Pages with multiple decoder instances might run out of memory (e.g.
+  // b/170870476) or crash (e.g. crbug.com/1109312). this class method provides
+  // that number to prevent that erroneous behaviour during Initialize().
+  static int GetMaxNumDecoderInstances();
+  // Tracks the number of decoder instances globally in the process.
+  static base::AtomicRefCount num_decoder_instances_;
+
   SEQUENCE_CHECKER(decoder_sequence_checker_);
 
   const scoped_refptr<StatelessDevice> device_;
diff --git a/media/gpu/v4l2/v4l2_stateful_video_decoder.h b/media/gpu/v4l2/v4l2_stateful_video_decoder.h
index f6a75ff..0eb413f 100644
--- a/media/gpu/v4l2/v4l2_stateful_video_decoder.h
+++ b/media/gpu/v4l2/v4l2_stateful_video_decoder.h
@@ -69,10 +69,11 @@
   size_t GetMaxOutputFramePoolSize() const override;
   void SetDmaIncoherentV4L2(bool incoherent) override;
 
- private:
-  FRIEND_TEST_ALL_PREFIXES(V4L2StatefulVideoDecoder, UnsupportedVideoCodec);
-  FRIEND_TEST_ALL_PREFIXES(V4L2StatefulVideoDecoder, TooManyDecoderInstances);
+  static int GetMaxNumDecoderInstancesForTesting() {
+    return GetMaxNumDecoderInstances();
+  }
 
+ private:
   V4L2StatefulVideoDecoder(std::unique_ptr<MediaLog> media_log,
                            scoped_refptr<base::SequencedTaskRunner> task_runner,
                            base::WeakPtr<VideoDecoderMixin::Client> client);
diff --git a/media/gpu/v4l2/v4l2_unittest.cc b/media/gpu/v4l2/v4l2_unittest.cc
index b3f84b5..65a42d38 100644
--- a/media/gpu/v4l2/v4l2_unittest.cc
+++ b/media/gpu/v4l2/v4l2_unittest.cc
@@ -17,6 +17,7 @@
 #include "media/base/mock_media_log.h"
 #include "media/base/test_helpers.h"
 #include "media/base/video_codecs.h"
+#include "media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.h"
 #include "media/gpu/v4l2/v4l2_device.h"
 #include "media/gpu/v4l2/v4l2_stateful_video_decoder.h"
 #include "media/gpu/v4l2/v4l2_utils.h"
@@ -259,38 +260,75 @@
   base::WeakPtrFactory<MockVideoDecoderMixinClient> weak_ptr_factory_;
 };
 
-// Verifies that V4L2StatefulVideoDecoder::Initialize() fails when called
-// with an unsupported codec profile.
-TEST(V4L2StatefulVideoDecoder, UnsupportedVideoCodec) {
+// Templatized test fixture to use with V4L2StatefulVideoDecoder and
+// V4L2StatelessVideoDecoder.
+template <class FlatVideoDecoder>
+class V4L2FlatVideoDecoderTest : public ::testing::Test {
+ public:
+  V4L2FlatVideoDecoderTest() = default;
+  static int GetMaxNumDecoderInstances() {
+    return FlatVideoDecoder::GetMaxNumDecoderInstancesForTesting();
+  }
+  void SetUp() override {
+    // Only run tests using V4L2StatefulVideoDecoder on platforms supporting the
+    // V4L2 stateful decoder API; correspondingly for V4L2StatelessVideoDecoder
+    // and platforms with the V4L2 stateless API implementations.
+    if (std::is_same<FlatVideoDecoder, V4L2StatefulVideoDecoder>::value !=
+        IsV4L2DecoderStateful()) {
+      GTEST_SKIP();
+    }
+  }
+};
+class V4L2FlatVideoDecoderNames {
+ public:
+  template <typename T>
+  static std::string GetName(int) {
+    if (std::is_same<T, V4L2StatefulVideoDecoder>()) {
+      return "Stateful";
+    }
+    if (std::is_same<T, V4L2StatelessVideoDecoder>()) {
+      return "Stateless";
+    }
+  }
+};
+using V4L2FlatVideoDecoderTypes =
+    ::testing::Types<V4L2StatefulVideoDecoder, V4L2StatelessVideoDecoder>;
+TYPED_TEST_SUITE(V4L2FlatVideoDecoderTest,
+                 V4L2FlatVideoDecoderTypes,
+                 V4L2FlatVideoDecoderNames);
+
+// Verifies that V4L2Stateful/StatelessVideoDecoder::Initialize() fails when
+// called with an unsupported codec profile.
+TYPED_TEST(V4L2FlatVideoDecoderTest, UnsupportedVideoCodec) {
   base::test::SingleThreadTaskEnvironment task_environment;
   MockVideoDecoderMixinClient mock_client;
 
-  V4L2StatefulVideoDecoder decoder(
-      std::make_unique<MockMediaLog>(),
-      base::SequencedTaskRunner::GetCurrentDefault(),
-      mock_client.weak_ptr_factory_.GetWeakPtr());
+  auto decoder =
+      TypeParam::Create(std::make_unique<MockMediaLog>(),
+                        base::SequencedTaskRunner::GetCurrentDefault(),
+                        mock_client.weak_ptr_factory_.GetWeakPtr());
 
   const auto unsupported_config = TestVideoConfig::Normal(VideoCodec::kMPEG2);
   EXPECT_CALL(
       mock_client,
       InitCallback(DecoderStatus(DecoderStatus::Codes::kUnsupportedConfig)));
-  decoder.Initialize(unsupported_config, /*low_delay=*/false,
-                     /*cdm_context=*/nullptr,
-                     base::BindOnce(&MockVideoDecoderMixinClient::InitCallback,
-                                    mock_client.weak_ptr_factory_.GetWeakPtr()),
-                     /*output_cb=*/base::DoNothing(),
-                     /*waiting_cb*/ base::DoNothing());
+  static_cast<TypeParam*>(decoder.get())
+      ->Initialize(unsupported_config, /*low_delay=*/false,
+                   /*cdm_context=*/nullptr,
+                   base::BindOnce(&MockVideoDecoderMixinClient::InitCallback,
+                                  mock_client.weak_ptr_factory_.GetWeakPtr()),
+                   /*output_cb=*/base::DoNothing(),
+                   /*waiting_cb*/ base::DoNothing());
 }
 
-// Verifies that V4L2StatefulVideoDecoder::Initialize() fails after the limit
-// of created instances exceeds the threshold.
-TEST(V4L2StatefulVideoDecoder, TooManyDecoderInstances) {
+// Verifies that V4L2Stateful/StatelessVideoDecoder::Initialize() fails after
+// the limit of created instances exceeds the threshold.
+TYPED_TEST(V4L2FlatVideoDecoderTest, TooManyDecoderInstances) {
   base::test::SingleThreadTaskEnvironment task_environment;
   ::testing::NiceMock<MockVideoDecoderMixinClient> mock_client;
   const auto supported_config = TestVideoConfig::Normal(VideoCodec::kH264);
 
-  const int kMaxNumOfInstances =
-      V4L2StatefulVideoDecoder::GetMaxNumDecoderInstances();
+  const int kMaxNumOfInstances = TestFixture::GetMaxNumDecoderInstances();
 
   ::testing::InSequence s;
   EXPECT_CALL(mock_client,
@@ -299,12 +337,11 @@
 
   std::vector<std::unique_ptr<VideoDecoderMixin>> decoders(kMaxNumOfInstances);
   for (auto& decoder : decoders) {
-    decoder = V4L2StatefulVideoDecoder::Create(
-        std::make_unique<MockMediaLog>(),
-        base::SequencedTaskRunner::GetCurrentDefault(),
-        mock_client.weak_ptr_factory_.GetWeakPtr());
+    decoder = TypeParam::Create(std::make_unique<MockMediaLog>(),
+                                base::SequencedTaskRunner::GetCurrentDefault(),
+                                mock_client.weak_ptr_factory_.GetWeakPtr());
 
-    static_cast<V4L2StatefulVideoDecoder*>(decoder.get())
+    static_cast<TypeParam*>(decoder.get())
         ->Initialize(supported_config,
                      /*low_delay=*/false, /*cdm_context=*/nullptr,
                      base::BindOnce(&MockVideoDecoderMixinClient::InitCallback,
@@ -318,16 +355,17 @@
   EXPECT_CALL(
       mock_client,
       InitCallback(DecoderStatus(DecoderStatus::Codes::kTooManyDecoders)));
-  V4L2StatefulVideoDecoder decoder(
-      std::make_unique<MockMediaLog>(),
-      base::SequencedTaskRunner::GetCurrentDefault(),
-      mock_client.weak_ptr_factory_.GetWeakPtr());
-  decoder.Initialize(supported_config,
-                     /*low_delay=*/false, /*cdm_context=*/nullptr,
-                     base::BindOnce(&MockVideoDecoderMixinClient::InitCallback,
-                                    mock_client.weak_ptr_factory_.GetWeakPtr()),
-                     /*output_cb=*/base::DoNothing(),
-                     /*waiting_cb*/ base::DoNothing());
+  auto decoder =
+      TypeParam::Create(std::make_unique<MockMediaLog>(),
+                        base::SequencedTaskRunner::GetCurrentDefault(),
+                        mock_client.weak_ptr_factory_.GetWeakPtr());
+  static_cast<TypeParam*>(decoder.get())
+      ->Initialize(supported_config,
+                   /*low_delay=*/false, /*cdm_context=*/nullptr,
+                   base::BindOnce(&MockVideoDecoderMixinClient::InitCallback,
+                                  mock_client.weak_ptr_factory_.GetWeakPtr()),
+                   /*output_cb=*/base::DoNothing(),
+                   /*waiting_cb*/ base::DoNothing());
 }
 
 }  // namespace media
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index 4d35bda..64c28ec 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -82,7 +82,6 @@
 
   configs += [
     "//build/config/linux/libva",
-    "//third_party/libvpx:libvpx_config",
   ]
 
   deps = [
@@ -200,7 +199,6 @@
     "vaapi_wrapper_unittest.cc",
     "vp9_vaapi_video_encoder_delegate_unittest.cc",
   ]
-  configs += [ "//third_party/libvpx:libvpx_config" ]
   deps = [
     ":common",
     ":vaapi",
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index ed7d366..7aaccec 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -41,6 +41,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
 #include "base/trace_event/trace_event.h"
+#include "base/version.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "media/base/media_switches.h"
@@ -1465,6 +1466,16 @@
   return VASupportedProfiles::Get().IsProfileSupported(mode, va_profile);
 }
 
+bool IsLibVACompatible(const base::Version& runtime,
+                       const base::Version& build_time) {
+  // Since the libva is now ABI-compatible, relax the version check which helps
+  // in upgrading the libva, without breaking any existing functionality. Make
+  // sure the system version (|runtime|) is not older than the version with
+  // which the chromium is built (|build_time|) since libva is only guaranteed
+  // to be backward (and not forward) compatible.
+  return runtime >= build_time;
+}
+
 }  // namespace
 
 // static
@@ -1571,7 +1582,8 @@
     return false;
   }
 
-  // The VA-API version.
+  // The VAAPI version is determined from what is loaded on the system by
+  // calling vaInitialize().
   int major_version, minor_version;
   VAStatus va_res = vaInitialize(va_display, &major_version, &minor_version);
   if (va_res != VA_STATUS_SUCCESS) {
@@ -1590,17 +1602,26 @@
   const VAImplementation implementation_type =
       VendorStringToImplementationType(va_vendor_string);
 
-  // The VAAPI version is determined from what is loaded on the system by
-  // calling vaInitialize(). Since the libva is now ABI-compatible, relax the
-  // version check which helps in upgrading the libva, without breaking any
-  // existing functionality. Make sure the system version is not older than
-  // the version with which the chromium is built since libva is only
-  // guaranteed to be backward (and not forward) compatible.
-  if (VA_MAJOR_VERSION > major_version ||
-      (VA_MAJOR_VERSION == major_version && VA_MINOR_VERSION > minor_version)) {
-    VLOGF(1) << "The system version " << major_version << "." << minor_version
-             << " should be greater than or equal to " << VA_MAJOR_VERSION
-             << "." << VA_MINOR_VERSION;
+  const base::Version runtime_version(
+      {static_cast<unsigned int>(major_version),
+       static_cast<unsigned int>(minor_version)});
+  const base::Version build_time_version({VA_MAJOR_VERSION, VA_MINOR_VERSION});
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (IsGen11Gpu()) {
+    // Jasperlake devices run with pinned libva driver (VA-API version 1.15)
+    // due to b/303841978.
+    // Relax the VA-API version check so Lacros does not fall back to
+    // software encoding on these devices by hardcoding the minor version number
+    // to be 15 instead of the actual (higher) one.
+    // TODO(b/303841978): go back to using the actual minor version number
+    // when libva is upreved in Jasperlake devices.
+    const base::Version jsl_build_version({VA_MAJOR_VERSION, 15});
+    if (!IsLibVACompatible(runtime_version, jsl_build_version)) {
+      return false;
+    }
+  } else
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (!IsLibVACompatible(runtime_version, build_time_version)) {
     return false;
   }
 
diff --git a/media/gpu/windows/mf_audio_encoder.cc b/media/gpu/windows/mf_audio_encoder.cc
index d8c9569..f06a239 100644
--- a/media/gpu/windows/mf_audio_encoder.cc
+++ b/media/gpu/windows/mf_audio_encoder.cc
@@ -983,4 +983,21 @@
   }
 }
 
+// static.
+uint32_t MFAudioEncoder::ClampAccCodecBitrate(uint32_t bitrate) {
+  // 0 audio bitrate could mean multiple things such as no audio, use
+  // default, etc. So, the client should handle the case by itself.
+  CHECK_GT(bitrate, 0u);
+
+  auto it = std::lower_bound(std::begin(kSupportedBitrates),
+                             std::end(kSupportedBitrates), bitrate);
+  if (it != std::end(kSupportedBitrates)) {
+    return *it;
+  }
+
+  return kSupportedBitrates[sizeof(kSupportedBitrates) /
+                                sizeof(kSupportedBitrates[0]) -
+                            1];
+}
+
 }  // namespace media
diff --git a/media/gpu/windows/mf_audio_encoder.h b/media/gpu/windows/mf_audio_encoder.h
index 72238b5..878f820 100644
--- a/media/gpu/windows/mf_audio_encoder.h
+++ b/media/gpu/windows/mf_audio_encoder.h
@@ -74,6 +74,10 @@
   // previous call to `Flush()` has been run.
   void Flush(EncoderStatusCB done_cb) override;
 
+  // Clamp given audio bits per second to the value that Media Foundation
+  // supports.
+  static uint32_t ClampAccCodecBitrate(uint32_t bitrate);
+
  private:
   // This class has six states.
   // kIdle: no input has been received yet, or all data has been processed, and
diff --git a/media/gpu/windows/mf_audio_encoder_unittest.cc b/media/gpu/windows/mf_audio_encoder_unittest.cc
new file mode 100644
index 0000000..9e91f4a
--- /dev/null
+++ b/media/gpu/windows/mf_audio_encoder_unittest.cc
@@ -0,0 +1,25 @@
+// 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 "media/gpu/windows/mf_audio_encoder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+// Most unit tests are in the "AudioDecoderTest" class in another file.
+TEST(MfAudioEncoderTest, ClampAccCodecBitrate) {
+  EXPECT_EQ(96000u, MFAudioEncoder::ClampAccCodecBitrate(96000u));
+  EXPECT_EQ(128000u, MFAudioEncoder::ClampAccCodecBitrate(128000u));
+  EXPECT_EQ(160000u, MFAudioEncoder::ClampAccCodecBitrate(160000u));
+  EXPECT_EQ(192000u, MFAudioEncoder::ClampAccCodecBitrate(192000u));
+
+  EXPECT_EQ(96000u, MFAudioEncoder::ClampAccCodecBitrate(90000));
+  EXPECT_EQ(128000u, MFAudioEncoder::ClampAccCodecBitrate(100000));
+  EXPECT_EQ(160000u, MFAudioEncoder::ClampAccCodecBitrate(150000));
+  EXPECT_EQ(192000u, MFAudioEncoder::ClampAccCodecBitrate(180000));
+  EXPECT_EQ(192000u, MFAudioEncoder::ClampAccCodecBitrate(300000));
+}
+
+}  // namespace media
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index 27e6af0..3fba039 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -841,7 +841,9 @@
     int max_resource_size)
     : context_provider_(context_provider),
       shared_bitmap_reporter_(shared_bitmap_reporter),
-      shared_image_interface_(std::move(shared_image_interface)),
+      shared_image_interface_(MediaSharedBitmapConversionEnabled()
+                                  ? std::move(shared_image_interface)
+                                  : nullptr),
       resource_provider_(resource_provider),
       use_stream_video_draw_quad_(use_stream_video_draw_quad),
       use_gpu_memory_buffer_resources_(use_gpu_memory_buffer_resources),
@@ -1106,7 +1108,9 @@
   const uint32_t plane_resource_id = next_plane_resource_id_++;
 
   if (software_compositor()) {
-    DCHECK_EQ(format, viz::SinglePlaneFormat::kRGBA_8888);
+    DCHECK_EQ(format,
+              (shared_image_interface() ? viz::SinglePlaneFormat::kBGRA_8888
+                                        : viz::SinglePlaneFormat::kRGBA_8888));
 
     all_resources_.push_back(std::make_unique<SoftwarePlaneResource>(
         plane_resource_id, plane_size, color_space, shared_bitmap_reporter_,
@@ -1376,7 +1380,8 @@
   if (software_compositor() || texture_needs_rgb_conversion_out) {
     output_si_format =
         software_compositor()
-            ? viz::SinglePlaneFormat::kRGBA_8888
+            ? (shared_image_interface() ? viz::SinglePlaneFormat::kBGRA_8888
+                                        : viz::SinglePlaneFormat::kRGBA_8888)
             : PaintCanvasVideoRenderer::GetRGBPixelsOutputFormat();
   }
 
@@ -2118,8 +2123,7 @@
 
 scoped_refptr<gpu::ClientSharedImageInterface>
 VideoResourceUpdater::shared_image_interface() const {
-  return MediaSharedBitmapConversionEnabled() ? shared_image_interface_
-                                              : nullptr;
+  return shared_image_interface_;
 }
 
 VideoResourceUpdater::FrameResource::FrameResource() = default;
diff --git a/net/BUILD.gn b/net/BUILD.gn
index f35c35e..a0085c7 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1106,6 +1106,8 @@
     sources += [
       "device_bound_sessions/bound_session_registration_fetcher_param.cc",
       "device_bound_sessions/bound_session_registration_fetcher_param.h",
+      "device_bound_sessions/device_bound_session_service.cc",
+      "device_bound_sessions/device_bound_session_service.h",
     ]
   }
 
@@ -3205,7 +3207,11 @@
   }
 
   if (enable_device_bound_sessions) {
-    sources += [ "device_bound_sessions/bound_session_registration_fetcher_param_unittest.cc" ]
+    sources += [
+      "device_bound_sessions/bound_session_registration_fetcher_param_unittest.cc",
+      "device_bound_sessions/test_util.cc",
+      "device_bound_sessions/test_util.h",
+    ]
   }
 }
 
diff --git a/net/base/features.cc b/net/base/features.cc
index 034d2f4..57ca347 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -534,4 +534,8 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_MAC)
 
+BASE_FEATURE(kDeviceBoundSessions,
+             "DeviceBoundSessions",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace net::features
diff --git a/net/base/features.h b/net/base/features.h
index dbccad6..d5239f3 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -521,6 +521,10 @@
 NET_EXPORT BASE_DECLARE_FEATURE(kReduceIPAddressChangeNotification);
 #endif  // BUILDFLAG(IS_MAC)
 
+// This feature will enable the Device Bound Session Credentials protocol to let
+// the server assert sessions (and cookies) are bound to a specific device.
+NET_EXPORT BASE_DECLARE_FEATURE(kDeviceBoundSessions);
+
 }  // namespace net::features
 
 #endif  // NET_BASE_FEATURES_H_
diff --git a/net/device_bound_sessions/device_bound_session_service.cc b/net/device_bound_sessions/device_bound_session_service.cc
new file mode 100644
index 0000000..278f8f68
--- /dev/null
+++ b/net/device_bound_sessions/device_bound_session_service.cc
@@ -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.
+
+#include "net/device_bound_sessions/device_bound_session_service.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "net/base/features.h"
+
+namespace net {
+
+namespace {
+class DeviceBoundSessionServiceImpl : public DeviceBoundSessionService {
+ public:
+  // TODO(kristianm): Implement RegisterBoundSession
+  void RegisterBoundSession(const BoundSessionRegistrationFetcherParam&
+                                registration_params) override {}
+};
+}  // namespace
+
+std::unique_ptr<DeviceBoundSessionService> DeviceBoundSessionService::Create() {
+  return std::make_unique<DeviceBoundSessionServiceImpl>();
+}
+
+}  // namespace net
diff --git a/net/device_bound_sessions/device_bound_session_service.h b/net/device_bound_sessions/device_bound_session_service.h
new file mode 100644
index 0000000..5261ff7
--- /dev/null
+++ b/net/device_bound_sessions/device_bound_session_service.h
@@ -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.
+
+#ifndef NET_DEVICE_BOUND_SESSIONS_DEVICE_BOUND_SESSION_SERVICE_H_
+#define NET_DEVICE_BOUND_SESSIONS_DEVICE_BOUND_SESSION_SERVICE_H_
+
+#include <memory>
+
+#include "net/base/net_export.h"
+#include "net/device_bound_sessions/bound_session_registration_fetcher_param.h"
+
+namespace net {
+
+// Main class for Device Bound Session Credentials (DBSC).
+// Full information can be found at https://github.com/WICG/dbsc
+class NET_EXPORT DeviceBoundSessionService {
+ public:
+  static std::unique_ptr<DeviceBoundSessionService> Create();
+
+  virtual void RegisterBoundSession(
+      const BoundSessionRegistrationFetcherParam& registration_params) = 0;
+
+  virtual ~DeviceBoundSessionService() = default;
+};
+
+}  // namespace net
+
+#endif  // NET_DEVICE_BOUND_SESSIONS_DEVICE_BOUND_SESSION_SERVICE_H_
diff --git a/net/device_bound_sessions/test_util.cc b/net/device_bound_sessions/test_util.cc
new file mode 100644
index 0000000..b6a9c1e
--- /dev/null
+++ b/net/device_bound_sessions/test_util.cc
@@ -0,0 +1,12 @@
+// 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 "net/device_bound_sessions/test_util.h"
+
+namespace net {
+
+DeviceBoundSessionServiceMock::DeviceBoundSessionServiceMock() = default;
+DeviceBoundSessionServiceMock::~DeviceBoundSessionServiceMock() = default;
+
+}  // namespace net
diff --git a/net/device_bound_sessions/test_util.h b/net/device_bound_sessions/test_util.h
new file mode 100644
index 0000000..101298f2
--- /dev/null
+++ b/net/device_bound_sessions/test_util.h
@@ -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.
+
+#ifndef NET_DEVICE_BOUND_SESSIONS_TEST_UTIL_H_
+#define NET_DEVICE_BOUND_SESSIONS_TEST_UTIL_H_
+
+#include "net/device_bound_sessions/bound_session_registration_fetcher_param.h"
+#include "net/device_bound_sessions/device_bound_session_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+
+class DeviceBoundSessionServiceMock : public DeviceBoundSessionService {
+ public:
+  DeviceBoundSessionServiceMock();
+  ~DeviceBoundSessionServiceMock() override;
+
+  MOCK_METHOD(void,
+              RegisterBoundSession,
+              (const BoundSessionRegistrationFetcherParam& registration_params),
+              (override));
+};
+
+}  // namespace net
+
+#endif  // NET_DEVICE_BOUND_SESSIONS_TEST_UTIL_H_
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index d1ba7773..36f8300 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -47,6 +47,10 @@
 #include "net/reporting/reporting_service.h"
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+#include "net/device_bound_sessions/device_bound_session_service.h"
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 namespace net {
 
 URLRequestContext::URLRequestContext(
@@ -253,4 +257,11 @@
   transport_security_persister_ = std::move(transport_security_persister);
 }
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+void URLRequestContext::set_device_bound_session_service(
+    std::unique_ptr<DeviceBoundSessionService> device_bound_session_service) {
+  device_bound_session_service_ = std::move(device_bound_session_service);
+}
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 }  // namespace net
diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h
index 5618d02..883f9cd 100644
--- a/net/url_request/url_request_context.h
+++ b/net/url_request/url_request_context.h
@@ -61,6 +61,10 @@
 class ReportingService;
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+class DeviceBoundSessionService;
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 // Class that provides application-specific context for URLRequest
 // instances. May only be created by URLRequestContextBuilder.
 // Owns most of its member variables, except a few that may be shared
@@ -207,6 +211,13 @@
   }
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+  // May return nullptr if the feature is disabled.
+  DeviceBoundSessionService* device_bound_session_service() const {
+    return device_bound_session_service_.get();
+  }
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
   bool enable_brotli() const { return enable_brotli_; }
 
   bool enable_zstd() const { return enable_zstd_; }
@@ -301,6 +312,10 @@
       std::unique_ptr<TransportSecurityPersister> transport_security_persister);
 
   raw_ptr<NetLog> net_log_ = nullptr;
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+  void set_device_bound_session_service(
+      std::unique_ptr<DeviceBoundSessionService> device_bound_session_service);
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
 
   std::unique_ptr<HostResolver> host_resolver_;
   std::unique_ptr<CertVerifier> cert_verifier_;
@@ -348,6 +363,10 @@
   std::unique_ptr<std::set<raw_ptr<const URLRequest, SetExperimental>>>
       url_requests_;
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+  std::unique_ptr<DeviceBoundSessionService> device_bound_session_service_;
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
   // Enables Brotli Content-Encoding support.
   bool enable_brotli_ = false;
   // Enables Zstd Content-Encoding support.
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index c4ba7328..4cd9a37 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -63,6 +63,10 @@
 #include "base/android/build_info.h"
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+#include "net/device_bound_sessions/device_bound_session_service.h"
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 namespace net {
 
 URLRequestContextBuilder::HttpCacheParams::HttpCacheParams() = default;
@@ -243,6 +247,13 @@
       std::move(create_http_network_transaction_factory);
 }
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+void URLRequestContextBuilder::set_device_bound_session_service(
+    std::unique_ptr<DeviceBoundSessionService> device_bound_session_service) {
+  device_bound_session_service_ = std::move(device_bound_session_service);
+}
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 void URLRequestContextBuilder::BindToNetwork(
     handles::NetworkHandle network,
     std::optional<HostResolver::ManagerOptions> options) {
@@ -492,6 +503,11 @@
   }
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+  context->set_device_bound_session_service(
+      std::move(device_bound_session_service_));
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
   HttpNetworkSessionContext network_session_context;
   // Unlike the other fields of HttpNetworkSession::Context,
   // |client_socket_factory| is not mirrored in URLRequestContext.
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h
index 66fd979..d638aed 100644
--- a/net/url_request/url_request_context_builder.h
+++ b/net/url_request/url_request_context_builder.h
@@ -65,6 +65,10 @@
 class PersistentReportingAndNelStore;
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+class DeviceBoundSessionService;
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 // A URLRequestContextBuilder creates a single URLRequestContext. It provides
 // methods to manage various URLRequestContext components which should be called
 // before creating the Context. Once configuration is complete, calling Build()
@@ -345,6 +349,11 @@
     cookie_deprecation_label_ = label;
   }
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+  void set_device_bound_session_service(
+      std::unique_ptr<DeviceBoundSessionService> device_bound_session_service);
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
   // Binds the context to `network`. All requests scheduled through the context
   // built by this builder will be sent using `network`. Requests will fail if
   // `network` disconnects. `options` allows to specify the ManagerOptions that
@@ -451,6 +460,9 @@
   std::unique_ptr<HttpServerProperties> http_server_properties_;
   std::map<std::string, std::unique_ptr<URLRequestJobFactory::ProtocolHandler>>
       protocol_handlers_;
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+  std::unique_ptr<DeviceBoundSessionService> device_bound_session_service_;
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
 
   raw_ptr<ClientSocketFactory> client_socket_factory_raw_ = nullptr;
 };
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index d007b862..80344217 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -103,6 +103,11 @@
 #include "net/android/network_library.h"
 #endif
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+#include "net/device_bound_sessions/bound_session_registration_fetcher_param.h"
+#include "net/device_bound_sessions/device_bound_session_service.h"
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 namespace {
 
 // These values are persisted to logs. Entries should not be renumbered and
@@ -523,6 +528,9 @@
   }
 
   ProcessStrictTransportSecurityHeader();
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+  ProcessDeviceBoundSessionsHeader();
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
 
   // Clear |set_cookie_access_result_list_| after any processing in case
   // SaveCookiesAndNotifyHeadersComplete is called again.
@@ -1055,6 +1063,19 @@
     NotifyHeadersComplete();
 }
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+void URLRequestHttpJob::ProcessDeviceBoundSessionsHeader() {
+  std::vector<BoundSessionRegistrationFetcherParam> params =
+      BoundSessionRegistrationFetcherParam::CreateIfValid(request_->url(),
+                                                          GetResponseHeaders());
+  if (auto* service = request_->context()->device_bound_session_service()) {
+    for (const auto& param : params) {
+      service->RegisterBoundSession(param);
+    }
+  }
+}
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 void URLRequestHttpJob::ProcessStrictTransportSecurityHeader() {
   DCHECK(response_info_);
   TransportSecurityState* security_state =
diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h
index 3422977..c131875e 100644
--- a/net/url_request/url_request_http_job.h
+++ b/net/url_request/url_request_http_job.h
@@ -125,6 +125,11 @@
       CookieAccessResultList& excluded_cookies) const;
   void SaveCookiesAndNotifyHeadersComplete(int result);
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+  // Process the DBSC header, if one exists.
+  void ProcessDeviceBoundSessionsHeader();
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
   // Processes the Strict-Transport-Security header, if one exists.
   void ProcessStrictTransportSecurityHeader();
 
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index 8146ba6..f12492de 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -68,6 +68,11 @@
 #include "net/android/net_test_support_jni/AndroidNetworkLibraryTestUtil_jni.h"
 #endif
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+#include "net/device_bound_sessions/device_bound_session_service.h"
+#include "net/device_bound_sessions/test_util.h"
+#endif
+
 using net::test::IsError;
 using net::test::IsOk;
 
@@ -1170,6 +1175,86 @@
   }
 }
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
+class URLRequestHttpJobWithMockSocketsDeviceBoundSessionServiceTest
+    : public TestWithTaskEnvironment {
+ protected:
+  URLRequestHttpJobWithMockSocketsDeviceBoundSessionServiceTest() {
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    context_builder->set_client_socket_factory_for_testing(&socket_factory_);
+    context_builder->set_device_bound_session_service(
+        std::make_unique<testing::StrictMock<DeviceBoundSessionServiceMock>>());
+    context_ = context_builder->Build();
+    request_ = context_->CreateRequest(GURL("http://www.example.com"),
+                                       DEFAULT_PRIORITY, &delegate_,
+                                       TRAFFIC_ANNOTATION_FOR_TESTS);
+  }
+
+  DeviceBoundSessionServiceMock& GetMockService() {
+    return *static_cast<DeviceBoundSessionServiceMock*>(
+        context_->device_bound_session_service());
+  }
+
+  MockClientSocketFactory socket_factory_;
+  std::unique_ptr<URLRequestContext> context_;
+  TestDelegate delegate_;
+  std::unique_ptr<URLRequest> request_;
+};
+
+TEST_F(URLRequestHttpJobWithMockSocketsDeviceBoundSessionServiceTest,
+       ShouldRespondToDeviceBoundSessionHeader) {
+  const MockWrite writes[] = {
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: www.example.com\r\n"
+                "Connection: keep-alive\r\n"
+                "User-Agent: \r\n"
+                "Accept-Encoding: gzip, deflate\r\n"
+                "Accept-Language: en-us,fr\r\n\r\n")};
+
+  const MockRead reads[] = {
+      MockRead(
+          "HTTP/1.1 200 OK\r\n"
+          "Accept-Ranges: bytes\r\n"
+          "Sec-Session-Registration: \"new\";challenge=:Y29kZWQ=:;es256\r\n"
+          "Content-Length: 12\r\n\r\n"),
+      MockRead("Test Content")};
+
+  StaticSocketDataProvider socket_data(reads, writes);
+  socket_factory_.AddSocketDataProvider(&socket_data);
+
+  request_->Start();
+  EXPECT_CALL(GetMockService(), RegisterBoundSession).Times(1);
+  delegate_.RunUntilComplete();
+  EXPECT_THAT(delegate_.request_status(), IsOk());
+}
+
+TEST_F(URLRequestHttpJobWithMockSocketsDeviceBoundSessionServiceTest,
+       ShouldNotRespondWithoutDeviceBoundSessionHeader) {
+  const MockWrite writes[] = {
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: www.example.com\r\n"
+                "Connection: keep-alive\r\n"
+                "User-Agent: \r\n"
+                "Accept-Encoding: gzip, deflate\r\n"
+                "Accept-Language: en-us,fr\r\n\r\n")};
+
+  const MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
+                                     "Accept-Ranges: bytes\r\n"
+                                     "Content-Length: 12\r\n\r\n"),
+                            MockRead("Test Content")};
+
+  StaticSocketDataProvider socket_data(reads, writes);
+  socket_factory_.AddSocketDataProvider(&socket_data);
+
+  request_->Start();
+  EXPECT_CALL(GetMockService(), RegisterBoundSession).Times(0);
+  delegate_.RunUntilComplete();
+  EXPECT_THAT(delegate_.request_status(), IsOk());
+}
+
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 namespace {
 std::unique_ptr<test_server::HttpResponse> HandleRequest(
     const std::string_view& content,
diff --git a/printing/printing_features.cc b/printing/printing_features.cc
index 442116f..d7d0c28 100644
--- a/printing/printing_features.cc
+++ b/printing/printing_features.cc
@@ -89,6 +89,9 @@
              "EnableOopPrintDrivers",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+const base::FeatureParam<bool> kEnableOopPrintDriversEarlyStart{
+    &kEnableOopPrintDrivers, "EarlyStart", false};
+
 const base::FeatureParam<bool> kEnableOopPrintDriversJobPrint{
     &kEnableOopPrintDrivers, "JobPrint", true};
 
diff --git a/printing/printing_features.h b/printing/printing_features.h
index ac5d3b6..fc159db5 100644
--- a/printing/printing_features.h
+++ b/printing/printing_features.h
@@ -49,6 +49,8 @@
 #if BUILDFLAG(ENABLE_OOP_PRINTING)
 COMPONENT_EXPORT(PRINTING_BASE) BASE_DECLARE_FEATURE(kEnableOopPrintDrivers);
 COMPONENT_EXPORT(PRINTING_BASE)
+extern const base::FeatureParam<bool> kEnableOopPrintDriversEarlyStart;
+COMPONENT_EXPORT(PRINTING_BASE)
 extern const base::FeatureParam<bool> kEnableOopPrintDriversJobPrint;
 COMPONENT_EXPORT(PRINTING_BASE)
 extern const base::FeatureParam<bool> kEnableOopPrintDriversSandbox;
diff --git a/sandbox/policy/linux/bpf_network_policy_linux.cc b/sandbox/policy/linux/bpf_network_policy_linux.cc
index 148be75..e5168ff0 100644
--- a/sandbox/policy/linux/bpf_network_policy_linux.cc
+++ b/sandbox/policy/linux/bpf_network_policy_linux.cc
@@ -124,16 +124,16 @@
   // IP_TOS and IPV6_TCLASS are for P2P sockets.
   ResultExpr ipv4_optname_switch =
       Switch(optname)
-          .Cases(
-              {IP_RECVERR, IP_MTU_DISCOVER, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
-               IP_MULTICAST_IF, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_TOS},
-              Allow())
+          .Cases({IP_RECVERR, IP_MTU_DISCOVER, IP_MULTICAST_LOOP,
+                  IP_MULTICAST_TTL, IP_MULTICAST_IF, IP_ADD_MEMBERSHIP,
+                  IP_DROP_MEMBERSHIP, IP_TOS, IP_RECVTOS},
+                 Allow())
           .Default(CrashSIGSYSSockopt());
   ResultExpr ipv6_optname_switch =
       Switch(optname)
           .Cases({IPV6_RECVERR, IPV6_MTU_DISCOVER, IPV6_MULTICAST_LOOP,
                   IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_JOIN_GROUP,
-                  IPV6_LEAVE_GROUP, IPV6_TCLASS, IPV6_V6ONLY},
+                  IPV6_LEAVE_GROUP, IPV6_TCLASS, IPV6_V6ONLY, IPV6_RECVTCLASS},
                  Allow())
           .Default(CrashSIGSYSSockopt());
   ResultExpr tcp_optname_switch =
diff --git a/services/device/public/cpp/geolocation/BUILD.gn b/services/device/public/cpp/geolocation/BUILD.gn
index e089f494..3998ae2 100644
--- a/services/device/public/cpp/geolocation/BUILD.gn
+++ b/services/device/public/cpp/geolocation/BUILD.gn
@@ -27,8 +27,8 @@
   }
   if (is_apple) {
     sources += [
-      "system_geolocation_source_mac.h",
-      "system_geolocation_source_mac.mm",
+      "system_geolocation_source_apple.h",
+      "system_geolocation_source_apple.mm",
     ]
     frameworks = [
       "CoreLocation.framework",
diff --git a/services/device/public/cpp/geolocation/system_geolocation_source_mac.h b/services/device/public/cpp/geolocation/system_geolocation_source_apple.h
similarity index 85%
rename from services/device/public/cpp/geolocation/system_geolocation_source_mac.h
rename to services/device/public/cpp/geolocation/system_geolocation_source_apple.h
index 31800cc..df337c1 100644
--- a/services/device/public/cpp/geolocation/system_geolocation_source_mac.h
+++ b/services/device/public/cpp/geolocation/system_geolocation_source_apple.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_DEVICE_PUBLIC_CPP_GEOLOCATION_SYSTEM_GEOLOCATION_SOURCE_MAC_H_
-#define SERVICES_DEVICE_PUBLIC_CPP_GEOLOCATION_SYSTEM_GEOLOCATION_SOURCE_MAC_H_
+#ifndef SERVICES_DEVICE_PUBLIC_CPP_GEOLOCATION_SYSTEM_GEOLOCATION_SOURCE_APPLE_H_
+#define SERVICES_DEVICE_PUBLIC_CPP_GEOLOCATION_SYSTEM_GEOLOCATION_SOURCE_APPLE_H_
 
 #include "base/memory/weak_ptr.h"
 #include "services/device/public/cpp/geolocation/geolocation_system_permission_manager.h"
@@ -14,14 +14,14 @@
 
 namespace device {
 
-class COMPONENT_EXPORT(GEOLOCATION) SystemGeolocationSourceMac
+class COMPONENT_EXPORT(GEOLOCATION) SystemGeolocationSourceApple
     : public SystemGeolocationSource {
  public:
   static std::unique_ptr<GeolocationSystemPermissionManager>
-  CreateGeolocationSystemPermissionManagerOnMac();
+  CreateGeolocationSystemPermissionManager();
 
-  SystemGeolocationSourceMac();
-  ~SystemGeolocationSourceMac() override;
+  SystemGeolocationSourceApple();
+  ~SystemGeolocationSourceApple() override;
 
   // SystemGeolocationSource implementation:
   void RegisterPermissionUpdateCallback(
@@ -51,9 +51,9 @@
   SEQUENCE_CHECKER(sequence_checker_);
   PermissionUpdateCallback permission_update_callback_;
   scoped_refptr<PositionObserverList> position_observers_;
-  base::WeakPtrFactory<SystemGeolocationSourceMac> weak_ptr_factory_{this};
+  base::WeakPtrFactory<SystemGeolocationSourceApple> weak_ptr_factory_{this};
 };
 
 }  // namespace device
 
-#endif  // SERVICES_DEVICE_PUBLIC_CPP_GEOLOCATION_SYSTEM_GEOLOCATION_SOURCE_MAC_H_
+#endif  // SERVICES_DEVICE_PUBLIC_CPP_GEOLOCATION_SYSTEM_GEOLOCATION_SOURCE_APPLE_H_
diff --git a/services/device/public/cpp/geolocation/system_geolocation_source_mac.mm b/services/device/public/cpp/geolocation/system_geolocation_source_apple.mm
similarity index 79%
rename from services/device/public/cpp/geolocation/system_geolocation_source_mac.mm
rename to services/device/public/cpp/geolocation/system_geolocation_source_apple.mm
index 158efa6..879af59 100644
--- a/services/device/public/cpp/geolocation/system_geolocation_source_mac.mm
+++ b/services/device/public/cpp/geolocation/system_geolocation_source_apple.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/device/public/cpp/geolocation/system_geolocation_source_mac.h"
+#include "services/device/public/cpp/geolocation/system_geolocation_source_apple.h"
 
 #import <CoreLocation/CoreLocation.h>
 
@@ -17,11 +17,11 @@
     : NSObject <CLLocationManagerDelegate> {
   BOOL _permissionInitialized;
   BOOL _hasPermission;
-  base::WeakPtr<device::SystemGeolocationSourceMac> _manager;
+  base::WeakPtr<device::SystemGeolocationSourceApple> _manager;
 }
 
 - (instancetype)initWithManager:
-    (base::WeakPtr<device::SystemGeolocationSourceMac>)manager;
+    (base::WeakPtr<device::SystemGeolocationSourceApple>)manager;
 
 // CLLocationManagerDelegate
 - (void)locationManager:(CLLocationManager*)manager
@@ -35,7 +35,7 @@
 
 namespace device {
 
-SystemGeolocationSourceMac::SystemGeolocationSourceMac()
+SystemGeolocationSourceApple::SystemGeolocationSourceApple()
     : location_manager_([[CLLocationManager alloc] init]),
       permission_update_callback_(base::DoNothing()),
       position_observers_(base::MakeRefCounted<PositionObserverList>()) {
@@ -44,38 +44,38 @@
   location_manager_.delegate = delegate_;
 }
 
-SystemGeolocationSourceMac::~SystemGeolocationSourceMac() = default;
+SystemGeolocationSourceApple::~SystemGeolocationSourceApple() = default;
 
 // static
 std::unique_ptr<GeolocationSystemPermissionManager>
-SystemGeolocationSourceMac::CreateGeolocationSystemPermissionManagerOnMac() {
+SystemGeolocationSourceApple::CreateGeolocationSystemPermissionManager() {
   return std::make_unique<GeolocationSystemPermissionManager>(
-      std::make_unique<SystemGeolocationSourceMac>());
+      std::make_unique<SystemGeolocationSourceApple>());
 }
 
-void SystemGeolocationSourceMac::RegisterPermissionUpdateCallback(
+void SystemGeolocationSourceApple::RegisterPermissionUpdateCallback(
     PermissionUpdateCallback callback) {
   permission_update_callback_ = callback;
   permission_update_callback_.Run(GetSystemPermission());
 }
 
-void SystemGeolocationSourceMac::PermissionUpdated() {
+void SystemGeolocationSourceApple::PermissionUpdated() {
   permission_update_callback_.Run(GetSystemPermission());
 }
 
-void SystemGeolocationSourceMac::PositionUpdated(
+void SystemGeolocationSourceApple::PositionUpdated(
     const mojom::Geoposition& position) {
   position_observers_->Notify(FROM_HERE, &PositionObserver::OnPositionUpdated,
                               position);
 }
 
-void SystemGeolocationSourceMac::PositionError(
+void SystemGeolocationSourceApple::PositionError(
     const mojom::GeopositionError& error) {
   position_observers_->Notify(FROM_HERE, &PositionObserver::OnPositionError,
                               error);
 }
 
-void SystemGeolocationSourceMac::StartWatchingPosition(bool high_accuracy) {
+void SystemGeolocationSourceApple::StartWatchingPosition(bool high_accuracy) {
   if (high_accuracy) {
     location_manager_.desiredAccuracy = kCLLocationAccuracyBest;
   } else {
@@ -85,11 +85,11 @@
   [location_manager_ startUpdatingLocation];
 }
 
-void SystemGeolocationSourceMac::StopWatchingPosition() {
+void SystemGeolocationSourceApple::StopWatchingPosition() {
   [location_manager_ stopUpdatingLocation];
 }
 
-LocationSystemPermissionStatus SystemGeolocationSourceMac::GetSystemPermission()
+LocationSystemPermissionStatus SystemGeolocationSourceApple::GetSystemPermission()
     const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (![delegate_ permissionInitialized]) {
@@ -103,23 +103,23 @@
   return LocationSystemPermissionStatus::kDenied;
 }
 
-void SystemGeolocationSourceMac::OpenSystemPermissionSetting() {
+void SystemGeolocationSourceApple::OpenSystemPermissionSetting() {
 #if BUILDFLAG(IS_MAC)
   base::mac::OpenSystemSettingsPane(
       base::mac::SystemSettingsPane::kPrivacySecurity_LocationServices);
 #endif
 }
 
-void SystemGeolocationSourceMac::RequestPermission() {
+void SystemGeolocationSourceApple::RequestPermission() {
   [location_manager_ requestWhenInUseAuthorization];
 }
 
-void SystemGeolocationSourceMac::AddPositionUpdateObserver(
+void SystemGeolocationSourceApple::AddPositionUpdateObserver(
     PositionObserver* observer) {
   position_observers_->AddObserver(observer);
 }
 
-void SystemGeolocationSourceMac::RemovePositionUpdateObserver(
+void SystemGeolocationSourceApple::RemovePositionUpdateObserver(
     PositionObserver* observer) {
   position_observers_->RemoveObserver(observer);
 }
@@ -129,7 +129,7 @@
 @implementation GeolocationSystemPermissionManagerDelegate
 
 - (instancetype)initWithManager:
-    (base::WeakPtr<device::SystemGeolocationSourceMac>)manager {
+    (base::WeakPtr<device::SystemGeolocationSourceApple>)manager {
   if (self = [super init]) {
     _permissionInitialized = NO;
     _hasPermission = NO;
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 16e8cf4..511d138 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -189,6 +189,10 @@
 #include "base/android/application_status_listener.h"
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+#include "net/device_bound_sessions/device_bound_session_service.h"
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 namespace network {
 
 namespace {
@@ -2681,6 +2685,13 @@
     builder.set_cookie_deprecation_label(*params_->cookie_deprecation_label);
   }
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+  if (params_->device_bound_sessions_enabled) {
+    builder.set_device_bound_session_service(
+        net::DeviceBoundSessionService::Create());
+  }
+#endif
+
   if (on_url_request_context_builder_configured) {
     std::move(on_url_request_context_builder_configured).Run(&builder);
   }
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 6d3382b4..2d3bf53 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -799,6 +799,42 @@
                   ->store());
 }
 
+#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
+TEST_F(NetworkContextTest, DeviceBoundSessionsDefaultParam) {
+  mojom::NetworkContextParamsPtr context_params =
+      CreateNetworkContextParamsForTesting();
+
+  std::unique_ptr<NetworkContext> network_context =
+      CreateContextWithParams(std::move(context_params));
+  EXPECT_FALSE(
+      network_context->url_request_context()->device_bound_session_service());
+}
+
+TEST_F(NetworkContextTest, DeviceBoundSessionsEnableParam) {
+  mojom::NetworkContextParamsPtr context_params =
+      CreateNetworkContextParamsForTesting();
+  context_params->device_bound_sessions_enabled = true;
+
+  std::unique_ptr<NetworkContext> network_context =
+      CreateContextWithParams(std::move(context_params));
+  EXPECT_TRUE(
+      network_context->url_request_context()->device_bound_session_service());
+}
+
+TEST_F(NetworkContextTest, DeviceBoundSessionsDisableParam) {
+  mojom::NetworkContextParamsPtr context_params =
+      CreateNetworkContextParamsForTesting();
+  context_params->device_bound_sessions_enabled = false;
+
+  std::unique_ptr<NetworkContext> network_context =
+      CreateContextWithParams(std::move(context_params));
+  EXPECT_FALSE(
+      network_context->url_request_context()->device_bound_session_service());
+}
+
+#endif  // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
+
 TEST_F(NetworkContextTest, DisableNetworkErrorLogging) {
   base::test::ScopedFeatureList scoped_feature_list_;
   scoped_feature_list_.InitAndDisableFeature(features::kNetworkErrorLogging);
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index c774dfc..8d28c21 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -598,6 +598,9 @@
   // `cookie_encryption_provider` is called to obtain a valid set of keys for
   // cookie encryption.
   pending_remote<CookieEncryptionProvider>? cookie_encryption_provider;
+
+  // Enables Device Bound Session Credential for this network context.
+  bool device_bound_sessions_enabled = false;
 };
 
 struct NetworkConditions {
diff --git a/services/tracing/public/cpp/BUILD.gn b/services/tracing/public/cpp/BUILD.gn
index 41ba1d1..6b350436 100644
--- a/services/tracing/public/cpp/BUILD.gn
+++ b/services/tracing/public/cpp/BUILD.gn
@@ -106,6 +106,8 @@
       "traced_process_impl.h",
       "tracing_features.cc",
       "tracing_features.h",
+      "triggers_data_source.cc",
+      "triggers_data_source.h",
     ]
 
     if (is_android) {
diff --git a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
index d0d3ca8..827f57a 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
+++ b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
@@ -24,6 +24,7 @@
 #include "services/tracing/public/cpp/trace_startup.h"
 #include "services/tracing/public/cpp/traced_process_impl.h"
 #include "services/tracing/public/cpp/tracing_features.h"
+#include "services/tracing/public/cpp/triggers_data_source.h"
 #include "services/tracing/public/mojom/tracing_service.mojom.h"
 #include "third_party/perfetto/include/perfetto/tracing/tracing.h"
 
@@ -397,6 +398,7 @@
 
 #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
   base::TrackEvent::Register();
+  tracing::TriggersDataSource::Register();
   tracing::TracingSamplerProfiler::RegisterDataSource();
 #if BUILDFLAG(IS_WIN)
   if (enable_consumer) {
diff --git a/services/tracing/public/cpp/triggers_data_source.cc b/services/tracing/public/cpp/triggers_data_source.cc
new file mode 100644
index 0000000..f2a67552
--- /dev/null
+++ b/services/tracing/public/cpp/triggers_data_source.cc
@@ -0,0 +1,38 @@
+// 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 "services/tracing/public/cpp/triggers_data_source.h"
+
+#include "base/trace_event/trace_event.h"
+#include "base/tracing/trace_time.h"
+#include "third_party/perfetto/protos/perfetto/common/data_source_descriptor.gen.h"
+#include "third_party/perfetto/protos/perfetto/trace/trigger.pbzero.h"
+
+namespace tracing {
+
+void TriggersDataSource::Register() {
+  perfetto::DataSourceDescriptor desc;
+  desc.set_name("org.chromium.triggers");
+  perfetto::DataSource<TriggersDataSource>::Register(desc);
+}
+
+void TriggersDataSource::EmitTrigger(const std::string& trigger_name) {
+  Trace([trigger_name](TraceContext ctx) {
+    auto packet = ctx.NewTracePacket();
+    packet->set_timestamp(
+        TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
+    packet->set_timestamp_clock_id(base::tracing::kTraceClockId);
+    auto* trigger = packet->set_trigger();
+    trigger->set_trigger_name(trigger_name);
+  });
+}
+
+void TriggersDataSource::OnStart(const StartArgs&) {}
+void TriggersDataSource::OnStop(const StopArgs&) {}
+
+}  // namespace tracing
+
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(
+    COMPONENT_EXPORT(TRACING_CPP),
+    tracing::TriggersDataSource);
diff --git a/services/tracing/public/cpp/triggers_data_source.h b/services/tracing/public/cpp/triggers_data_source.h
new file mode 100644
index 0000000..d05f8b8
--- /dev/null
+++ b/services/tracing/public/cpp/triggers_data_source.h
@@ -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.
+
+#ifndef SERVICES_TRACING_PUBLIC_CPP_TRIGGERS_DATA_SOURCE_H_
+#define SERVICES_TRACING_PUBLIC_CPP_TRIGGERS_DATA_SOURCE_H_
+
+#include <string>
+
+#include "base/component_export.h"
+#include "third_party/perfetto/include/perfetto/tracing/data_source.h"
+
+namespace tracing {
+
+class COMPONENT_EXPORT(TRACING_CPP) TriggersDataSource
+    : public perfetto::DataSource<TriggersDataSource> {
+ public:
+  static void Register();
+  static void EmitTrigger(const std::string& trigger_name);
+
+  void OnStart(const StartArgs&) override;
+  void OnStop(const StopArgs&) override;
+};
+
+}  // namespace tracing
+
+#endif  // SERVICES_TRACING_PUBLIC_CPP_TRIGGERS_DATA_SOURCE_H_
diff --git a/testing/android/native_test/BUILD.gn b/testing/android/native_test/BUILD.gn
index 7b50772e..4dca3e22 100644
--- a/testing/android/native_test/BUILD.gn
+++ b/testing/android/native_test/BUILD.gn
@@ -31,7 +31,7 @@
     "native_browser_test_support.cc",
     "native_browser_test_support.h",
   ]
-  deps = [ ":native_test_jni" ]
+  deps = [ ":native_browser_test_jni" ]
   public_deps = [ "//base" ]
 }
 
@@ -68,14 +68,10 @@
     "//build/android:build_java",
     "//build/android/gtest_apk:native_test_instrumentation_test_runner_java",
     "//testing/android/reporter:reporter_java",
-    "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/jni_zero:jni_zero_java",
   ]
   srcjar_deps = [ ":native_test_jni" ]
   sources = [
-    "java/src/org/chromium/native_test/NativeBrowserTest.java",
-    "java/src/org/chromium/native_test/NativeBrowserTestActivity.java",
-    "java/src/org/chromium/native_test/NativeBrowserTestApplication.java",
     "java/src/org/chromium/native_test/NativeTest.java",
     "java/src/org/chromium/native_test/NativeTestApplication.java",
     "java/src/org/chromium/native_test/NativeUnitTest.java",
@@ -84,10 +80,29 @@
   ]
 }
 
-generate_jni("native_test_jni") {
+android_library("native_browser_test_java") {
   testonly = true
+
   sources = [
     "java/src/org/chromium/native_test/NativeBrowserTest.java",
-    "java/src/org/chromium/native_test/NativeTest.java",
+    "java/src/org/chromium/native_test/NativeBrowserTestActivity.java",
+    "java/src/org/chromium/native_test/NativeBrowserTestApplication.java",
   ]
+  srcjar_deps = [ ":native_browser_test_jni" ]
+  deps = [
+    ":native_test_java",
+    "//base:base_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
+    "//third_party/jni_zero:jni_zero_java",
+  ]
+}
+
+generate_jni("native_test_jni") {
+  testonly = true
+  sources = [ "java/src/org/chromium/native_test/NativeTest.java" ]
+}
+
+generate_jni("native_browser_test_jni") {
+  testonly = true
+  sources = [ "java/src/org/chromium/native_test/NativeBrowserTest.java" ]
 }
diff --git a/testing/android/native_test/native_browser_test_support.cc b/testing/android/native_test/native_browser_test_support.cc
index 56679d7..5cfa1af 100644
--- a/testing/android/native_test/native_browser_test_support.cc
+++ b/testing/android/native_test/native_browser_test_support.cc
@@ -9,7 +9,7 @@
 // These markers are read by the test runner script to generate test results.
 // It installs signal handlers to detect crashes.
 
-#include "testing/android/native_test/native_test_jni/NativeBrowserTest_jni.h"
+#include "testing/android/native_test/native_browser_test_jni/NativeBrowserTest_jni.h"
 
 namespace testing {
 namespace android {
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 6e9bdaa..7ebb4a1f 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -35483,7 +35483,6 @@
           "enable": true,
           "has_native_resultdb_integration": true
         },
-        "retry_only_failed_tests": true,
         "swarming": {
           "dimensions": {
             "device_os": "PQ3A.190801.002",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index f466416..469749aa 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5469,9 +5469,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5481,8 +5481,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
@@ -5625,9 +5625,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5637,8 +5637,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 9aca555..f99ba3ba 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -20313,9 +20313,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -20325,8 +20325,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
@@ -20463,9 +20463,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -20475,8 +20475,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 919506d9..bf1eb54 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -39981,9 +39981,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -39992,8 +39992,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
@@ -40131,9 +40131,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -40142,8 +40142,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
@@ -41480,9 +41480,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -41492,8 +41492,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
@@ -41636,9 +41636,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -41648,8 +41648,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
@@ -42961,9 +42961,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -42972,8 +42972,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
@@ -43111,9 +43111,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -43122,8 +43122,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index b2e0670c..f036293 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -16343,12 +16343,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -16358,8 +16358,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
@@ -16519,12 +16519,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 124.0.6365.0",
+        "description": "Run with ash-chrome version 124.0.6366.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -16534,8 +16534,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v124.0.6365.0",
-              "revision": "version:124.0.6365.0"
+              "location": "lacros_version_skew_tests_v124.0.6366.0",
+              "revision": "version:124.0.6366.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index ace6d60..ea25b30 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -306,7 +306,6 @@
           "can_use_on_swarming_builders": true,
           "dimensions": {
             "gpu": "10de",
-            "id": "build186-b7",
             "os": "Ubuntu-18.04",
             "pool": "chrome.tests.perf-fyi"
           },
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index eaf90abd..6bdab29 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -728,7 +728,7 @@
           "hard_timeout": 21600,
           "io_timeout": 21600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
+          "shards": 28
         },
         "test": "performance_test_suite_android_clank_trichrome_chrome_google_64_32_bundle",
         "trigger_script": {
diff --git a/testing/buildbot/chromium.perf.pinpoint.json b/testing/buildbot/chromium.perf.pinpoint.json
index 07743227..694d6f95 100644
--- a/testing/buildbot/chromium.perf.pinpoint.json
+++ b/testing/buildbot/chromium.perf.pinpoint.json
@@ -656,7 +656,7 @@
           "hard_timeout": 21600,
           "io_timeout": 21600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
+          "shards": 28
         },
         "test": "performance_test_suite_android_clank_trichrome_chrome_google_64_32_bundle",
         "trigger_script": {
diff --git a/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter b/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
index 3bac404d..7c7465e 100644
--- a/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
+++ b/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
@@ -14,6 +14,7 @@
 DesksTemplatesClientLacrosTest*
 Dictation*
 *DictationTest*
+EduCoexistenceWithArcRestrictionsMochaTest*
 ExtensionKeeplistAllowPerfettoTest*
 ExtensionKeeplistTest*
 # Note: Some of FileTasksBrowserTest* runs only on branded ash and lacros builds.
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 978efd4..977b124 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2066,10 +2066,6 @@
           '--gtest-also-run-pre-tests',
           '--test-launcher-filter-file=../../testing/buildbot/filters/android.pie_arm64.content_browsertests_coverage.filter',
         ],
-        # Only retry the individual failed tests instead of rerunning entire
-        # shards.
-        # crbug.com/1475852
-        'retry_only_failed_tests': True,
         'swarming': {
           # TODO(crbug.com/1462745): Temporarily increase the shard by 5 for the surging number of tests.
           # Recover to default number of shards(20) after the bug is fixed.
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 7ed04c46..b2cf259 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -307,16 +307,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 124.0.6365.0',
+    'description': 'Run with ash-chrome version 124.0.6366.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6365.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v124.0.6366.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v124.0.6365.0',
-          'revision': 'version:124.0.6365.0',
+          'location': 'lacros_version_skew_tests_v124.0.6366.0',
+          'revision': 'version:124.0.6366.0',
         },
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index c9a890f..b88c3169 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -50,21 +50,6 @@
             ]
         }
     ],
-    "AccessibilityJNIOptimizations": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AccessibilityJNIOptimizations"
-                    ]
-                }
-            ]
-        }
-    ],
     "AccessibilityPageZoom": [
         {
             "platforms": [
@@ -1138,26 +1123,6 @@
             ]
         }
     ],
-    "AugmentExistingImageLabels": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20230823",
-                    "enable_features": [
-                        "AugmentExistingImageLabels"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutoDisableAccessibility": [
         {
             "platforms": [
@@ -2934,6 +2899,28 @@
             ]
         }
     ],
+    "Canvas2DReclaimUnusedResources": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "Canvas2DReclaimUnusedResources"
+                    ]
+                }
+            ]
+        }
+    ],
     "CanvasOopWithoutGpuTileRaster": [
         {
             "platforms": [
@@ -5850,6 +5837,23 @@
             ]
         }
     ],
+    "DesktopWebAppUniversalInstallExperiment": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "WebAppUniversalInstallExperiment",
+                    "enable_features": [
+                        "WebAppUniversalInstall"
+                    ]
+                }
+            ]
+        }
+    ],
     "DestroyProfileOnBrowserClose": [
         {
             "platforms": [
@@ -14249,6 +14253,39 @@
             ],
             "experiments": [
                 {
+                    "name": "DriveRecentsWithDefaultRecency",
+                    "enable_features": [
+                        "LauncherContinueSectionWithRecentsRollout"
+                    ]
+                },
+                {
+                    "name": "DriveRecentsWithMixedLocalAndDriveFiles",
+                    "params": {
+                        "mix_local_and_drive": "true"
+                    },
+                    "enable_features": [
+                        "LauncherContinueSectionWithRecentsRollout"
+                    ]
+                },
+                {
+                    "name": "DriveRecentsWith14DayRecency",
+                    "params": {
+                        "max_recency_in_days": "14"
+                    },
+                    "enable_features": [
+                        "LauncherContinueSectionWithRecentsRollout"
+                    ]
+                },
+                {
+                    "name": "DriveRecentsWith30DayRecency",
+                    "params": {
+                        "max_recency_in_days": "30"
+                    },
+                    "enable_features": [
+                        "LauncherContinueSectionWithRecentsRollout"
+                    ]
+                },
+                {
                     "name": "EnabledWithContinueAndMultipleQueries_20220621",
                     "params": {
                         "enable_continue": "true",
@@ -15106,20 +15143,6 @@
             ],
             "experiments": [
                 {
-                    "name": "EnabledSubframeWithQueueing_20240108",
-                    "params": {
-                        "level": "subframe",
-                        "queueing_level": "full"
-                    },
-                    "enable_features": [
-                        "QueueNavigationsWhileWaitingForCommit",
-                        "RenderDocument"
-                    ],
-                    "disable_features": [
-                        "RenderDocumentCompositorReuse"
-                    ]
-                },
-                {
                     "name": "EnabledCrashedFrameWithQueueing_20240108",
                     "params": {
                         "level": "crashed-frame",
@@ -15148,6 +15171,20 @@
                     ]
                 },
                 {
+                    "name": "EnabledSubframeWithQueueing_20240108",
+                    "params": {
+                        "level": "subframe",
+                        "queueing_level": "full"
+                    },
+                    "enable_features": [
+                        "QueueNavigationsWhileWaitingForCommit",
+                        "RenderDocument"
+                    ],
+                    "disable_features": [
+                        "RenderDocumentCompositorReuse"
+                    ]
+                },
+                {
                     "name": "EnabledSubframeWithQueueingAndCompReuse_20240108",
                     "params": {
                         "level": "subframe",
diff --git a/third_party/angle b/third_party/angle
index 8080a73..d2cef82 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 8080a736f13854f806cb115e6ab78a8f4f33d08e
+Subproject commit d2cef82a8fd31e1792d845c79b1c85f8c820566c
diff --git a/third_party/beto-core/DIR_METADATA b/third_party/beto-core/DIR_METADATA
new file mode 100644
index 0000000..206ebd4d
--- /dev/null
+++ b/third_party/beto-core/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer {
+  component_id: 1342830
+}
+team_email: "beto-rust-devs@google.com"
diff --git a/third_party/bidimapper/README.chromium b/third_party/bidimapper/README.chromium
index 09be7772..836b4f14 100644
--- a/third_party/bidimapper/README.chromium
+++ b/third_party/bidimapper/README.chromium
@@ -1,10 +1,10 @@
 Name: Implementation of WebDriver BiDi standard
 Short Name: chromium-bidi
-URL: https://github.com/GoogleChromeLabs/chromium-bidi/archive/63373305f99d0498bcf8c82dd1dca1b7955a9cd6.zip
+URL: https://github.com/GoogleChromeLabs/chromium-bidi/archive/cefbaa5e7ee4dade7f51daa9c73ddbaff6c12868.zip
 Version: N/A
-Date: 2024-03-18
-Revision: 63373305f99d0498bcf8c82dd1dca1b7955a9cd6
-SHA-512: 30e20b435b45b4379ee96c7e588f605d823951327a5a9e84322e8d84f946fad18d513e07bf0f94b000344ba5211eb6c2736607a89bdb18b15b4539a755a17d7d
+Date: 2024-02-01
+Revision: cefbaa5e7ee4dade7f51daa9c73ddbaff6c12868
+SHA-512: 6fb504b214706e2f2e5865e4cee69db6f982d9a82ceedaf74532eafd471a5e6f68edaf10ddbbb58ba895078148baa27dcd79c6ccffe35bc9de9e8492281c5b37
 License: Apache 2.0
 License File: LICENSE
 Shipped: yes
@@ -31,33 +31,3 @@
 Local modifications are not allowed.
 The upstream is maintained by the Chromium developers.
 All the changes must be done there.
-
--------------------- DEPENDENCY DIVIDER --------------------
-
-Name: mitt
-URL: https://github.com/developit/mitt
-Version: 3.0.1
-License: MIT
-Security Critical: no
-Shipped: yes
-License File: licenses/LICENSE.mitt
-
--------------------- DEPENDENCY DIVIDER --------------------
-
-Name: urlpattern-polyfill
-URL: https://github.com/kenchris/urlpattern-polyfill
-Version: 10.0.0
-License: MIT
-Security Critical: no
-Shipped: yes
-License File: licenses/LICENSE.urlpattern_polyfill
-
--------------------- DEPENDENCY DIVIDER --------------------
-
-Name: zod
-URL: https://zod.dev
-Version: 3.22.4
-License: MIT
-Security Critical: no
-Shipped: yes
-License File: licenses/LICENSE.zod
diff --git a/third_party/bidimapper/mapper.js b/third_party/bidimapper/mapper.js
index ce38a9b..983af1f 100644
--- a/third_party/bidimapper/mapper.js
+++ b/third_party/bidimapper/mapper.js
@@ -1,16036 +1,5 @@
-var mapperTab = (function () {
-	'use strict';
-
-	var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
-
-	function getAugmentedNamespace(n) {
-	  if (n.__esModule) return n;
-	  var f = n.default;
-		if (typeof f == "function") {
-			var a = function a () {
-				if (this instanceof a) {
-	        return Reflect.construct(f, arguments, this.constructor);
-				}
-				return f.apply(this, arguments);
-			};
-			a.prototype = f.prototype;
-	  } else a = {};
-	  Object.defineProperty(a, '__esModule', {value: true});
-		Object.keys(n).forEach(function (k) {
-			var d = Object.getOwnPropertyDescriptor(n, k);
-			Object.defineProperty(a, k, d.get ? d : {
-				enumerable: true,
-				get: function () {
-					return n[k];
-				}
-			});
-		});
-		return a;
-	}
-
-	var bidiTab = {};
-
-	var BidiMapper = {};
-
-	var BidiServer$1 = {};
-
-	var EventEmitter$1 = {};
-
-	function mitt(n){return {all:n=n||new Map,on:function(t,e){var i=n.get(t);i?i.push(e):n.set(t,[e]);},off:function(t,e){var i=n.get(t);i&&(e?i.splice(i.indexOf(e)>>>0,1):n.set(t,[]));},emit:function(t,e){var i=n.get(t);i&&i.slice().map(function(n){n(e);}),(i=n.get("*"))&&i.slice().map(function(n){n(t,e);});}}}
-
-	var mitt$1 = /*#__PURE__*/Object.freeze({
-		__proto__: null,
-		default: mitt
-	});
-
-	var require$$0 = /*@__PURE__*/getAugmentedNamespace(mitt$1);
-
-	var __importDefault$2 = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
-	    return (mod && mod.__esModule) ? mod : { "default": mod };
-	};
-	Object.defineProperty(EventEmitter$1, "__esModule", { value: true });
-	EventEmitter$1.EventEmitter = void 0;
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	const mitt_1 = __importDefault$2(require$$0);
-	class EventEmitter {
-	    #emitter = (0, mitt_1.default)();
-	    on(type, handler) {
-	        this.#emitter.on(type, handler);
-	        return this;
-	    }
-	    /**
-	     * Like `on` but the listener will only be fired once and then it will be removed.
-	     * @param event The event you'd like to listen to
-	     * @param handler The handler function to run when the event occurs
-	     * @return `this` to enable chaining method calls.
-	     */
-	    once(event, handler) {
-	        const onceHandler = (eventData) => {
-	            handler(eventData);
-	            this.off(event, onceHandler);
-	        };
-	        return this.on(event, onceHandler);
-	    }
-	    off(type, handler) {
-	        this.#emitter.off(type, handler);
-	        return this;
-	    }
-	    /**
-	     * Emits an event and call any associated listeners.
-	     *
-	     * @param event The event to emit.
-	     * @param eventData Any data to emit with the event.
-	     * @return `true` if there are any listeners, `false` otherwise.
-	     */
-	    emit(event, eventData) {
-	        this.#emitter.emit(event, eventData);
-	    }
-	    /**
-	     * Removes all listeners. If given an event argument, it will remove only
-	     * listeners for that event.
-	     * @param event - the event to remove listeners for.
-	     * @returns `this` to enable you to chain method calls.
-	     */
-	    removeAllListeners(event) {
-	        if (event) {
-	            this.#emitter.all.delete(event);
-	        }
-	        else {
-	            this.#emitter.all.clear();
-	        }
-	        return this;
-	    }
-	}
-	EventEmitter$1.EventEmitter = EventEmitter;
-
-	var log$1 = {};
-
-	/**
-	 * Copyright 2021 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(log$1, "__esModule", { value: true });
-	log$1.LogType = void 0;
-	var LogType;
-	(function (LogType) {
-	    // keep-sorted start
-	    LogType["bidi"] = "bidi";
-	    LogType["cdp"] = "cdp";
-	    LogType["debug"] = "debug";
-	    LogType["debugError"] = "debug:error";
-	    LogType["debugInfo"] = "debug:info";
-	    // keep-sorted end
-	})(LogType || (log$1.LogType = LogType = {}));
-
-	var ProcessingQueue$1 = {};
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(ProcessingQueue$1, "__esModule", { value: true });
-	ProcessingQueue$1.ProcessingQueue = void 0;
-	const log_js_1$d = log$1;
-	class ProcessingQueue {
-	    static LOGGER_PREFIX = `${log_js_1$d.LogType.debug}:queue`;
-	    #logger;
-	    #processor;
-	    #queue = [];
-	    // Flag to keep only 1 active processor.
-	    #isProcessing = false;
-	    constructor(processor, logger) {
-	        this.#processor = processor;
-	        this.#logger = logger;
-	    }
-	    add(entry, name) {
-	        this.#queue.push([entry, name]);
-	        // No need in waiting. Just initialize processor if needed.
-	        void this.#processIfNeeded();
-	    }
-	    async #processIfNeeded() {
-	        if (this.#isProcessing) {
-	            return;
-	        }
-	        this.#isProcessing = true;
-	        while (this.#queue.length > 0) {
-	            const arrayEntry = this.#queue.shift();
-	            if (!arrayEntry) {
-	                continue;
-	            }
-	            const [entryPromise, name] = arrayEntry;
-	            this.#logger?.(ProcessingQueue.LOGGER_PREFIX, 'Processing event:', name);
-	            await entryPromise
-	                .then((entry) => {
-	                if (entry.kind === 'error') {
-	                    this.#logger?.(log_js_1$d.LogType.debugError, 'Event threw before sending:', entry.error.message, entry.error.stack);
-	                    return;
-	                }
-	                return this.#processor(entry.value);
-	            })
-	                .catch((error) => {
-	                this.#logger?.(log_js_1$d.LogType.debugError, 'Event was not processed:', error?.message);
-	            });
-	        }
-	        this.#isProcessing = false;
-	    }
-	}
-	ProcessingQueue$1.ProcessingQueue = ProcessingQueue;
-
-	var CommandProcessor$1 = {};
-
-	var protocol = {};
-
-	var cdp = {};
-
-	Object.defineProperty(cdp, "__esModule", { value: true });
-
-	var chromiumBidi = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(chromiumBidi, "__esModule", { value: true });
-	chromiumBidi.EVENT_NAMES = chromiumBidi.Network = chromiumBidi.BrowsingContext = chromiumBidi.Log = chromiumBidi.Script = chromiumBidi.BiDiModule = void 0;
-	// keep-sorted end
-	var BiDiModule;
-	(function (BiDiModule) {
-	    // keep-sorted start
-	    BiDiModule["Browser"] = "browser";
-	    BiDiModule["BrowsingContext"] = "browsingContext";
-	    BiDiModule["Cdp"] = "cdp";
-	    BiDiModule["Input"] = "input";
-	    BiDiModule["Log"] = "log";
-	    BiDiModule["Network"] = "network";
-	    BiDiModule["Script"] = "script";
-	    BiDiModule["Session"] = "session";
-	    // keep-sorted end
-	})(BiDiModule || (chromiumBidi.BiDiModule = BiDiModule = {}));
-	var Script$1;
-	(function (Script) {
-	    (function (EventNames) {
-	        // keep-sorted start
-	        EventNames["Message"] = "script.message";
-	        EventNames["RealmCreated"] = "script.realmCreated";
-	        EventNames["RealmDestroyed"] = "script.realmDestroyed";
-	        // keep-sorted end
-	    })(Script.EventNames || (Script.EventNames = {}));
-	})(Script$1 || (chromiumBidi.Script = Script$1 = {}));
-	var Log;
-	(function (Log) {
-	    (function (EventNames) {
-	        EventNames["LogEntryAdded"] = "log.entryAdded";
-	    })(Log.EventNames || (Log.EventNames = {}));
-	})(Log || (chromiumBidi.Log = Log = {}));
-	var BrowsingContext$1;
-	(function (BrowsingContext) {
-	    (function (EventNames) {
-	        // keep-sorted start
-	        EventNames["ContextCreated"] = "browsingContext.contextCreated";
-	        EventNames["ContextDestroyed"] = "browsingContext.contextDestroyed";
-	        EventNames["DomContentLoaded"] = "browsingContext.domContentLoaded";
-	        EventNames["DownloadWillBegin"] = "browsingContext.downloadWillBegin";
-	        EventNames["FragmentNavigated"] = "browsingContext.fragmentNavigated";
-	        EventNames["Load"] = "browsingContext.load";
-	        EventNames["NavigationAborted"] = "browsingContext.navigationAborted";
-	        EventNames["NavigationFailed"] = "browsingContext.navigationFailed";
-	        EventNames["NavigationStarted"] = "browsingContext.navigationStarted";
-	        EventNames["UserPromptClosed"] = "browsingContext.userPromptClosed";
-	        EventNames["UserPromptOpened"] = "browsingContext.userPromptOpened";
-	        // keep-sorted end
-	    })(BrowsingContext.EventNames || (BrowsingContext.EventNames = {}));
-	})(BrowsingContext$1 || (chromiumBidi.BrowsingContext = BrowsingContext$1 = {}));
-	var Network$1;
-	(function (Network) {
-	    (function (EventNames) {
-	        // keep-sorted start
-	        EventNames["AuthRequired"] = "network.authRequired";
-	        EventNames["BeforeRequestSent"] = "network.beforeRequestSent";
-	        EventNames["FetchError"] = "network.fetchError";
-	        EventNames["ResponseCompleted"] = "network.responseCompleted";
-	        EventNames["ResponseStarted"] = "network.responseStarted";
-	        // keep-sorted end
-	    })(Network.EventNames || (Network.EventNames = {}));
-	})(Network$1 || (chromiumBidi.Network = Network$1 = {}));
-	chromiumBidi.EVENT_NAMES = new Set([
-	    // keep-sorted start
-	    ...Object.values(BiDiModule),
-	    ...Object.values(BrowsingContext$1.EventNames),
-	    ...Object.values(Log.EventNames),
-	    ...Object.values(Network$1.EventNames),
-	    ...Object.values(Script$1.EventNames),
-	    // keep-sorted end
-	]);
-
-	var webdriverBidi$1 = {};
-
-	/**
-	 * Copyright 2024 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(webdriverBidi$1, "__esModule", { value: true });
-
-	var ErrorResponse = {};
-
-	Object.defineProperty(ErrorResponse, "__esModule", { value: true });
-	ErrorResponse.UnderspecifiedStoragePartitionException = ErrorResponse.UnableToSetFileInputException = ErrorResponse.UnableToSetCookieException = ErrorResponse.NoSuchStoragePartitionException = ErrorResponse.UnsupportedOperationException = ErrorResponse.UnableToCloseBrowserException = ErrorResponse.UnableToCaptureScreenException = ErrorResponse.UnknownErrorException = ErrorResponse.UnknownCommandException = ErrorResponse.SessionNotCreatedException = ErrorResponse.NoSuchUserContextException = ErrorResponse.NoSuchScriptException = ErrorResponse.NoSuchRequestException = ErrorResponse.NoSuchNodeException = ErrorResponse.NoSuchInterceptException = ErrorResponse.NoSuchHistoryEntryException = ErrorResponse.NoSuchHandleException = ErrorResponse.NoSuchFrameException = ErrorResponse.NoSuchElementException = ErrorResponse.NoSuchAlertException = ErrorResponse.MoveTargetOutOfBoundsException = ErrorResponse.InvalidSessionIdException = ErrorResponse.InvalidSelectorException = ErrorResponse.InvalidArgumentException = ErrorResponse.Exception = void 0;
-	class Exception {
-	    error;
-	    message;
-	    stacktrace;
-	    constructor(error, message, stacktrace) {
-	        this.error = error;
-	        this.message = message;
-	        this.stacktrace = stacktrace;
-	    }
-	    toErrorResponse(commandId) {
-	        return {
-	            type: 'error',
-	            id: commandId,
-	            error: this.error,
-	            message: this.message,
-	            stacktrace: this.stacktrace,
-	        };
-	    }
-	}
-	ErrorResponse.Exception = Exception;
-	class InvalidArgumentException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("invalid argument" /* ErrorCode.InvalidArgument */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.InvalidArgumentException = InvalidArgumentException;
-	class InvalidSelectorException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("invalid selector" /* ErrorCode.InvalidSelector */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.InvalidSelectorException = InvalidSelectorException;
-	class InvalidSessionIdException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("invalid session id" /* ErrorCode.InvalidSessionId */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.InvalidSessionIdException = InvalidSessionIdException;
-	class MoveTargetOutOfBoundsException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("move target out of bounds" /* ErrorCode.MoveTargetOutOfBounds */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.MoveTargetOutOfBoundsException = MoveTargetOutOfBoundsException;
-	class NoSuchAlertException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such alert" /* ErrorCode.NoSuchAlert */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchAlertException = NoSuchAlertException;
-	class NoSuchElementException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such element" /* ErrorCode.NoSuchElement */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchElementException = NoSuchElementException;
-	class NoSuchFrameException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such frame" /* ErrorCode.NoSuchFrame */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchFrameException = NoSuchFrameException;
-	class NoSuchHandleException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such handle" /* ErrorCode.NoSuchHandle */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchHandleException = NoSuchHandleException;
-	class NoSuchHistoryEntryException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such history entry" /* ErrorCode.NoSuchHistoryEntry */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchHistoryEntryException = NoSuchHistoryEntryException;
-	class NoSuchInterceptException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such intercept" /* ErrorCode.NoSuchIntercept */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchInterceptException = NoSuchInterceptException;
-	class NoSuchNodeException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such node" /* ErrorCode.NoSuchNode */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchNodeException = NoSuchNodeException;
-	class NoSuchRequestException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such request" /* ErrorCode.NoSuchRequest */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchRequestException = NoSuchRequestException;
-	class NoSuchScriptException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such script" /* ErrorCode.NoSuchScript */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchScriptException = NoSuchScriptException;
-	class NoSuchUserContextException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such user context" /* ErrorCode.NoSuchUserContext */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchUserContextException = NoSuchUserContextException;
-	class SessionNotCreatedException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("session not created" /* ErrorCode.SessionNotCreated */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.SessionNotCreatedException = SessionNotCreatedException;
-	class UnknownCommandException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("unknown command" /* ErrorCode.UnknownCommand */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.UnknownCommandException = UnknownCommandException;
-	class UnknownErrorException extends Exception {
-	    constructor(message, stacktrace = new Error().stack) {
-	        super("unknown error" /* ErrorCode.UnknownError */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.UnknownErrorException = UnknownErrorException;
-	class UnableToCaptureScreenException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("unable to capture screen" /* ErrorCode.UnableToCaptureScreen */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.UnableToCaptureScreenException = UnableToCaptureScreenException;
-	class UnableToCloseBrowserException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("unable to close browser" /* ErrorCode.UnableToCloseBrowser */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.UnableToCloseBrowserException = UnableToCloseBrowserException;
-	class UnsupportedOperationException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("unsupported operation" /* ErrorCode.UnsupportedOperation */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.UnsupportedOperationException = UnsupportedOperationException;
-	class NoSuchStoragePartitionException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("no such storage partition" /* ErrorCode.NoSuchStoragePartition */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.NoSuchStoragePartitionException = NoSuchStoragePartitionException;
-	class UnableToSetCookieException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("unable to set cookie" /* ErrorCode.UnableToSetCookie */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.UnableToSetCookieException = UnableToSetCookieException;
-	class UnableToSetFileInputException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("unable to set file input" /* ErrorCode.UnableToSetFileInput */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.UnableToSetFileInputException = UnableToSetFileInputException;
-	class UnderspecifiedStoragePartitionException extends Exception {
-	    constructor(message, stacktrace) {
-	        super("underspecified storage partition" /* ErrorCode.UnderspecifiedStoragePartition */, message, stacktrace);
-	    }
-	}
-	ErrorResponse.UnderspecifiedStoragePartitionException = UnderspecifiedStoragePartitionException;
-
-	var webdriverBidiPermissions$1 = {};
-
-	/**
-	 * Copyright 2024 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(webdriverBidiPermissions$1, "__esModule", { value: true });
-
-	(function (exports) {
-		var __createBinding = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
-		    if (k2 === undefined) k2 = k;
-		    var desc = Object.getOwnPropertyDescriptor(m, k);
-		    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
-		      desc = { enumerable: true, get: function() { return m[k]; } };
-		    }
-		    Object.defineProperty(o, k2, desc);
-		}) : (function(o, m, k, k2) {
-		    if (k2 === undefined) k2 = k;
-		    o[k2] = m[k];
-		}));
-		var __setModuleDefault = (commonjsGlobal && commonjsGlobal.__setModuleDefault) || (Object.create ? (function(o, v) {
-		    Object.defineProperty(o, "default", { enumerable: true, value: v });
-		}) : function(o, v) {
-		    o["default"] = v;
-		});
-		var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
-		    if (mod && mod.__esModule) return mod;
-		    var result = {};
-		    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
-		    __setModuleDefault(result, mod);
-		    return result;
-		};
-		var __exportStar = (commonjsGlobal && commonjsGlobal.__exportStar) || function(m, exports) {
-		    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
-		};
-		Object.defineProperty(exports, "__esModule", { value: true });
-		exports.ChromiumBidi = exports.Cdp = void 0;
-		/**
-		 * Copyright 2023 Google LLC.
-		 * Copyright (c) Microsoft Corporation.
-		 *
-		 * 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
-		 *
-		 *     http://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.
-		 */
-		exports.Cdp = __importStar(cdp);
-		exports.ChromiumBidi = __importStar(chromiumBidi);
-		__exportStar(webdriverBidi$1, exports);
-		__exportStar(ErrorResponse, exports);
-		__exportStar(webdriverBidiPermissions$1, exports);
-		
-	} (protocol));
-
-	var BidiNoOpParser$1 = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(BidiNoOpParser$1, "__esModule", { value: true });
-	BidiNoOpParser$1.BidiNoOpParser = void 0;
-	class BidiNoOpParser {
-	    // Browser domain
-	    // keep-sorted start block=yes
-	    parseRemoveUserContextParams(params) {
-	        return params;
-	    }
-	    // keep-sorted end
-	    // Browsing Context domain
-	    // keep-sorted start block=yes
-	    parseActivateParams(params) {
-	        return params;
-	    }
-	    parseCaptureScreenshotParams(params) {
-	        return params;
-	    }
-	    parseCloseParams(params) {
-	        return params;
-	    }
-	    parseCreateParams(params) {
-	        return params;
-	    }
-	    parseGetTreeParams(params) {
-	        return params;
-	    }
-	    parseHandleUserPromptParams(params) {
-	        return params;
-	    }
-	    parseLocateNodesParams(params) {
-	        return params;
-	    }
-	    parseNavigateParams(params) {
-	        return params;
-	    }
-	    parsePrintParams(params) {
-	        return params;
-	    }
-	    parseReloadParams(params) {
-	        return params;
-	    }
-	    parseSetViewportParams(params) {
-	        return params;
-	    }
-	    parseTraverseHistoryParams(params) {
-	        return params;
-	    }
-	    // keep-sorted end
-	    // CDP domain
-	    // keep-sorted start block=yes
-	    parseGetSessionParams(params) {
-	        return params;
-	    }
-	    parseResolveRealmParams(params) {
-	        return params;
-	    }
-	    parseSendCommandParams(params) {
-	        return params;
-	    }
-	    // keep-sorted end
-	    // Script domain
-	    // keep-sorted start block=yes
-	    parseAddPreloadScriptParams(params) {
-	        return params;
-	    }
-	    parseCallFunctionParams(params) {
-	        return params;
-	    }
-	    parseDisownParams(params) {
-	        return params;
-	    }
-	    parseEvaluateParams(params) {
-	        return params;
-	    }
-	    parseGetRealmsParams(params) {
-	        return params;
-	    }
-	    parseRemovePreloadScriptParams(params) {
-	        return params;
-	    }
-	    // keep-sorted end
-	    // Input domain
-	    // keep-sorted start block=yes
-	    parsePerformActionsParams(params) {
-	        return params;
-	    }
-	    parseReleaseActionsParams(params) {
-	        return params;
-	    }
-	    parseSetFilesParams(params) {
-	        return params;
-	    }
-	    // keep-sorted end
-	    // Network domain
-	    // keep-sorted start block=yes
-	    parseAddInterceptParams(params) {
-	        return params;
-	    }
-	    parseContinueRequestParams(params) {
-	        return params;
-	    }
-	    parseContinueResponseParams(params) {
-	        return params;
-	    }
-	    parseContinueWithAuthParams(params) {
-	        return params;
-	    }
-	    parseFailRequestParams(params) {
-	        return params;
-	    }
-	    parseProvideResponseParams(params) {
-	        return params;
-	    }
-	    parseRemoveInterceptParams(params) {
-	        return params;
-	    }
-	    // keep-sorted end
-	    // Permissions domain
-	    // keep-sorted start block=yes
-	    parseSetPermissionsParams(params) {
-	        return params;
-	    }
-	    // keep-sorted end
-	    // Session domain
-	    // keep-sorted start block=yes
-	    parseSubscribeParams(params) {
-	        return params;
-	    }
-	    // keep-sorted end
-	    // Storage domain
-	    // keep-sorted start block=yes
-	    parseDeleteCookiesParams(params) {
-	        return params;
-	    }
-	    parseGetCookiesParams(params) {
-	        return params;
-	    }
-	    parseSetCookieParams(params) {
-	        return params;
-	    }
-	}
-	BidiNoOpParser$1.BidiNoOpParser = BidiNoOpParser;
-
-	var BrowserProcessor$1 = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(BrowserProcessor$1, "__esModule", { value: true });
-	BrowserProcessor$1.BrowserProcessor = void 0;
-	const protocol_js_1$m = protocol;
-	class BrowserProcessor {
-	    #browserCdpClient;
-	    constructor(browserCdpClient) {
-	        this.#browserCdpClient = browserCdpClient;
-	    }
-	    close() {
-	        // Ensure that it is put at the end of the event loop.
-	        // This way we send back the response before closing the tab.
-	        setTimeout(() => this.#browserCdpClient.sendCommand('Browser.close'), 0);
-	        return {};
-	    }
-	    async createUserContext(params) {
-	        const request = {
-	            proxyServer: params['goog:proxyServer'] ?? undefined,
-	        };
-	        const proxyBypassList = params['goog:proxyBypassList'] ?? undefined;
-	        if (proxyBypassList) {
-	            request.proxyBypassList = proxyBypassList.join(',');
-	        }
-	        const context = await this.#browserCdpClient.sendCommand('Target.createBrowserContext', request);
-	        return {
-	            userContext: context.browserContextId,
-	        };
-	    }
-	    async removeUserContext(params) {
-	        const userContext = params.userContext;
-	        if (userContext === 'default') {
-	            throw new protocol_js_1$m.InvalidArgumentException('`default` user context cannot be removed');
-	        }
-	        try {
-	            await this.#browserCdpClient.sendCommand('Target.disposeBrowserContext', {
-	                browserContextId: userContext,
-	            });
-	        }
-	        catch (err) {
-	            // https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/protocol/target_handler.cc;l=1424;drc=c686e8f4fd379312469fe018f5c390e9c8f20d0d
-	            if (err.message.startsWith('Failed to find context with id')) {
-	                throw new protocol_js_1$m.NoSuchUserContextException(err.message);
-	            }
-	            throw err;
-	        }
-	        return {};
-	    }
-	    async getUserContexts() {
-	        const result = await this.#browserCdpClient.sendCommand('Target.getBrowserContexts');
-	        return {
-	            userContexts: [
-	                {
-	                    userContext: 'default',
-	                },
-	                ...result.browserContextIds.map((id) => {
-	                    return {
-	                        userContext: id,
-	                    };
-	                }),
-	            ],
-	        };
-	    }
-	}
-	BrowserProcessor$1.BrowserProcessor = BrowserProcessor;
-
-	var CdpProcessor$1 = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(CdpProcessor$1, "__esModule", { value: true });
-	CdpProcessor$1.CdpProcessor = void 0;
-	const protocol_js_1$l = protocol;
-	class CdpProcessor {
-	    #browsingContextStorage;
-	    #realmStorage;
-	    #cdpConnection;
-	    #browserCdpClient;
-	    constructor(browsingContextStorage, realmStorage, cdpConnection, browserCdpClient) {
-	        this.#browsingContextStorage = browsingContextStorage;
-	        this.#realmStorage = realmStorage;
-	        this.#cdpConnection = cdpConnection;
-	        this.#browserCdpClient = browserCdpClient;
-	    }
-	    getSession(params) {
-	        const context = params.context;
-	        const sessionId = this.#browsingContextStorage.getContext(context).cdpTarget.cdpSessionId;
-	        if (sessionId === undefined) {
-	            return {};
-	        }
-	        return { session: sessionId };
-	    }
-	    resolveRealm(params) {
-	        const context = params.realm;
-	        const realm = this.#realmStorage.getRealm({ realmId: context });
-	        if (realm === undefined) {
-	            throw new protocol_js_1$l.UnknownErrorException(`Could not find realm ${params.realm}`);
-	        }
-	        return { executionContextId: realm.executionContextId };
-	    }
-	    async sendCommand(params) {
-	        const client = params.session
-	            ? this.#cdpConnection.getCdpClient(params.session)
-	            : this.#browserCdpClient;
-	        const result = await client.sendCommand(params.method, params.params);
-	        return {
-	            result,
-	            session: params.session,
-	        };
-	    }
-	}
-	CdpProcessor$1.CdpProcessor = CdpProcessor;
-
-	var BrowsingContextProcessor$1 = {};
-
-	var WorkerRealm$1 = {};
-
-	var Realm$1 = {};
-
-	var uuid = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(uuid, "__esModule", { value: true });
-	uuid.uuidv4 = void 0;
-	/**
-	 * Generates a random v4 UUID, as specified in RFC4122.
-	 *
-	 * Uses the native Web Crypto API if available, otherwise falls back to a
-	 * polyfill.
-	 *
-	 * Example: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
-	 */
-	function uuidv4() {
-	    // Available only in secure contexts
-	    // https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
-	    if ('crypto' in globalThis && 'randomUUID' in globalThis.crypto) {
-	        // Node with
-	        // https://nodejs.org/dist/latest-v20.x/docs/api/globals.html#crypto_1 or
-	        // secure browser context.
-	        return globalThis.crypto.randomUUID();
-	    }
-	    const randomValues = new Uint8Array(16);
-	    if ('crypto' in globalThis && 'getRandomValues' in globalThis.crypto) {
-	        // Node with
-	        // https://nodejs.org/dist/latest-v20.x/docs/api/globals.html#crypto_1 or
-	        // browser.
-	        globalThis.crypto.getRandomValues(randomValues);
-	    }
-	    else {
-	        // Node without
-	        // https://nodejs.org/dist/latest-v20.x/docs/api/globals.html#crypto_1.
-	        // eslint-disable-next-line @typescript-eslint/no-var-requires
-	        require('crypto').webcrypto.getRandomValues(randomValues);
-	    }
-	    // Set version (4) and variant (RFC4122) bits.
-	    randomValues[6] = (randomValues[6] & 0x0f) | 0x40;
-	    randomValues[8] = (randomValues[8] & 0x3f) | 0x80;
-	    const bytesToHex = (bytes) => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
-	    return [
-	        bytesToHex(randomValues.subarray(0, 4)),
-	        bytesToHex(randomValues.subarray(4, 6)),
-	        bytesToHex(randomValues.subarray(6, 8)),
-	        bytesToHex(randomValues.subarray(8, 10)),
-	        bytesToHex(randomValues.subarray(10, 16)),
-	    ].join('-');
-	}
-	uuid.uuidv4 = uuidv4;
-
-	var ChannelProxy$1 = {};
-
-	/*
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 *
-	 */
-	Object.defineProperty(ChannelProxy$1, "__esModule", { value: true });
-	ChannelProxy$1.ChannelProxy = void 0;
-	const protocol_js_1$k = protocol;
-	const log_js_1$c = log$1;
-	const uuid_js_1$3 = uuid;
-	/**
-	 * Used to send messages from realm to BiDi user.
-	 */
-	class ChannelProxy {
-	    #properties;
-	    #id = (0, uuid_js_1$3.uuidv4)();
-	    #logger;
-	    constructor(channel, logger) {
-	        this.#properties = channel;
-	        this.#logger = logger;
-	    }
-	    /**
-	     * Creates a channel proxy in the given realm, initialises listener and
-	     * returns a handle to `sendMessage` delegate.
-	     */
-	    async init(realm, eventManager) {
-	        const channelHandle = await ChannelProxy.#createAndGetHandleInRealm(realm);
-	        const sendMessageHandle = await ChannelProxy.#createSendMessageHandle(realm, channelHandle);
-	        void this.#startListener(realm, channelHandle, eventManager);
-	        return sendMessageHandle;
-	    }
-	    /** Gets a ChannelProxy from window and returns its handle. */
-	    async startListenerFromWindow(realm, eventManager) {
-	        try {
-	            const channelHandle = await this.#getHandleFromWindow(realm);
-	            void this.#startListener(realm, channelHandle, eventManager);
-	        }
-	        catch (error) {
-	            this.#logger?.(log_js_1$c.LogType.debugError, error);
-	        }
-	    }
-	    /**
-	     * Evaluation string which creates a ChannelProxy object on the client side.
-	     */
-	    static #createChannelProxyEvalStr() {
-	        const functionStr = String(() => {
-	            const queue = [];
-	            let queueNonEmptyResolver = null;
-	            return {
-	                /**
-	                 * Gets a promise, which is resolved as soon as a message occurs
-	                 * in the queue.
-	                 */
-	                async getMessage() {
-	                    const onMessage = queue.length > 0
-	                        ? Promise.resolve()
-	                        : new Promise((resolve) => {
-	                            queueNonEmptyResolver = resolve;
-	                        });
-	                    await onMessage;
-	                    return queue.shift();
-	                },
-	                /**
-	                 * Adds a message to the queue.
-	                 * Resolves the pending promise if needed.
-	                 */
-	                sendMessage(message) {
-	                    queue.push(message);
-	                    if (queueNonEmptyResolver !== null) {
-	                        queueNonEmptyResolver();
-	                        queueNonEmptyResolver = null;
-	                    }
-	                },
-	            };
-	        });
-	        return `(${functionStr})()`;
-	    }
-	    /** Creates a ChannelProxy in the given realm. */
-	    static async #createAndGetHandleInRealm(realm) {
-	        const createChannelHandleResult = await realm.cdpClient.sendCommand('Runtime.evaluate', {
-	            expression: this.#createChannelProxyEvalStr(),
-	            contextId: realm.executionContextId,
-	            serializationOptions: {
-	                serialization: "idOnly" /* Protocol.Runtime.SerializationOptionsSerialization.IdOnly */,
-	            },
-	        });
-	        if (createChannelHandleResult.exceptionDetails ||
-	            createChannelHandleResult.result.objectId === undefined) {
-	            throw new Error(`Cannot create channel`);
-	        }
-	        return createChannelHandleResult.result.objectId;
-	    }
-	    /** Gets a handle to `sendMessage` delegate from the ChannelProxy handle. */
-	    static async #createSendMessageHandle(realm, channelHandle) {
-	        const sendMessageArgResult = await realm.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	            functionDeclaration: String((channelHandle) => {
-	                return channelHandle.sendMessage;
-	            }),
-	            arguments: [{ objectId: channelHandle }],
-	            executionContextId: realm.executionContextId,
-	            serializationOptions: {
-	                serialization: "idOnly" /* Protocol.Runtime.SerializationOptionsSerialization.IdOnly */,
-	            },
-	        });
-	        // TODO: check for exceptionDetails.
-	        return sendMessageArgResult.result.objectId;
-	    }
-	    /** Starts listening for the channel events of the provided ChannelProxy. */
-	    async #startListener(realm, channelHandle, eventManager) {
-	        // noinspection InfiniteLoopJS
-	        for (;;) {
-	            try {
-	                const message = await realm.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	                    functionDeclaration: String(async (channelHandle) => await channelHandle.getMessage()),
-	                    arguments: [
-	                        {
-	                            objectId: channelHandle,
-	                        },
-	                    ],
-	                    awaitPromise: true,
-	                    executionContextId: realm.executionContextId,
-	                    serializationOptions: {
-	                        serialization: "deep" /* Protocol.Runtime.SerializationOptionsSerialization.Deep */,
-	                        maxDepth: this.#properties.serializationOptions?.maxObjectDepth ??
-	                            undefined,
-	                    },
-	                });
-	                if (message.exceptionDetails) {
-	                    throw message.exceptionDetails;
-	                }
-	                for (const browsingContext of realm.associatedBrowsingContexts) {
-	                    eventManager.registerEvent({
-	                        type: 'event',
-	                        method: protocol_js_1$k.ChromiumBidi.Script.EventNames.Message,
-	                        params: {
-	                            channel: this.#properties.channel,
-	                            data: realm.cdpToBidiValue(message, this.#properties.ownership ?? "none" /* Script.ResultOwnership.None */),
-	                            source: realm.source,
-	                        },
-	                    }, browsingContext.id);
-	                }
-	            }
-	            catch (error) {
-	                // If an error is thrown, then the channel is permanently broken, so we
-	                // exit the loop.
-	                this.#logger?.(log_js_1$c.LogType.debugError, error);
-	                break;
-	            }
-	        }
-	    }
-	    /**
-	     * Returns a handle of ChannelProxy from window's property which was set there
-	     * by `getEvalInWindowStr`. If window property is not set yet, sets a promise
-	     * resolver to the window property, so that `getEvalInWindowStr` can resolve
-	     * the promise later on with the channel.
-	     * This is needed because `getEvalInWindowStr` can be called before or
-	     * after this method.
-	     */
-	    async #getHandleFromWindow(realm) {
-	        const channelHandleResult = await realm.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	            functionDeclaration: String((id) => {
-	                const w = window;
-	                if (w[id] === undefined) {
-	                    // The channelProxy is not created yet. Create a promise, put the
-	                    // resolver to window property and return the promise.
-	                    // `getEvalInWindowStr` will resolve the promise later.
-	                    return new Promise((resolve) => (w[id] = resolve));
-	                }
-	                // The channelProxy is already created by `getEvalInWindowStr` and
-	                // is set into window property. Return it.
-	                const channelProxy = w[id];
-	                delete w[id];
-	                return channelProxy;
-	            }),
-	            arguments: [{ value: this.#id }],
-	            executionContextId: realm.executionContextId,
-	            awaitPromise: true,
-	            serializationOptions: {
-	                serialization: "idOnly" /* Protocol.Runtime.SerializationOptionsSerialization.IdOnly */,
-	            },
-	        });
-	        if (channelHandleResult.exceptionDetails !== undefined ||
-	            channelHandleResult.result.objectId === undefined) {
-	            throw new Error(`ChannelHandle not found in window["${this.#id}"]`);
-	        }
-	        return channelHandleResult.result.objectId;
-	    }
-	    /**
-	     * String to be evaluated to create a ProxyChannel and put it to window.
-	     * Returns the delegate `sendMessage`. Used to provide an argument for preload
-	     * script. Does the following:
-	     * 1. Creates a ChannelProxy.
-	     * 2. Puts the ChannelProxy to window['${this.#id}'] or resolves the promise
-	     *    by calling delegate stored in window['${this.#id}'].
-	     *    This is needed because `#getHandleFromWindow` can be called before or
-	     *    after this method.
-	     * 3. Returns the delegate `sendMessage` of the created ChannelProxy.
-	     */
-	    getEvalInWindowStr() {
-	        const delegate = String((id, channelProxy) => {
-	            const w = window;
-	            if (w[id] === undefined) {
-	                // `#getHandleFromWindow` is not initialized yet, and will get the
-	                // channelProxy later.
-	                w[id] = channelProxy;
-	            }
-	            else {
-	                // `#getHandleFromWindow` is already set a delegate to window property
-	                // and is waiting for it to be called with the channelProxy.
-	                w[id](channelProxy);
-	                delete w[id];
-	            }
-	            return channelProxy.sendMessage;
-	        });
-	        const channelProxyEval = ChannelProxy.#createChannelProxyEvalStr();
-	        return `(${delegate})('${this.#id}',${channelProxyEval})`;
-	    }
-	}
-	ChannelProxy$1.ChannelProxy = ChannelProxy;
-
-	Object.defineProperty(Realm$1, "__esModule", { value: true });
-	Realm$1.Realm = void 0;
-	const protocol_js_1$j = protocol;
-	const log_js_1$b = log$1;
-	const uuid_js_1$2 = uuid;
-	const ChannelProxy_js_1$1 = ChannelProxy$1;
-	class Realm {
-	    #cdpClient;
-	    #eventManager;
-	    #executionContextId;
-	    #logger;
-	    #origin;
-	    #realmId;
-	    #realmStorage;
-	    constructor(cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage) {
-	        this.#cdpClient = cdpClient;
-	        this.#eventManager = eventManager;
-	        this.#executionContextId = executionContextId;
-	        this.#logger = logger;
-	        this.#origin = origin;
-	        this.#realmId = realmId;
-	        this.#realmStorage = realmStorage;
-	        this.#realmStorage.addRealm(this);
-	    }
-	    cdpToBidiValue(cdpValue, resultOwnership) {
-	        const bidiValue = this.serializeForBiDi(cdpValue.result.deepSerializedValue, new Map());
-	        if (cdpValue.result.objectId) {
-	            const objectId = cdpValue.result.objectId;
-	            if (resultOwnership === "root" /* Script.ResultOwnership.Root */) {
-	                // Extend BiDi value with `handle` based on required `resultOwnership`
-	                // and  CDP response but not on the actual BiDi type.
-	                bidiValue.handle = objectId;
-	                // Remember all the handles sent to client.
-	                this.#realmStorage.knownHandlesToRealmMap.set(objectId, this.realmId);
-	            }
-	            else {
-	                // No need to await for the object to be released.
-	                void this.#releaseObject(objectId).catch((error) => this.#logger?.(log_js_1$b.LogType.debugError, error));
-	            }
-	        }
-	        if (cdpValue.result.type === 'object') {
-	            switch (cdpValue.result.subtype) {
-	                case 'generator':
-	                case 'iterator':
-	                    bidiValue.type = cdpValue.result.subtype;
-	                    delete bidiValue['value'];
-	                    break;
-	                // Intentionally left blank.
-	            }
-	        }
-	        return bidiValue;
-	    }
-	    /**
-	     * Relies on the CDP to implement proper BiDi serialization, except:
-	     * * CDP integer property `backendNodeId` is replaced with `sharedId` of
-	     * `{documentId}_element_{backendNodeId}`;
-	     * * CDP integer property `weakLocalObjectReference` is replaced with UUID `internalId`
-	     * using unique-per serialization `internalIdMap`.
-	     * * CDP type `platformobject` is replaced with `object`.
-	     * @param deepSerializedValue - CDP value to be converted to BiDi.
-	     * @param internalIdMap - Map from CDP integer `weakLocalObjectReference` to BiDi UUID
-	     * `internalId`.
-	     */
-	    serializeForBiDi(deepSerializedValue, internalIdMap) {
-	        if (Object.hasOwn(deepSerializedValue, 'weakLocalObjectReference')) {
-	            const weakLocalObjectReference = deepSerializedValue.weakLocalObjectReference;
-	            if (!internalIdMap.has(weakLocalObjectReference)) {
-	                internalIdMap.set(weakLocalObjectReference, (0, uuid_js_1$2.uuidv4)());
-	            }
-	            deepSerializedValue.internalId = internalIdMap.get(weakLocalObjectReference);
-	            delete deepSerializedValue['weakLocalObjectReference'];
-	        }
-	        // Platform object is a special case. It should have only `{type: object}`
-	        // without `value` field.
-	        if (deepSerializedValue.type === 'platformobject') {
-	            return { type: 'object' };
-	        }
-	        const bidiValue = deepSerializedValue.value;
-	        if (bidiValue === undefined) {
-	            return deepSerializedValue;
-	        }
-	        // Recursively update the nested values.
-	        if (['array', 'set', 'htmlcollection', 'nodelist'].includes(deepSerializedValue.type)) {
-	            for (const i in bidiValue) {
-	                bidiValue[i] = this.serializeForBiDi(bidiValue[i], internalIdMap);
-	            }
-	        }
-	        if (['object', 'map'].includes(deepSerializedValue.type)) {
-	            for (const i in bidiValue) {
-	                bidiValue[i] = [
-	                    this.serializeForBiDi(bidiValue[i][0], internalIdMap),
-	                    this.serializeForBiDi(bidiValue[i][1], internalIdMap),
-	                ];
-	            }
-	        }
-	        return deepSerializedValue;
-	    }
-	    get realmId() {
-	        return this.#realmId;
-	    }
-	    get executionContextId() {
-	        return this.#executionContextId;
-	    }
-	    get origin() {
-	        return this.#origin;
-	    }
-	    get source() {
-	        return {
-	            realm: this.realmId,
-	        };
-	    }
-	    get cdpClient() {
-	        return this.#cdpClient;
-	    }
-	    get baseInfo() {
-	        return {
-	            realm: this.realmId,
-	            origin: this.origin,
-	        };
-	    }
-	    async evaluate(expression, awaitPromise, resultOwnership, serializationOptions, userActivation = false) {
-	        const cdpEvaluateResult = await this.cdpClient.sendCommand('Runtime.evaluate', {
-	            contextId: this.executionContextId,
-	            expression,
-	            awaitPromise,
-	            serializationOptions: Realm.#getSerializationOptions("deep" /* Protocol.Runtime.SerializationOptionsSerialization.Deep */, serializationOptions),
-	            userGesture: userActivation,
-	        });
-	        if (cdpEvaluateResult.exceptionDetails) {
-	            return await this.#getExceptionResult(cdpEvaluateResult.exceptionDetails, 0, resultOwnership);
-	        }
-	        return {
-	            realm: this.realmId,
-	            result: this.cdpToBidiValue(cdpEvaluateResult, resultOwnership),
-	            type: 'success',
-	        };
-	    }
-	    #registerEvent(event) {
-	        if (this.associatedBrowsingContexts.length === 0) {
-	            this.#eventManager.registerEvent(event, null);
-	        }
-	        else {
-	            for (const browsingContext of this.associatedBrowsingContexts) {
-	                this.#eventManager.registerEvent(event, browsingContext.id);
-	            }
-	        }
-	    }
-	    initialize() {
-	        this.#registerEvent({
-	            type: 'event',
-	            method: protocol_js_1$j.ChromiumBidi.Script.EventNames.RealmCreated,
-	            params: this.realmInfo,
-	        });
-	    }
-	    /**
-	     * Serializes a given CDP object into BiDi, keeping references in the
-	     * target's `globalThis`.
-	     */
-	    async serializeCdpObject(cdpRemoteObject, resultOwnership) {
-	        const argument = Realm.#cdpRemoteObjectToCallArgument(cdpRemoteObject);
-	        const cdpValue = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	            functionDeclaration: String((remoteObject) => remoteObject),
-	            awaitPromise: false,
-	            arguments: [argument],
-	            serializationOptions: {
-	                serialization: "deep" /* Protocol.Runtime.SerializationOptionsSerialization.Deep */,
-	            },
-	            executionContextId: this.executionContextId,
-	        });
-	        return this.cdpToBidiValue(cdpValue, resultOwnership);
-	    }
-	    static #cdpRemoteObjectToCallArgument(cdpRemoteObject) {
-	        if (cdpRemoteObject.objectId !== undefined) {
-	            return { objectId: cdpRemoteObject.objectId };
-	        }
-	        if (cdpRemoteObject.unserializableValue !== undefined) {
-	            return { unserializableValue: cdpRemoteObject.unserializableValue };
-	        }
-	        return { value: cdpRemoteObject.value };
-	    }
-	    /**
-	     * Gets the string representation of an object. This is equivalent to
-	     * calling `toString()` on the object value.
-	     */
-	    async stringifyObject(cdpRemoteObject) {
-	        const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	            functionDeclaration: String((remoteObject) => String(remoteObject)),
-	            awaitPromise: false,
-	            arguments: [cdpRemoteObject],
-	            returnByValue: true,
-	            executionContextId: this.executionContextId,
-	        });
-	        return result.value;
-	    }
-	    async #flattenKeyValuePairs(mappingLocalValue) {
-	        const keyValueArray = [];
-	        for (const [key, value] of mappingLocalValue) {
-	            let keyArg;
-	            if (typeof key === 'string') {
-	                // Key is a string.
-	                keyArg = { value: key };
-	            }
-	            else {
-	                // Key is a serialized value.
-	                keyArg = await this.deserializeForCdp(key);
-	            }
-	            const valueArg = await this.deserializeForCdp(value);
-	            keyValueArray.push(keyArg);
-	            keyValueArray.push(valueArg);
-	        }
-	        return keyValueArray;
-	    }
-	    async #flattenValueList(listLocalValue) {
-	        return await Promise.all(listLocalValue.map((localValue) => this.deserializeForCdp(localValue)));
-	    }
-	    async #serializeCdpExceptionDetails(cdpExceptionDetails, lineOffset, resultOwnership) {
-	        const callFrames = cdpExceptionDetails.stackTrace?.callFrames.map((frame) => ({
-	            url: frame.url,
-	            functionName: frame.functionName,
-	            lineNumber: frame.lineNumber - lineOffset,
-	            columnNumber: frame.columnNumber,
-	        })) ?? [];
-	        // Exception should always be there.
-	        const exception = cdpExceptionDetails.exception;
-	        return {
-	            exception: await this.serializeCdpObject(exception, resultOwnership),
-	            columnNumber: cdpExceptionDetails.columnNumber,
-	            lineNumber: cdpExceptionDetails.lineNumber - lineOffset,
-	            stackTrace: {
-	                callFrames,
-	            },
-	            text: (await this.stringifyObject(exception)) || cdpExceptionDetails.text,
-	        };
-	    }
-	    async callFunction(functionDeclaration, thisLocalValue, argumentsLocalValues, awaitPromise, resultOwnership, serializationOptions, userActivation = false) {
-	        const callFunctionAndSerializeScript = `(...args) => {
-      function callFunction(f, args) {
-        const deserializedThis = args.shift();
-        const deserializedArgs = args;
-        return f.apply(deserializedThis, deserializedArgs);
-      }
-      return callFunction((
-        ${functionDeclaration}
-      ), args);
-    }`;
-	        const thisAndArgumentsList = [
-	            await this.deserializeForCdp(thisLocalValue),
-	            ...(await Promise.all(argumentsLocalValues.map(async (argumentLocalValue) => await this.deserializeForCdp(argumentLocalValue)))),
-	        ];
-	        let cdpCallFunctionResult;
-	        try {
-	            cdpCallFunctionResult = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	                functionDeclaration: callFunctionAndSerializeScript,
-	                awaitPromise,
-	                arguments: thisAndArgumentsList,
-	                serializationOptions: Realm.#getSerializationOptions("deep" /* Protocol.Runtime.SerializationOptionsSerialization.Deep */, serializationOptions),
-	                executionContextId: this.executionContextId,
-	                userGesture: userActivation,
-	            });
-	        }
-	        catch (error) {
-	            // Heuristic to determine if the problem is in the argument.
-	            // The check can be done on the `deserialization` step, but this approach
-	            // helps to save round-trips.
-	            if (error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
-	                [
-	                    'Could not find object with given id',
-	                    'Argument should belong to the same JavaScript world as target object',
-	                    'Invalid remote object id',
-	                ].includes(error.message)) {
-	                throw new protocol_js_1$j.NoSuchHandleException('Handle was not found.');
-	            }
-	            throw error;
-	        }
-	        if (cdpCallFunctionResult.exceptionDetails) {
-	            return await this.#getExceptionResult(cdpCallFunctionResult.exceptionDetails, 1, resultOwnership);
-	        }
-	        return {
-	            type: 'success',
-	            result: this.cdpToBidiValue(cdpCallFunctionResult, resultOwnership),
-	            realm: this.realmId,
-	        };
-	    }
-	    async deserializeForCdp(localValue) {
-	        if ('handle' in localValue && localValue.handle) {
-	            return { objectId: localValue.handle };
-	            // We tried to find a handle value but failed
-	            // This allows us to have exhaustive switch on `localValue.type`
-	        }
-	        else if ('handle' in localValue || 'sharedId' in localValue) {
-	            throw new protocol_js_1$j.NoSuchHandleException('Handle was not found.');
-	        }
-	        switch (localValue.type) {
-	            case 'undefined':
-	                return { unserializableValue: 'undefined' };
-	            case 'null':
-	                return { unserializableValue: 'null' };
-	            case 'string':
-	                return { value: localValue.value };
-	            case 'number':
-	                if (localValue.value === 'NaN') {
-	                    return { unserializableValue: 'NaN' };
-	                }
-	                else if (localValue.value === '-0') {
-	                    return { unserializableValue: '-0' };
-	                }
-	                else if (localValue.value === 'Infinity') {
-	                    return { unserializableValue: 'Infinity' };
-	                }
-	                else if (localValue.value === '-Infinity') {
-	                    return { unserializableValue: '-Infinity' };
-	                }
-	                return {
-	                    value: localValue.value,
-	                };
-	            case 'boolean':
-	                return { value: Boolean(localValue.value) };
-	            case 'bigint':
-	                return {
-	                    unserializableValue: `BigInt(${JSON.stringify(localValue.value)})`,
-	                };
-	            case 'date':
-	                return {
-	                    unserializableValue: `new Date(Date.parse(${JSON.stringify(localValue.value)}))`,
-	                };
-	            case 'regexp':
-	                return {
-	                    unserializableValue: `new RegExp(${JSON.stringify(localValue.value.pattern)}, ${JSON.stringify(localValue.value.flags)})`,
-	                };
-	            case 'map': {
-	                // TODO: If none of the nested keys and values has a remote
-	                // reference, serialize to `unserializableValue` without CDP roundtrip.
-	                const keyValueArray = await this.#flattenKeyValuePairs(localValue.value);
-	                const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	                    functionDeclaration: String((...args) => {
-	                        const result = new Map();
-	                        for (let i = 0; i < args.length; i += 2) {
-	                            result.set(args[i], args[i + 1]);
-	                        }
-	                        return result;
-	                    }),
-	                    awaitPromise: false,
-	                    arguments: keyValueArray,
-	                    returnByValue: false,
-	                    executionContextId: this.executionContextId,
-	                });
-	                // TODO(#375): Release `result.objectId` after using.
-	                return { objectId: result.objectId };
-	            }
-	            case 'object': {
-	                // TODO: If none of the nested keys and values has a remote
-	                // reference, serialize to `unserializableValue` without CDP roundtrip.
-	                const keyValueArray = await this.#flattenKeyValuePairs(localValue.value);
-	                const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	                    functionDeclaration: String((...args) => {
-	                        const result = {};
-	                        for (let i = 0; i < args.length; i += 2) {
-	                            // Key should be either `string`, `number`, or `symbol`.
-	                            const key = args[i];
-	                            result[key] = args[i + 1];
-	                        }
-	                        return result;
-	                    }),
-	                    awaitPromise: false,
-	                    arguments: keyValueArray,
-	                    returnByValue: false,
-	                    executionContextId: this.executionContextId,
-	                });
-	                // TODO(#375): Release `result.objectId` after using.
-	                return { objectId: result.objectId };
-	            }
-	            case 'array': {
-	                // TODO: If none of the nested items has a remote reference,
-	                // serialize to `unserializableValue` without CDP roundtrip.
-	                const args = await this.#flattenValueList(localValue.value);
-	                const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	                    functionDeclaration: String((...args) => args),
-	                    awaitPromise: false,
-	                    arguments: args,
-	                    returnByValue: false,
-	                    executionContextId: this.executionContextId,
-	                });
-	                // TODO(#375): Release `result.objectId` after using.
-	                return { objectId: result.objectId };
-	            }
-	            case 'set': {
-	                // TODO: if none of the nested items has a remote reference,
-	                // serialize to `unserializableValue` without CDP roundtrip.
-	                const args = await this.#flattenValueList(localValue.value);
-	                const { result } = await this.cdpClient.sendCommand('Runtime.callFunctionOn', {
-	                    functionDeclaration: String((...args) => new Set(args)),
-	                    awaitPromise: false,
-	                    arguments: args,
-	                    returnByValue: false,
-	                    executionContextId: this.executionContextId,
-	                });
-	                // TODO(#375): Release `result.objectId` after using.
-	                return { objectId: result.objectId };
-	            }
-	            case 'channel': {
-	                const channelProxy = new ChannelProxy_js_1$1.ChannelProxy(localValue.value, this.#logger);
-	                const channelProxySendMessageHandle = await channelProxy.init(this, this.#eventManager);
-	                return { objectId: channelProxySendMessageHandle };
-	            }
-	            // TODO(#375): Dispose of nested objects.
-	        }
-	        // Intentionally outside to handle unknown types
-	        throw new Error(`Value ${JSON.stringify(localValue)} is not deserializable.`);
-	    }
-	    async #getExceptionResult(exceptionDetails, lineOffset, resultOwnership) {
-	        return {
-	            exceptionDetails: await this.#serializeCdpExceptionDetails(exceptionDetails, lineOffset, resultOwnership),
-	            realm: this.realmId,
-	            type: 'exception',
-	        };
-	    }
-	    static #getSerializationOptions(serialization, serializationOptions) {
-	        return {
-	            serialization,
-	            additionalParameters: Realm.#getAdditionalSerializationParameters(serializationOptions),
-	            ...Realm.#getMaxObjectDepth(serializationOptions),
-	        };
-	    }
-	    static #getAdditionalSerializationParameters(serializationOptions) {
-	        const additionalParameters = {};
-	        if (serializationOptions.maxDomDepth !== undefined) {
-	            additionalParameters['maxNodeDepth'] =
-	                serializationOptions.maxDomDepth === null
-	                    ? 1000
-	                    : serializationOptions.maxDomDepth;
-	        }
-	        if (serializationOptions.includeShadowTree !== undefined) {
-	            additionalParameters['includeShadowTree'] =
-	                serializationOptions.includeShadowTree;
-	        }
-	        return additionalParameters;
-	    }
-	    static #getMaxObjectDepth(serializationOptions) {
-	        return serializationOptions.maxObjectDepth === undefined ||
-	            serializationOptions.maxObjectDepth === null
-	            ? {}
-	            : { maxDepth: serializationOptions.maxObjectDepth };
-	    }
-	    async #releaseObject(handle) {
-	        try {
-	            await this.cdpClient.sendCommand('Runtime.releaseObject', {
-	                objectId: handle,
-	            });
-	        }
-	        catch (error) {
-	            // Heuristic to determine if the problem is in the unknown handler.
-	            // Ignore the error if so.
-	            if (!(error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
-	                error.message === 'Invalid remote object id')) {
-	                throw error;
-	            }
-	        }
-	    }
-	    async disown(handle) {
-	        // Disowning an object from different realm does nothing.
-	        if (this.#realmStorage.knownHandlesToRealmMap.get(handle) !== this.realmId) {
-	            return;
-	        }
-	        await this.#releaseObject(handle);
-	        this.#realmStorage.knownHandlesToRealmMap.delete(handle);
-	    }
-	    dispose() {
-	        this.#registerEvent({
-	            type: 'event',
-	            method: protocol_js_1$j.ChromiumBidi.Script.EventNames.RealmDestroyed,
-	            params: {
-	                realm: this.realmId,
-	            },
-	        });
-	    }
-	}
-	Realm$1.Realm = Realm;
-
-	/**
-	 * Copyright 2024 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(WorkerRealm$1, "__esModule", { value: true });
-	WorkerRealm$1.WorkerRealm = void 0;
-	const Realm_js_1$1 = Realm$1;
-	class WorkerRealm extends Realm_js_1$1.Realm {
-	    #realmType;
-	    #ownerRealms;
-	    constructor(cdpClient, eventManager, executionContextId, logger, origin, ownerRealms, realmId, realmStorage, realmType) {
-	        super(cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage);
-	        this.#ownerRealms = ownerRealms;
-	        this.#realmType = realmType;
-	        this.initialize();
-	    }
-	    get associatedBrowsingContexts() {
-	        return this.#ownerRealms.flatMap((realm) => realm.associatedBrowsingContexts);
-	    }
-	    get realmType() {
-	        return this.#realmType;
-	    }
-	    get source() {
-	        return {
-	            realm: this.realmId,
-	            // This is a hack to make Puppeteer able to track workers.
-	            // TODO: remove after Puppeteer tracks workers by owners and use the base version.
-	            context: this.associatedBrowsingContexts[0]?.id,
-	        };
-	    }
-	    get realmInfo() {
-	        const owners = this.#ownerRealms.map((realm) => realm.realmId);
-	        const { realmType } = this;
-	        switch (realmType) {
-	            case 'dedicated-worker': {
-	                const owner = owners[0];
-	                if (owner === undefined || owners.length !== 1) {
-	                    throw new Error('Dedicated worker must have exactly one owner');
-	                }
-	                return {
-	                    ...this.baseInfo,
-	                    type: realmType,
-	                    owners: [owner],
-	                };
-	            }
-	            case 'service-worker':
-	            case 'shared-worker': {
-	                return {
-	                    ...this.baseInfo,
-	                    type: realmType,
-	                };
-	            }
-	        }
-	    }
-	}
-	WorkerRealm$1.WorkerRealm = WorkerRealm;
-
-	var BrowsingContextImpl$1 = {};
-
-	var assert$1 = {};
-
-	Object.defineProperty(assert$1, "__esModule", { value: true });
-	assert$1.assert = void 0;
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	function assert(predicate, message) {
-	    if (!predicate) {
-	        throw new Error(message ?? 'Internal assertion failed.');
-	    }
-	}
-	assert$1.assert = assert;
-
-	var Deferred$1 = {};
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(Deferred$1, "__esModule", { value: true });
-	Deferred$1.Deferred = void 0;
-	class Deferred {
-	    #isFinished = false;
-	    #promise;
-	    #resolve;
-	    #reject;
-	    get isFinished() {
-	        return this.#isFinished;
-	    }
-	    constructor() {
-	        this.#promise = new Promise((resolve, reject) => {
-	            this.#resolve = resolve;
-	            this.#reject = reject;
-	        });
-	        // Needed to avoid `Uncaught (in promise)`. The promises returned by `then`
-	        // and `catch` will be rejected anyway.
-	        this.#promise.catch((_error) => {
-	            // Intentionally empty.
-	        });
-	    }
-	    then(onFulfilled, onRejected) {
-	        return this.#promise.then(onFulfilled, onRejected);
-	    }
-	    catch(onRejected) {
-	        return this.#promise.catch(onRejected);
-	    }
-	    resolve(value) {
-	        if (!this.#isFinished) {
-	            this.#isFinished = true;
-	            this.#resolve(value);
-	        }
-	    }
-	    reject(reason) {
-	        if (!this.#isFinished) {
-	            this.#isFinished = true;
-	            this.#reject(reason);
-	        }
-	    }
-	    finally(onFinally) {
-	        return this.#promise.finally(onFinally);
-	    }
-	    [Symbol.toStringTag] = 'Promise';
-	}
-	Deferred$1.Deferred = Deferred;
-
-	var unitConversions = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(unitConversions, "__esModule", { value: true });
-	unitConversions.inchesFromCm = void 0;
-	/** @return Given an input in cm, convert it to inches. */
-	function inchesFromCm(cm) {
-	    return cm / 2.54;
-	}
-	unitConversions.inchesFromCm = inchesFromCm;
-
-	var WindowRealm$1 = {};
-
-	var SharedId = {};
-
-	/*
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(SharedId, "__esModule", { value: true });
-	SharedId.parseSharedId = SharedId.getSharedId = void 0;
-	const SHARED_ID_DIVIDER = '_element_';
-	function getSharedId(frameId, documentId, backendNodeId, sharedIdWithFrame) {
-	    if (sharedIdWithFrame) {
-	        return `f.${frameId}.d.${documentId}.e.${backendNodeId}`;
-	    }
-	    // TODO: remove once ChromeDriver accepts sharedId in the new format:
-	    //  http://go/chromedriver:weak-map
-	    return `${documentId}${SHARED_ID_DIVIDER}${backendNodeId}`;
-	}
-	SharedId.getSharedId = getSharedId;
-	function parseLegacySharedId(sharedId) {
-	    const match = sharedId.match(new RegExp(`(.*)${SHARED_ID_DIVIDER}(.*)`));
-	    if (!match) {
-	        // SharedId is incorrectly formatted.
-	        return null;
-	    }
-	    const documentId = match[1];
-	    const elementId = match[2];
-	    if (documentId === undefined || elementId === undefined) {
-	        return null;
-	    }
-	    const backendNodeId = parseInt(elementId ?? '');
-	    if (isNaN(backendNodeId)) {
-	        return null;
-	    }
-	    return {
-	        documentId,
-	        backendNodeId,
-	    };
-	}
-	function parseSharedId(sharedId) {
-	    // TODO: remove legacy check once ChromeDriver provides sharedId in the new format.
-	    const legacyFormattedSharedId = parseLegacySharedId(sharedId);
-	    if (legacyFormattedSharedId !== null) {
-	        return { ...legacyFormattedSharedId, frameId: undefined };
-	    }
-	    const match = sharedId.match(/f\.(.*)\.d\.(.*)\.e\.([0-9]*)/);
-	    if (!match) {
-	        // SharedId is incorrectly formatted.
-	        return null;
-	    }
-	    const frameId = match[1];
-	    const documentId = match[2];
-	    const elementId = match[3];
-	    if (frameId === undefined ||
-	        documentId === undefined ||
-	        elementId === undefined) {
-	        return null;
-	    }
-	    const backendNodeId = parseInt(elementId ?? '');
-	    if (isNaN(backendNodeId)) {
-	        return null;
-	    }
-	    return {
-	        frameId,
-	        documentId,
-	        backendNodeId,
-	    };
-	}
-	SharedId.parseSharedId = parseSharedId;
-
-	/**
-	 * Copyright 2024 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(WindowRealm$1, "__esModule", { value: true });
-	WindowRealm$1.WindowRealm = void 0;
-	const protocol_js_1$i = protocol;
-	const Realm_js_1 = Realm$1;
-	const SharedId_js_1 = SharedId;
-	class WindowRealm extends Realm_js_1.Realm {
-	    #browsingContextId;
-	    #browsingContextStorage;
-	    #sharedIdWithFrame;
-	    sandbox;
-	    constructor(browsingContextId, browsingContextStorage, cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage, sandbox, sharedIdWithFrame) {
-	        super(cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage);
-	        this.#browsingContextId = browsingContextId;
-	        this.#browsingContextStorage = browsingContextStorage;
-	        this.#sharedIdWithFrame = sharedIdWithFrame;
-	        this.sandbox = sandbox;
-	        this.initialize();
-	    }
-	    #getBrowsingContextId(navigableId) {
-	        const maybeBrowsingContext = this.#browsingContextStorage
-	            .getAllContexts()
-	            .find((context) => context.navigableId === navigableId);
-	        return maybeBrowsingContext?.id ?? 'UNKNOWN';
-	    }
-	    get browsingContext() {
-	        return this.#browsingContextStorage.getContext(this.#browsingContextId);
-	    }
-	    get associatedBrowsingContexts() {
-	        return [this.browsingContext];
-	    }
-	    get realmType() {
-	        return 'window';
-	    }
-	    get realmInfo() {
-	        return {
-	            ...this.baseInfo,
-	            type: this.realmType,
-	            context: this.#browsingContextId,
-	            sandbox: this.sandbox,
-	        };
-	    }
-	    get source() {
-	        return {
-	            realm: this.realmId,
-	            context: this.browsingContext.id,
-	        };
-	    }
-	    serializeForBiDi(deepSerializedValue, internalIdMap) {
-	        const bidiValue = deepSerializedValue.value;
-	        if (deepSerializedValue.type === 'node') {
-	            if (Object.hasOwn(bidiValue, 'backendNodeId')) {
-	                let navigableId = this.browsingContext.navigableId ?? 'UNKNOWN';
-	                if (Object.hasOwn(bidiValue, 'loaderId')) {
-	                    // `loaderId` should be always there after ~2024-03-05, when
-	                    // https://crrev.com/c/5116240 reaches stable.
-	                    // TODO: remove the check after the date.
-	                    navigableId = bidiValue.loaderId;
-	                    delete bidiValue['loaderId'];
-	                }
-	                deepSerializedValue.sharedId =
-	                    (0, SharedId_js_1.getSharedId)(this.#getBrowsingContextId(navigableId), navigableId, bidiValue.backendNodeId, this.#sharedIdWithFrame);
-	                delete bidiValue['backendNodeId'];
-	            }
-	            if (Object.hasOwn(bidiValue, 'children')) {
-	                for (const i in bidiValue.children) {
-	                    bidiValue.children[i] = this.serializeForBiDi(bidiValue.children[i], internalIdMap);
-	                }
-	            }
-	            if (Object.hasOwn(bidiValue, 'shadowRoot') &&
-	                bidiValue.shadowRoot !== null) {
-	                bidiValue.shadowRoot = this.serializeForBiDi(bidiValue.shadowRoot, internalIdMap);
-	            }
-	            // `namespaceURI` can be is either `null` or non-empty string.
-	            if (bidiValue.namespaceURI === '') {
-	                bidiValue.namespaceURI = null;
-	            }
-	        }
-	        return super.serializeForBiDi(deepSerializedValue, internalIdMap);
-	    }
-	    async deserializeForCdp(localValue) {
-	        if ('sharedId' in localValue && localValue.sharedId) {
-	            const parsedSharedId = (0, SharedId_js_1.parseSharedId)(localValue.sharedId);
-	            if (parsedSharedId === null) {
-	                throw new protocol_js_1$i.NoSuchNodeException(`SharedId "${localValue.sharedId}" was not found.`);
-	            }
-	            const { documentId, backendNodeId } = parsedSharedId;
-	            // TODO: add proper validation if the element is accessible from the current realm.
-	            if (this.browsingContext.navigableId !== documentId) {
-	                throw new protocol_js_1$i.NoSuchNodeException(`SharedId "${localValue.sharedId}" belongs to different document. Current document is ${this.browsingContext.navigableId}.`);
-	            }
-	            try {
-	                const { object } = await this.cdpClient.sendCommand('DOM.resolveNode', {
-	                    backendNodeId,
-	                    executionContextId: this.executionContextId,
-	                });
-	                // TODO(#375): Release `obj.object.objectId` after using.
-	                return { objectId: object.objectId };
-	            }
-	            catch (error) {
-	                // Heuristic to detect "no such node" exception. Based on the  specific
-	                // CDP implementation.
-	                if (error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
-	                    error.message === 'No node with given id found') {
-	                    throw new protocol_js_1$i.NoSuchNodeException(`SharedId "${localValue.sharedId}" was not found.`);
-	                }
-	                throw new protocol_js_1$i.UnknownErrorException(error.message, error.stack);
-	            }
-	        }
-	        return await super.deserializeForCdp(localValue);
-	    }
-	    async evaluate(expression, awaitPromise, resultOwnership, serializationOptions, userActivation) {
-	        await this.#browsingContextStorage
-	            .getContext(this.#browsingContextId)
-	            .targetUnblockedOrThrow();
-	        return await super.evaluate(expression, awaitPromise, resultOwnership, serializationOptions, userActivation);
-	    }
-	    async callFunction(functionDeclaration, thisLocalValue, argumentsLocalValues, awaitPromise, resultOwnership, serializationOptions, userActivation) {
-	        await this.#browsingContextStorage
-	            .getContext(this.#browsingContextId)
-	            .targetUnblockedOrThrow();
-	        return await super.callFunction(functionDeclaration, thisLocalValue, argumentsLocalValues, awaitPromise, resultOwnership, serializationOptions, userActivation);
-	    }
-	}
-	WindowRealm$1.WindowRealm = WindowRealm;
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(BrowsingContextImpl$1, "__esModule", { value: true });
-	BrowsingContextImpl$1.serializeOrigin = BrowsingContextImpl$1.BrowsingContextImpl = void 0;
-	const chromium_bidi_js_1$1 = chromiumBidi;
-	const protocol_js_1$h = protocol;
-	const assert_js_1$7 = assert$1;
-	const Deferred_js_1$2 = Deferred$1;
-	const log_js_1$a = log$1;
-	const unitConversions_js_1 = unitConversions;
-	const WindowRealm_js_1$1 = WindowRealm$1;
-	class BrowsingContextImpl {
-	    static LOGGER_PREFIX = `${log_js_1$a.LogType.debug}:browsingContext`;
-	    /** The ID of this browsing context. */
-	    #id;
-	    userContext;
-	    /**
-	     * The ID of the parent browsing context.
-	     * If null, this is a top-level context.
-	     */
-	    #parentId;
-	    /** Direct children browsing contexts. */
-	    #children = new Set();
-	    #browsingContextStorage;
-	    #lifecycle = {
-	        DOMContentLoaded: new Deferred_js_1$2.Deferred(),
-	        load: new Deferred_js_1$2.Deferred(),
-	    };
-	    #navigation = {
-	        withinDocument: new Deferred_js_1$2.Deferred(),
-	    };
-	    #url = 'about:blank';
-	    #eventManager;
-	    #realmStorage;
-	    #loaderId;
-	    #cdpTarget;
-	    #maybeDefaultRealm;
-	    #sharedIdWithFrame;
-	    #logger;
-	    constructor(cdpTarget, realmStorage, id, parentId, userContext, eventManager, browsingContextStorage, sharedIdWithFrame, logger) {
-	        this.#cdpTarget = cdpTarget;
-	        this.#realmStorage = realmStorage;
-	        this.#id = id;
-	        this.#parentId = parentId;
-	        this.userContext = userContext;
-	        this.#eventManager = eventManager;
-	        this.#browsingContextStorage = browsingContextStorage;
-	        this.#sharedIdWithFrame = sharedIdWithFrame;
-	        this.#logger = logger;
-	    }
-	    static create(cdpTarget, realmStorage, id, parentId, userContext, eventManager, browsingContextStorage, sharedIdWithFrame, logger) {
-	        const context = new BrowsingContextImpl(cdpTarget, realmStorage, id, parentId, userContext, eventManager, browsingContextStorage, sharedIdWithFrame, logger);
-	        context.#initListeners();
-	        browsingContextStorage.addContext(context);
-	        if (!context.isTopLevelContext()) {
-	            context.parent.addChild(context.id);
-	        }
-	        eventManager.registerEvent({
-	            type: 'event',
-	            method: protocol_js_1$h.ChromiumBidi.BrowsingContext.EventNames.ContextCreated,
-	            params: context.serializeToBidiValue(),
-	        }, context.id);
-	        return context;
-	    }
-	    static getTimestamp() {
-	        // `timestamp` from the event is MonotonicTime, not real time, so
-	        // the best Mapper can do is to set the timestamp to the epoch time
-	        // of the event arrived.
-	        // https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-MonotonicTime
-	        return new Date().getTime();
-	    }
-	    /**
-	     * @see https://html.spec.whatwg.org/multipage/document-sequences.html#navigable
-	     */
-	    get navigableId() {
-	        return this.#loaderId;
-	    }
-	    dispose() {
-	        this.#deleteAllChildren();
-	        this.#realmStorage.deleteRealms({
-	            browsingContextId: this.id,
-	        });
-	        // Remove context from the parent.
-	        if (!this.isTopLevelContext()) {
-	            this.parent.#children.delete(this.id);
-	        }
-	        // Fail all ongoing navigations.
-	        this.#failLifecycleIfNotFinished();
-	        this.#eventManager.registerEvent({
-	            type: 'event',
-	            method: protocol_js_1$h.ChromiumBidi.BrowsingContext.EventNames.ContextDestroyed,
-	            params: this.serializeToBidiValue(),
-	        }, this.id);
-	        this.#browsingContextStorage.deleteContextById(this.id);
-	    }
-	    /** Returns the ID of this context. */
-	    get id() {
-	        return this.#id;
-	    }
-	    /** Returns the parent context ID. */
-	    get parentId() {
-	        return this.#parentId;
-	    }
-	    /** Returns the parent context. */
-	    get parent() {
-	        if (this.parentId === null) {
-	            return null;
-	        }
-	        return this.#browsingContextStorage.getContext(this.parentId);
-	    }
-	    /** Returns all direct children contexts. */
-	    get directChildren() {
-	        return [...this.#children].map((id) => this.#browsingContextStorage.getContext(id));
-	    }
-	    /** Returns all children contexts, flattened. */
-	    get allChildren() {
-	        const children = this.directChildren;
-	        return children.concat(...children.map((child) => child.allChildren));
-	    }
-	    /**
-	     * Returns true if this is a top-level context.
-	     * This is the case whenever the parent context ID is null.
-	     */
-	    isTopLevelContext() {
-	        return this.#parentId === null;
-	    }
-	    get top() {
-	        // eslint-disable-next-line @typescript-eslint/no-this-alias
-	        let topContext = this;
-	        let parent = topContext.parent;
-	        while (parent) {
-	            topContext = parent;
-	            parent = topContext.parent;
-	        }
-	        return topContext;
-	    }
-	    addChild(childId) {
-	        this.#children.add(childId);
-	    }
-	    #deleteAllChildren() {
-	        this.directChildren.map((child) => child.dispose());
-	    }
-	    get #defaultRealm() {
-	        (0, assert_js_1$7.assert)(this.#maybeDefaultRealm, `No default realm for browsing context ${this.#id}`);
-	        return this.#maybeDefaultRealm;
-	    }
-	    get cdpTarget() {
-	        return this.#cdpTarget;
-	    }
-	    updateCdpTarget(cdpTarget) {
-	        this.#cdpTarget = cdpTarget;
-	        this.#initListeners();
-	    }
-	    get url() {
-	        return this.#url;
-	    }
-	    async lifecycleLoaded() {
-	        await this.#lifecycle.load;
-	    }
-	    async targetUnblockedOrThrow() {
-	        const result = await this.#cdpTarget.unblocked;
-	        if (result.kind === 'error') {
-	            throw result.error;
-	        }
-	    }
-	    async getOrCreateSandbox(sandbox) {
-	        if (sandbox === undefined || sandbox === '') {
-	            return this.#defaultRealm;
-	        }
-	        let maybeSandboxes = this.#realmStorage.findRealms({
-	            browsingContextId: this.id,
-	            sandbox,
-	        });
-	        if (maybeSandboxes.length === 0) {
-	            await this.#cdpTarget.cdpClient.sendCommand('Page.createIsolatedWorld', {
-	                frameId: this.id,
-	                worldName: sandbox,
-	            });
-	            // `Runtime.executionContextCreated` should be emitted by the time the
-	            // previous command is done.
-	            maybeSandboxes = this.#realmStorage.findRealms({
-	                browsingContextId: this.id,
-	                sandbox,
-	            });
-	            (0, assert_js_1$7.assert)(maybeSandboxes.length !== 0);
-	        }
-	        // It's possible for more than one sandbox to be created due to provisional
-	        // frames. In this case, it's always the first one (i.e. the oldest one)
-	        // that is more relevant since the user may have set that one up already
-	        // through evaluation.
-	        return maybeSandboxes[0];
-	    }
-	    serializeToBidiValue(maxDepth = 0, addParentField = true) {
-	        return {
-	            context: this.#id,
-	            url: this.url,
-	            userContext: this.userContext,
-	            children: maxDepth > 0
-	                ? this.directChildren.map((c) => c.serializeToBidiValue(maxDepth - 1, false))
-	                : null,
-	            ...(addParentField ? { parent: this.#parentId } : {}),
-	        };
-	    }
-	    onTargetInfoChanged(params) {
-	        this.#url = params.targetInfo.url;
-	    }
-	    #initListeners() {
-	        this.#cdpTarget.cdpClient.on('Page.frameNavigated', (params) => {
-	            if (this.id !== params.frame.id) {
-	                return;
-	            }
-	            this.#url = params.frame.url + (params.frame.urlFragment ?? '');
-	            // At the point the page is initialized, all the nested iframes from the
-	            // previous page are detached and realms are destroyed.
-	            // Remove children from context.
-	            this.#deleteAllChildren();
-	        });
-	        this.#cdpTarget.cdpClient.on('Page.navigatedWithinDocument', (params) => {
-	            if (this.id !== params.frameId) {
-	                return;
-	            }
-	            const timestamp = BrowsingContextImpl.getTimestamp();
-	            this.#url = params.url;
-	            this.#navigation.withinDocument.resolve(params);
-	            this.#eventManager.registerEvent({
-	                type: 'event',
-	                method: protocol_js_1$h.ChromiumBidi.BrowsingContext.EventNames.FragmentNavigated,
-	                params: {
-	                    context: this.id,
-	                    navigation: null,
-	                    timestamp,
-	                    url: this.#url,
-	                },
-	            }, this.id);
-	        });
-	        this.#cdpTarget.cdpClient.on('Page.frameStartedLoading', (params) => {
-	            if (this.id !== params.frameId) {
-	                return;
-	            }
-	            this.#eventManager.registerEvent({
-	                type: 'event',
-	                method: protocol_js_1$h.ChromiumBidi.BrowsingContext.EventNames.NavigationStarted,
-	                params: {
-	                    context: this.id,
-	                    navigation: null,
-	                    timestamp: BrowsingContextImpl.getTimestamp(),
-	                    url: '',
-	                },
-	            }, this.id);
-	        });
-	        this.#cdpTarget.cdpClient.on('Page.lifecycleEvent', (params) => {
-	            if (this.id !== params.frameId) {
-	                return;
-	            }
-	            if (params.name === 'init') {
-	                this.#documentChanged(params.loaderId);
-	                return;
-	            }
-	            if (params.name === 'commit') {
-	                this.#loaderId = params.loaderId;
-	                return;
-	            }
-	            // Ignore event from not current navigation.
-	            if (params.loaderId !== this.#loaderId) {
-	                return;
-	            }
-	            const timestamp = BrowsingContextImpl.getTimestamp();
-	            switch (params.name) {
-	                case 'DOMContentLoaded':
-	                    this.#eventManager.registerEvent({
-	                        type: 'event',
-	                        method: protocol_js_1$h.ChromiumBidi.BrowsingContext.EventNames.DomContentLoaded,
-	                        params: {
-	                            context: this.id,
-	                            navigation: this.#loaderId ?? null,
-	                            timestamp,
-	                            url: this.#url,
-	                        },
-	                    }, this.id);
-	                    this.#lifecycle.DOMContentLoaded.resolve(params);
-	                    break;
-	                case 'load':
-	                    this.#eventManager.registerEvent({
-	                        type: 'event',
-	                        method: protocol_js_1$h.ChromiumBidi.BrowsingContext.EventNames.Load,
-	                        params: {
-	                            context: this.id,
-	                            navigation: this.#loaderId ?? null,
-	                            timestamp,
-	                            url: this.#url,
-	                        },
-	                    }, this.id);
-	                    this.#lifecycle.load.resolve(params);
-	                    break;
-	            }
-	        });
-	        this.#cdpTarget.cdpClient.on('Runtime.executionContextCreated', (params) => {
-	            const { auxData, name, uniqueId, id } = params.context;
-	            if (!auxData || auxData.frameId !== this.id) {
-	                return;
-	            }
-	            let origin;
-	            let sandbox;
-	            // Only these execution contexts are supported for now.
-	            switch (auxData.type) {
-	                case 'isolated':
-	                    sandbox = name;
-	                    // Sandbox should have the same origin as the context itself, but in CDP
-	                    // it has an empty one.
-	                    origin = this.#defaultRealm.origin;
-	                    break;
-	                case 'default':
-	                    origin = serializeOrigin(params.context.origin);
-	                    break;
-	                default:
-	                    return;
-	            }
-	            const realm = new WindowRealm_js_1$1.WindowRealm(this.id, this.#browsingContextStorage, this.#cdpTarget.cdpClient, this.#eventManager, id, this.#logger, origin, uniqueId, this.#realmStorage, sandbox, this.#sharedIdWithFrame);
-	            if (auxData.isDefault) {
-	                this.#maybeDefaultRealm = realm;
-	                // Initialize ChannelProxy listeners for all the channels of all the
-	                // preload scripts related to this BrowsingContext.
-	                // TODO: extend for not default realms by the sandbox name.
-	                void Promise.all(this.#cdpTarget
-	                    .getChannels()
-	                    .map((channel) => channel.startListenerFromWindow(realm, this.#eventManager)));
-	            }
-	        });
-	        this.#cdpTarget.cdpClient.on('Runtime.executionContextDestroyed', (params) => {
-	            this.#realmStorage.deleteRealms({
-	                cdpSessionId: this.#cdpTarget.cdpSessionId,
-	                executionContextId: params.executionContextId,
-	            });
-	        });
-	        this.#cdpTarget.cdpClient.on('Runtime.executionContextsCleared', () => {
-	            this.#realmStorage.deleteRealms({
-	                cdpSessionId: this.#cdpTarget.cdpSessionId,
-	            });
-	        });
-	        this.#cdpTarget.cdpClient.on('Page.javascriptDialogClosed', (params) => {
-	            const accepted = params.result;
-	            this.#eventManager.registerEvent({
-	                type: 'event',
-	                method: protocol_js_1$h.ChromiumBidi.BrowsingContext.EventNames.UserPromptClosed,
-	                params: {
-	                    context: this.id,
-	                    accepted,
-	                    userText: accepted && params.userInput ? params.userInput : undefined,
-	                },
-	            }, this.id);
-	        });
-	        this.#cdpTarget.cdpClient.on('Page.javascriptDialogOpening', (params) => {
-	            this.#eventManager.registerEvent({
-	                type: 'event',
-	                method: protocol_js_1$h.ChromiumBidi.BrowsingContext.EventNames.UserPromptOpened,
-	                params: {
-	                    context: this.id,
-	                    type: params.type,
-	                    message: params.message,
-	                    // Don't set the value if empty string
-	                    defaultValue: params.defaultPrompt || undefined,
-	                },
-	            }, this.id);
-	        });
-	    }
-	    #documentChanged(loaderId) {
-	        // Same document navigation.
-	        if (loaderId === undefined || this.#loaderId === loaderId) {
-	            if (this.#navigation.withinDocument.isFinished) {
-	                this.#navigation.withinDocument =
-	                    new Deferred_js_1$2.Deferred();
-	            }
-	            else {
-	                this.#logger?.(BrowsingContextImpl.LOGGER_PREFIX, 'Document changed (navigatedWithinDocument)');
-	            }
-	            return;
-	        }
-	        this.#resetLifecycleIfFinished();
-	        this.#loaderId = loaderId;
-	    }
-	    #resetLifecycleIfFinished() {
-	        if (this.#lifecycle.DOMContentLoaded.isFinished) {
-	            this.#lifecycle.DOMContentLoaded =
-	                new Deferred_js_1$2.Deferred();
-	        }
-	        else {
-	            this.#logger?.(BrowsingContextImpl.LOGGER_PREFIX, 'Document changed (DOMContentLoaded)');
-	        }
-	        if (this.#lifecycle.load.isFinished) {
-	            this.#lifecycle.load = new Deferred_js_1$2.Deferred();
-	        }
-	        else {
-	            this.#logger?.(BrowsingContextImpl.LOGGER_PREFIX, 'Document changed (load)');
-	        }
-	    }
-	    #failLifecycleIfNotFinished() {
-	        if (!this.#lifecycle.DOMContentLoaded.isFinished) {
-	            this.#lifecycle.DOMContentLoaded.reject(new protocol_js_1$h.UnknownErrorException('navigation canceled'));
-	        }
-	        if (!this.#lifecycle.load.isFinished) {
-	            this.#lifecycle.load.reject(new protocol_js_1$h.UnknownErrorException('navigation canceled'));
-	        }
-	    }
-	    async navigate(url, wait) {
-	        try {
-	            new URL(url);
-	        }
-	        catch {
-	            throw new protocol_js_1$h.InvalidArgumentException(`Invalid URL: ${url}`);
-	        }
-	        await this.targetUnblockedOrThrow();
-	        // TODO: handle loading errors.
-	        const cdpNavigateResult = await this.#cdpTarget.cdpClient.sendCommand('Page.navigate', {
-	            url,
-	            frameId: this.id,
-	        });
-	        if (cdpNavigateResult.errorText) {
-	            throw new protocol_js_1$h.UnknownErrorException(cdpNavigateResult.errorText);
-	        }
-	        this.#documentChanged(cdpNavigateResult.loaderId);
-	        switch (wait) {
-	            case "none" /* BrowsingContext.ReadinessState.None */:
-	                break;
-	            case "interactive" /* BrowsingContext.ReadinessState.Interactive */:
-	                // No `loaderId` means same-document navigation.
-	                if (cdpNavigateResult.loaderId === undefined) {
-	                    await this.#navigation.withinDocument;
-	                }
-	                else {
-	                    await this.#lifecycle.DOMContentLoaded;
-	                }
-	                break;
-	            case "complete" /* BrowsingContext.ReadinessState.Complete */:
-	                // No `loaderId` means same-document navigation.
-	                if (cdpNavigateResult.loaderId === undefined) {
-	                    await this.#navigation.withinDocument;
-	                }
-	                else {
-	                    await this.#lifecycle.load;
-	                }
-	                break;
-	        }
-	        return {
-	            navigation: cdpNavigateResult.loaderId ?? null,
-	            // Url can change due to redirect get the latest one.
-	            url: wait === "none" /* BrowsingContext.ReadinessState.None */ ? url : this.#url,
-	        };
-	    }
-	    async reload(ignoreCache, wait) {
-	        await this.targetUnblockedOrThrow();
-	        this.#resetLifecycleIfFinished();
-	        await this.#cdpTarget.cdpClient.sendCommand('Page.reload', {
-	            ignoreCache,
-	        });
-	        switch (wait) {
-	            case "none" /* BrowsingContext.ReadinessState.None */:
-	                break;
-	            case "interactive" /* BrowsingContext.ReadinessState.Interactive */:
-	                await this.#lifecycle.DOMContentLoaded;
-	                break;
-	            case "complete" /* BrowsingContext.ReadinessState.Complete */:
-	                await this.#lifecycle.load;
-	                break;
-	        }
-	        return {
-	            navigation: wait === "none" /* BrowsingContext.ReadinessState.None */
-	                ? null
-	                : this.navigableId ?? null,
-	            url: this.url,
-	        };
-	    }
-	    async setViewport(viewport, devicePixelRatio) {
-	        if (viewport === null && devicePixelRatio === null) {
-	            await this.#cdpTarget.cdpClient.sendCommand('Emulation.clearDeviceMetricsOverride');
-	        }
-	        else {
-	            try {
-	                await this.#cdpTarget.cdpClient.sendCommand('Emulation.setDeviceMetricsOverride', {
-	                    width: viewport ? viewport.width : 0,
-	                    height: viewport ? viewport.height : 0,
-	                    deviceScaleFactor: devicePixelRatio ? devicePixelRatio : 0,
-	                    mobile: false,
-	                    dontSetVisibleSize: true,
-	                });
-	            }
-	            catch (err) {
-	                if (err.message.startsWith(
-	                // https://crsrc.org/c/content/browser/devtools/protocol/emulation_handler.cc;l=257;drc=2f6eee84cf98d4227e7c41718dd71b82f26d90ff
-	                'Width and height values must be positive')) {
-	                    throw new protocol_js_1$h.UnsupportedOperationException('Provided viewport dimensions are not supported');
-	                }
-	                throw err;
-	            }
-	        }
-	    }
-	    async handleUserPrompt(params) {
-	        await this.#cdpTarget.cdpClient.sendCommand('Page.handleJavaScriptDialog', {
-	            accept: params.accept ?? true,
-	            promptText: params.userText,
-	        });
-	    }
-	    async activate() {
-	        await this.#cdpTarget.cdpClient.sendCommand('Page.bringToFront');
-	    }
-	    async captureScreenshot(params) {
-	        if (!this.isTopLevelContext()) {
-	            throw new protocol_js_1$h.UnsupportedOperationException(`Non-top-level 'context' (${params.context}) is currently not supported`);
-	        }
-	        const formatParameters = getImageFormatParameters(params);
-	        // XXX: Focus the original tab after the screenshot is taken.
-	        // This is needed because the screenshot gets blocked until the active tab gets focus.
-	        await this.#cdpTarget.cdpClient.sendCommand('Page.bringToFront');
-	        let captureBeyondViewport = false;
-	        let script;
-	        params.origin ??= 'viewport';
-	        switch (params.origin) {
-	            case 'document': {
-	                script = String(() => {
-	                    const element = document.documentElement;
-	                    return {
-	                        x: 0,
-	                        y: 0,
-	                        width: element.scrollWidth,
-	                        height: element.scrollHeight,
-	                    };
-	                });
-	                captureBeyondViewport = true;
-	                break;
-	            }
-	            case 'viewport': {
-	                script = String(() => {
-	                    const viewport = window.visualViewport;
-	                    return {
-	                        x: viewport.pageLeft,
-	                        y: viewport.pageTop,
-	                        width: viewport.width,
-	                        height: viewport.height,
-	                    };
-	                });
-	                break;
-	            }
-	        }
-	        const realm = await this.getOrCreateSandbox(undefined);
-	        const originResult = await realm.callFunction(script, { type: 'undefined' }, [], false, "none" /* Script.ResultOwnership.None */, {}, false);
-	        (0, assert_js_1$7.assert)(originResult.type === 'success');
-	        const origin = deserializeDOMRect(originResult.result);
-	        (0, assert_js_1$7.assert)(origin);
-	        const rect = params.clip
-	            ? getIntersectionRect(await this.#parseRect(params.clip), origin)
-	            : origin;
-	        if (rect.width === 0 || rect.height === 0) {
-	            throw new protocol_js_1$h.UnableToCaptureScreenException(`Unable to capture screenshot with zero dimensions: width=${rect.width}, height=${rect.height}`);
-	        }
-	        return await this.#cdpTarget.cdpClient.sendCommand('Page.captureScreenshot', {
-	            clip: { ...rect, scale: 1.0 },
-	            ...formatParameters,
-	            captureBeyondViewport,
-	        });
-	    }
-	    async print(params) {
-	        const cdpParams = {};
-	        if (params.background !== undefined) {
-	            cdpParams.printBackground = params.background;
-	        }
-	        if (params.margin?.bottom !== undefined) {
-	            cdpParams.marginBottom = (0, unitConversions_js_1.inchesFromCm)(params.margin.bottom);
-	        }
-	        if (params.margin?.left !== undefined) {
-	            cdpParams.marginLeft = (0, unitConversions_js_1.inchesFromCm)(params.margin.left);
-	        }
-	        if (params.margin?.right !== undefined) {
-	            cdpParams.marginRight = (0, unitConversions_js_1.inchesFromCm)(params.margin.right);
-	        }
-	        if (params.margin?.top !== undefined) {
-	            cdpParams.marginTop = (0, unitConversions_js_1.inchesFromCm)(params.margin.top);
-	        }
-	        if (params.orientation !== undefined) {
-	            cdpParams.landscape = params.orientation === 'landscape';
-	        }
-	        if (params.page?.height !== undefined) {
-	            cdpParams.paperHeight = (0, unitConversions_js_1.inchesFromCm)(params.page.height);
-	        }
-	        if (params.page?.width !== undefined) {
-	            cdpParams.paperWidth = (0, unitConversions_js_1.inchesFromCm)(params.page.width);
-	        }
-	        if (params.pageRanges !== undefined) {
-	            for (const range of params.pageRanges) {
-	                if (typeof range === 'number') {
-	                    continue;
-	                }
-	                const rangeParts = range.split('-');
-	                if (rangeParts.length < 1 || rangeParts.length > 2) {
-	                    throw new protocol_js_1$h.InvalidArgumentException(`Invalid page range: ${range} is not a valid integer range.`);
-	                }
-	                if (rangeParts.length === 1) {
-	                    void parseInteger(rangeParts[0] ?? '');
-	                    continue;
-	                }
-	                let lowerBound;
-	                let upperBound;
-	                const [rangeLowerPart = '', rangeUpperPart = ''] = rangeParts;
-	                if (rangeLowerPart === '') {
-	                    lowerBound = 1;
-	                }
-	                else {
-	                    lowerBound = parseInteger(rangeLowerPart);
-	                }
-	                if (rangeUpperPart === '') {
-	                    upperBound = Number.MAX_SAFE_INTEGER;
-	                }
-	                else {
-	                    upperBound = parseInteger(rangeUpperPart);
-	                }
-	                if (lowerBound > upperBound) {
-	                    throw new protocol_js_1$h.InvalidArgumentException(`Invalid page range: ${rangeLowerPart} > ${rangeUpperPart}`);
-	                }
-	            }
-	            cdpParams.pageRanges = params.pageRanges.join(',');
-	        }
-	        if (params.scale !== undefined) {
-	            cdpParams.scale = params.scale;
-	        }
-	        if (params.shrinkToFit !== undefined) {
-	            cdpParams.preferCSSPageSize = !params.shrinkToFit;
-	        }
-	        try {
-	            const result = await this.#cdpTarget.cdpClient.sendCommand('Page.printToPDF', cdpParams);
-	            return {
-	                data: result.data,
-	            };
-	        }
-	        catch (error) {
-	            // Effectively zero dimensions.
-	            if (error.message ===
-	                'invalid print parameters: content area is empty') {
-	                throw new protocol_js_1$h.UnsupportedOperationException(error.message);
-	            }
-	            throw error;
-	        }
-	    }
-	    /**
-	     * See
-	     * https://w3c.github.io/webdriver-bidi/#:~:text=If%20command%20parameters%20contains%20%22clip%22%3A
-	     */
-	    async #parseRect(clip) {
-	        switch (clip.type) {
-	            case 'box':
-	                return { x: clip.x, y: clip.y, width: clip.width, height: clip.height };
-	            case 'element': {
-	                // TODO: #1213: Use custom sandbox specifically for Chromium BiDi
-	                const sandbox = await this.getOrCreateSandbox(undefined);
-	                const result = await sandbox.callFunction(String((element) => {
-	                    return element instanceof Element;
-	                }), { type: 'undefined' }, [clip.element], false, "none" /* Script.ResultOwnership.None */, {});
-	                if (result.type === 'exception') {
-	                    throw new protocol_js_1$h.NoSuchElementException(`Element '${clip.element.sharedId}' was not found`);
-	                }
-	                (0, assert_js_1$7.assert)(result.result.type === 'boolean');
-	                if (!result.result.value) {
-	                    throw new protocol_js_1$h.NoSuchElementException(`Node '${clip.element.sharedId}' is not an Element`);
-	                }
-	                {
-	                    const result = await sandbox.callFunction(String((element) => {
-	                        const rect = element.getBoundingClientRect();
-	                        return {
-	                            x: rect.x,
-	                            y: rect.y,
-	                            height: rect.height,
-	                            width: rect.width,
-	                        };
-	                    }), { type: 'undefined' }, [clip.element], false, "none" /* Script.ResultOwnership.None */, {});
-	                    (0, assert_js_1$7.assert)(result.type === 'success');
-	                    const rect = deserializeDOMRect(result.result);
-	                    if (!rect) {
-	                        throw new protocol_js_1$h.UnableToCaptureScreenException(`Could not get bounding box for Element '${clip.element.sharedId}'`);
-	                    }
-	                    return rect;
-	                }
-	            }
-	        }
-	    }
-	    async close() {
-	        await this.#cdpTarget.cdpClient.sendCommand('Page.close');
-	    }
-	    async traverseHistory(delta) {
-	        if (delta === 0) {
-	            return;
-	        }
-	        const history = await this.#cdpTarget.cdpClient.sendCommand('Page.getNavigationHistory');
-	        const entry = history.entries[history.currentIndex + delta];
-	        if (!entry) {
-	            throw new protocol_js_1$h.NoSuchHistoryEntryException(`No history entry at delta ${delta}`);
-	        }
-	        await this.#cdpTarget.cdpClient.sendCommand('Page.navigateToHistoryEntry', {
-	            entryId: entry.id,
-	        });
-	    }
-	    async toggleModulesIfNeeded() {
-	        const enableNetwork = this.#eventManager.subscriptionManager.isSubscribedTo(chromium_bidi_js_1$1.BiDiModule.Network, this.id);
-	        await this.#cdpTarget.toggleNetworkIfNeeded(enableNetwork);
-	    }
-	    async locateNodes(params) {
-	        // TODO: test sandboxing.
-	        if (params.maxNodeCount !== undefined) {
-	            // TODO: implement.
-	            throw new protocol_js_1$h.UnsupportedOperationException(`maxNodeCount is not supported`);
-	        }
-	        if (params.serializationOptions !== undefined) {
-	            // TODO: implement.
-	            throw new protocol_js_1$h.UnsupportedOperationException(`serializationOptions is not supported`);
-	        }
-	        if (params.startNodes !== undefined) {
-	            // TODO: implement.
-	            throw new protocol_js_1$h.UnsupportedOperationException(`serializationOptions is not supported`);
-	        }
-	        return await this.#locateNodesByLocator(this.#defaultRealm, params.locator);
-	    }
-	    #getLocatorFunction(locator) {
-	        switch (locator.type) {
-	            case 'css':
-	                return String((cssSelector) => {
-	                    const results = document.querySelectorAll(cssSelector);
-	                    const array = [];
-	                    for (const item of results) {
-	                        array.push(item);
-	                    }
-	                    return array;
-	                });
-	            case 'xpath':
-	                return String((xPathSelector) => {
-	                    // https://w3c.github.io/webdriver-bidi/#locate-nodes-using-xpath
-	                    const evaluator = new XPathEvaluator();
-	                    const expression = evaluator.createExpression(xPathSelector);
-	                    const xPathResult = expression.evaluate(document, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
-	                    const result = [];
-	                    for (let i = 0; i < xPathResult.snapshotLength; i++) {
-	                        result.push(xPathResult.snapshotItem(i));
-	                    }
-	                    return result;
-	                });
-	            default:
-	                throw new protocol_js_1$h.UnsupportedOperationException(`locateNodes does not support ${locator.type} locator type.`);
-	        }
-	    }
-	    async #locateNodesByLocator(realm, locator) {
-	        const locatorFunction = this.#getLocatorFunction(locator);
-	        const locatorResult = await realm.callFunction(locatorFunction, { type: 'undefined' }, [{ type: 'string', value: locator.value }], false, "none" /* Script.ResultOwnership.None */, {}, false);
-	        if (locatorResult.type !== 'success') {
-	            this.#logger?.(BrowsingContextImpl.LOGGER_PREFIX, 'Failed locateNodesByLocator', locatorResult);
-	            // Heuristic to detect invalid selector for different types of selectors.
-	            if (
-	            // CSS selector.
-	            locatorResult.exceptionDetails.text?.endsWith('is not a valid selector.') ||
-	                // XPath selector.
-	                locatorResult.exceptionDetails.text?.endsWith('is not a valid XPath expression.')) {
-	                throw new protocol_js_1$h.InvalidSelectorException(`Not valid selector ${locator.value}`);
-	            }
-	            throw new protocol_js_1$h.UnknownErrorException(`Unexpected error in selector script: ${locatorResult.exceptionDetails.text}`);
-	        }
-	        if (locatorResult.result.type !== 'array') {
-	            throw new protocol_js_1$h.UnknownErrorException(`Unexpected selector script result type: ${locatorResult.result.type}`);
-	        }
-	        // Check there are no non-node elements in the result.
-	        const nodes = locatorResult.result.value.map((value) => {
-	            if (value.type !== 'node') {
-	                throw new protocol_js_1$h.UnknownErrorException(`Unexpected selector script result element: ${value.type}`);
-	            }
-	            return value;
-	        });
-	        return { nodes };
-	    }
-	}
-	BrowsingContextImpl$1.BrowsingContextImpl = BrowsingContextImpl;
-	function serializeOrigin(origin) {
-	    // https://html.spec.whatwg.org/multipage/origin.html#ascii-serialisation-of-an-origin
-	    if (['://', ''].includes(origin)) {
-	        origin = 'null';
-	    }
-	    return origin;
-	}
-	BrowsingContextImpl$1.serializeOrigin = serializeOrigin;
-	function getImageFormatParameters(params) {
-	    const { quality, type } = params.format ?? {
-	        type: 'image/png',
-	    };
-	    switch (type) {
-	        case 'image/png': {
-	            return { format: 'png' };
-	        }
-	        case 'image/jpeg': {
-	            return {
-	                format: 'jpeg',
-	                ...(quality === undefined ? {} : { quality: Math.round(quality * 100) }),
-	            };
-	        }
-	        case 'image/webp': {
-	            return {
-	                format: 'webp',
-	                ...(quality === undefined ? {} : { quality: Math.round(quality * 100) }),
-	            };
-	        }
-	    }
-	    throw new protocol_js_1$h.InvalidArgumentException(`Image format '${type}' is not a supported format`);
-	}
-	function deserializeDOMRect(result) {
-	    if (result.type !== 'object' || result.value === undefined) {
-	        return;
-	    }
-	    const x = result.value.find(([key]) => {
-	        return key === 'x';
-	    })?.[1];
-	    const y = result.value.find(([key]) => {
-	        return key === 'y';
-	    })?.[1];
-	    const height = result.value.find(([key]) => {
-	        return key === 'height';
-	    })?.[1];
-	    const width = result.value.find(([key]) => {
-	        return key === 'width';
-	    })?.[1];
-	    if (x?.type !== 'number' ||
-	        y?.type !== 'number' ||
-	        height?.type !== 'number' ||
-	        width?.type !== 'number') {
-	        return;
-	    }
-	    return {
-	        x: x.value,
-	        y: y.value,
-	        width: width.value,
-	        height: height.value,
-	    };
-	}
-	/** @see https://w3c.github.io/webdriver-bidi/#normalize-rect */
-	function normalizeRect(box) {
-	    return {
-	        ...(box.width < 0
-	            ? {
-	                x: box.x + box.width,
-	                width: -box.width,
-	            }
-	            : {
-	                x: box.x,
-	                width: box.width,
-	            }),
-	        ...(box.height < 0
-	            ? {
-	                y: box.y + box.height,
-	                height: -box.height,
-	            }
-	            : {
-	                y: box.y,
-	                height: box.height,
-	            }),
-	    };
-	}
-	/** @see https://w3c.github.io/webdriver-bidi/#rectangle-intersection */
-	function getIntersectionRect(first, second) {
-	    first = normalizeRect(first);
-	    second = normalizeRect(second);
-	    const x = Math.max(first.x, second.x);
-	    const y = Math.max(first.y, second.y);
-	    return {
-	        x,
-	        y,
-	        width: Math.max(Math.min(first.x + first.width, second.x + second.width) - x, 0),
-	        height: Math.max(Math.min(first.y + first.height, second.y + second.height) - y, 0),
-	    };
-	}
-	function parseInteger(value) {
-	    value = value.trim();
-	    if (!/^[0-9]+$/.test(value)) {
-	        throw new protocol_js_1$h.InvalidArgumentException(`Invalid integer: ${value}`);
-	    }
-	    return parseInt(value);
-	}
-
-	var CdpTarget$1 = {};
-
-	var LogManager$1 = {};
-
-	var logHelper = {};
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(logHelper, "__esModule", { value: true });
-	logHelper.getRemoteValuesText = logHelper.logMessageFormatter = void 0;
-	const assert_js_1$6 = assert$1;
-	const specifiers = ['%s', '%d', '%i', '%f', '%o', '%O', '%c'];
-	function isFormatSpecifier(str) {
-	    return specifiers.some((spec) => str.includes(spec));
-	}
-	/**
-	 * @param args input remote values to be format printed
-	 * @return parsed text of the remote values in specific format
-	 */
-	function logMessageFormatter(args) {
-	    let output = '';
-	    const argFormat = args[0].value.toString();
-	    const argValues = args.slice(1, undefined);
-	    const tokens = argFormat.split(new RegExp(specifiers.map((spec) => `(${spec})`).join('|'), 'g'));
-	    for (const token of tokens) {
-	        if (token === undefined || token === '') {
-	            continue;
-	        }
-	        if (isFormatSpecifier(token)) {
-	            const arg = argValues.shift();
-	            // raise an exception when less value is provided
-	            (0, assert_js_1$6.assert)(arg, `Less value is provided: "${getRemoteValuesText(args, false)}"`);
-	            if (token === '%s') {
-	                output += stringFromArg(arg);
-	            }
-	            else if (token === '%d' || token === '%i') {
-	                if (arg.type === 'bigint' ||
-	                    arg.type === 'number' ||
-	                    arg.type === 'string') {
-	                    output += parseInt(arg.value.toString(), 10);
-	                }
-	                else {
-	                    output += 'NaN';
-	                }
-	            }
-	            else if (token === '%f') {
-	                if (arg.type === 'bigint' ||
-	                    arg.type === 'number' ||
-	                    arg.type === 'string') {
-	                    output += parseFloat(arg.value.toString());
-	                }
-	                else {
-	                    output += 'NaN';
-	                }
-	            }
-	            else {
-	                // %o, %O, %c
-	                output += toJson(arg);
-	            }
-	        }
-	        else {
-	            output += token;
-	        }
-	    }
-	    // raise an exception when more value is provided
-	    if (argValues.length > 0) {
-	        throw new Error(`More value is provided: "${getRemoteValuesText(args, false)}"`);
-	    }
-	    return output;
-	}
-	logHelper.logMessageFormatter = logMessageFormatter;
-	/**
-	 * @param arg input remote value to be parsed
-	 * @return parsed text of the remote value
-	 *
-	 * input: {"type": "number", "value": 1}
-	 * output: 1
-	 *
-	 * input: {"type": "string", "value": "abc"}
-	 * output: "abc"
-	 *
-	 * input: {"type": "object",  "value": [["id", {"type": "number", "value": 1}]]}
-	 * output: '{"id": 1}'
-	 *
-	 * input: {"type": "object", "value": [["font-size", {"type": "string", "value": "20px"}]]}
-	 * output: '{"font-size": "20px"}'
-	 */
-	function toJson(arg) {
-	    // arg type validation
-	    if (arg.type !== 'array' &&
-	        arg.type !== 'bigint' &&
-	        arg.type !== 'date' &&
-	        arg.type !== 'number' &&
-	        arg.type !== 'object' &&
-	        arg.type !== 'string') {
-	        return stringFromArg(arg);
-	    }
-	    if (arg.type === 'bigint') {
-	        return `${arg.value.toString()}n`;
-	    }
-	    if (arg.type === 'number') {
-	        return arg.value.toString();
-	    }
-	    if (['date', 'string'].includes(arg.type)) {
-	        return JSON.stringify(arg.value);
-	    }
-	    if (arg.type === 'object') {
-	        return `{${arg.value
-            .map((pair) => {
-            return `${JSON.stringify(pair[0])}:${toJson(pair[1])}`;
-        })
-            .join(',')}}`;
-	    }
-	    if (arg.type === 'array') {
-	        return `[${arg.value?.map((val) => toJson(val)).join(',') ?? ''}]`;
-	    }
-	    // eslint-disable-next-line @typescript-eslint/no-base-to-string
-	    throw Error(`Invalid value type: ${arg}`);
-	}
-	function stringFromArg(arg) {
-	    if (!Object.hasOwn(arg, 'value')) {
-	        return arg.type;
-	    }
-	    switch (arg.type) {
-	        case 'string':
-	        case 'number':
-	        case 'boolean':
-	        case 'bigint':
-	            return String(arg.value);
-	        case 'regexp':
-	            return `/${arg.value.pattern}/${arg.value.flags ?? ''}`;
-	        case 'date':
-	            return new Date(arg.value).toString();
-	        case 'object':
-	            return `Object(${arg.value?.length ?? ''})`;
-	        case 'array':
-	            return `Array(${arg.value?.length ?? ''})`;
-	        case 'map':
-	            return `Map(${arg.value?.length})`;
-	        case 'set':
-	            return `Set(${arg.value?.length})`;
-	        default:
-	            return arg.type;
-	    }
-	}
-	function getRemoteValuesText(args, formatText) {
-	    const arg = args[0];
-	    if (!arg) {
-	        return '';
-	    }
-	    // if args[0] is a format specifier, format the args as output
-	    if (arg.type === 'string' &&
-	        isFormatSpecifier(arg.value.toString()) &&
-	        formatText) {
-	        return logMessageFormatter(args);
-	    }
-	    // if args[0] is not a format specifier, just join the args with \u0020 (unicode 'SPACE')
-	    return args
-	        .map((arg) => {
-	        return stringFromArg(arg);
-	    })
-	        .join('\u0020');
-	}
-	logHelper.getRemoteValuesText = getRemoteValuesText;
-
-	Object.defineProperty(LogManager$1, "__esModule", { value: true });
-	LogManager$1.LogManager = void 0;
-	const protocol_js_1$g = protocol;
-	const log_js_1$9 = log$1;
-	const logHelper_js_1 = logHelper;
-	/** Converts CDP StackTrace object to BiDi StackTrace object. */
-	function getBidiStackTrace(cdpStackTrace) {
-	    const stackFrames = cdpStackTrace?.callFrames.map((callFrame) => {
-	        return {
-	            columnNumber: callFrame.columnNumber,
-	            functionName: callFrame.functionName,
-	            lineNumber: callFrame.lineNumber,
-	            url: callFrame.url,
-	        };
-	    });
-	    return stackFrames ? { callFrames: stackFrames } : undefined;
-	}
-	function getLogLevel(consoleApiType) {
-	    if (["error" /* Log.Level.Error */, 'assert'].includes(consoleApiType)) {
-	        return "error" /* Log.Level.Error */;
-	    }
-	    if (["debug" /* Log.Level.Debug */, 'trace'].includes(consoleApiType)) {
-	        return "debug" /* Log.Level.Debug */;
-	    }
-	    if (["warn" /* Log.Level.Warn */, 'warning'].includes(consoleApiType)) {
-	        return "warn" /* Log.Level.Warn */;
-	    }
-	    return "info" /* Log.Level.Info */;
-	}
-	class LogManager {
-	    #eventManager;
-	    #realmStorage;
-	    #cdpTarget;
-	    #logger;
-	    constructor(cdpTarget, realmStorage, eventManager, logger) {
-	        this.#cdpTarget = cdpTarget;
-	        this.#realmStorage = realmStorage;
-	        this.#eventManager = eventManager;
-	        this.#logger = logger;
-	    }
-	    static create(cdpTarget, realmStorage, eventManager, logger) {
-	        const logManager = new LogManager(cdpTarget, realmStorage, eventManager, logger);
-	        logManager.#initializeEntryAddedEventListener();
-	        return logManager;
-	    }
-	    #initializeEntryAddedEventListener() {
-	        this.#cdpTarget.cdpClient.on('Runtime.consoleAPICalled', (params) => {
-	            // Try to find realm by `cdpSessionId` and `executionContextId`,
-	            // if provided.
-	            const realm = this.#realmStorage.findRealm({
-	                cdpSessionId: this.#cdpTarget.cdpSessionId,
-	                executionContextId: params.executionContextId,
-	            });
-	            if (realm === undefined) {
-	                // Ignore exceptions not attached to any realm.
-	                this.#logger?.(log_js_1$9.LogType.cdp, params);
-	                return;
-	            }
-	            const argsPromise = realm === undefined
-	                ? Promise.resolve(params.args)
-	                : // Properly serialize arguments if possible.
-	                    Promise.all(params.args.map((arg) => {
-	                        return realm.serializeCdpObject(arg, "none" /* Script.ResultOwnership.None */);
-	                    }));
-	            for (const browsingContext of realm.associatedBrowsingContexts) {
-	                this.#eventManager.registerPromiseEvent(argsPromise.then((args) => ({
-	                    kind: 'success',
-	                    value: {
-	                        type: 'event',
-	                        method: protocol_js_1$g.ChromiumBidi.Log.EventNames.LogEntryAdded,
-	                        params: {
-	                            level: getLogLevel(params.type),
-	                            source: realm.source,
-	                            text: (0, logHelper_js_1.getRemoteValuesText)(args, true),
-	                            timestamp: Math.round(params.timestamp),
-	                            stackTrace: getBidiStackTrace(params.stackTrace),
-	                            type: 'console',
-	                            // Console method is `warn`, not `warning`.
-	                            method: params.type === 'warning' ? 'warn' : params.type,
-	                            args,
-	                        },
-	                    },
-	                })), browsingContext.id, protocol_js_1$g.ChromiumBidi.Log.EventNames.LogEntryAdded);
-	            }
-	        });
-	        this.#cdpTarget.cdpClient.on('Runtime.exceptionThrown', (params) => {
-	            // Try to find realm by `cdpSessionId` and `executionContextId`,
-	            // if provided.
-	            const realm = this.#realmStorage.findRealm({
-	                cdpSessionId: this.#cdpTarget.cdpSessionId,
-	                executionContextId: params.exceptionDetails.executionContextId,
-	            });
-	            if (realm === undefined) {
-	                // Ignore exceptions not attached to any realm.
-	                this.#logger?.(log_js_1$9.LogType.cdp, params);
-	                return;
-	            }
-	            for (const browsingContext of realm.associatedBrowsingContexts) {
-	                this.#eventManager.registerPromiseEvent(LogManager.#getExceptionText(params, realm).then((text) => ({
-	                    kind: 'success',
-	                    value: {
-	                        type: 'event',
-	                        method: protocol_js_1$g.ChromiumBidi.Log.EventNames.LogEntryAdded,
-	                        params: {
-	                            level: "error" /* Log.Level.Error */,
-	                            source: realm.source,
-	                            text,
-	                            timestamp: Math.round(params.timestamp),
-	                            stackTrace: getBidiStackTrace(params.exceptionDetails.stackTrace),
-	                            type: 'javascript',
-	                        },
-	                    },
-	                })), browsingContext.id, protocol_js_1$g.ChromiumBidi.Log.EventNames.LogEntryAdded);
-	            }
-	        });
-	    }
-	    /**
-	     * Try the best to get the exception text.
-	     */
-	    static async #getExceptionText(params, realm) {
-	        if (!params.exceptionDetails.exception) {
-	            return params.exceptionDetails.text;
-	        }
-	        if (realm === undefined) {
-	            return JSON.stringify(params.exceptionDetails.exception);
-	        }
-	        return await realm.stringifyObject(params.exceptionDetails.exception);
-	    }
-	}
-	LogManager$1.LogManager = LogManager;
-
-	Object.defineProperty(CdpTarget$1, "__esModule", { value: true });
-	CdpTarget$1.CdpTarget = void 0;
-	const chromium_bidi_js_1 = chromiumBidi;
-	const Deferred_js_1$1 = Deferred$1;
-	const LogManager_js_1 = LogManager$1;
-	class CdpTarget {
-	    #id;
-	    #cdpClient;
-	    #browserCdpClient;
-	    #eventManager;
-	    #preloadScriptStorage;
-	    #networkStorage;
-	    #unblocked = new Deferred_js_1$1.Deferred();
-	    #acceptInsecureCerts;
-	    #networkDomainEnabled = false;
-	    #fetchDomainStages = {
-	        request: false,
-	        response: false,
-	        auth: false,
-	    };
-	    static create(targetId, cdpClient, browserCdpClient, realmStorage, eventManager, preloadScriptStorage, networkStorage, acceptInsecureCerts, logger) {
-	        const cdpTarget = new CdpTarget(targetId, cdpClient, browserCdpClient, eventManager, preloadScriptStorage, networkStorage, acceptInsecureCerts);
-	        LogManager_js_1.LogManager.create(cdpTarget, realmStorage, eventManager, logger);
-	        cdpTarget.#setEventListeners();
-	        // No need to await.
-	        // Deferred will be resolved when the target is unblocked.
-	        void cdpTarget.#unblock();
-	        return cdpTarget;
-	    }
-	    constructor(targetId, cdpClient, browserCdpClient, eventManager, preloadScriptStorage, networkStorage, acceptInsecureCerts) {
-	        this.#id = targetId;
-	        this.#cdpClient = cdpClient;
-	        this.#browserCdpClient = browserCdpClient;
-	        this.#eventManager = eventManager;
-	        this.#preloadScriptStorage = preloadScriptStorage;
-	        this.#networkStorage = networkStorage;
-	        this.#acceptInsecureCerts = acceptInsecureCerts;
-	    }
-	    /** Returns a deferred that resolves when the target is unblocked. */
-	    get unblocked() {
-	        return this.#unblocked;
-	    }
-	    get id() {
-	        return this.#id;
-	    }
-	    get cdpClient() {
-	        return this.#cdpClient;
-	    }
-	    get browserCdpClient() {
-	        return this.#browserCdpClient;
-	    }
-	    /** Needed for CDP escape path. */
-	    get cdpSessionId() {
-	        // SAFETY we got the client by it's id for creating
-	        return this.#cdpClient.sessionId;
-	    }
-	    /**
-	     * Enables all the required CDP domains and unblocks the target.
-	     */
-	    async #unblock() {
-	        // Check if the network domain is enabled globally.
-	        const enabledNetwork = this.isSubscribedTo(chromium_bidi_js_1.BiDiModule.Network);
-	        this.#networkDomainEnabled = enabledNetwork;
-	        try {
-	            await Promise.all([
-	                this.#cdpClient.sendCommand('Runtime.enable'),
-	                this.#cdpClient.sendCommand('Page.enable'),
-	                this.#cdpClient.sendCommand('Page.setLifecycleEventsEnabled', {
-	                    enabled: true,
-	                }),
-	                // Set ignore certificate errors for each target.
-	                this.#cdpClient.sendCommand('Security.setIgnoreCertificateErrors', {
-	                    ignore: this.#acceptInsecureCerts,
-	                }),
-	                // TODO: enable Network domain for OOPiF targets.
-	                enabledNetwork
-	                    ? this.#cdpClient.sendCommand('Network.enable')
-	                    : undefined,
-	                this.toggleFetchIfNeeded(),
-	                this.#cdpClient.sendCommand('Target.setAutoAttach', {
-	                    autoAttach: true,
-	                    waitForDebuggerOnStart: true,
-	                    flatten: true,
-	                }),
-	                this.#initAndEvaluatePreloadScripts(),
-	                this.#cdpClient.sendCommand('Runtime.runIfWaitingForDebugger'),
-	            ]);
-	        }
-	        catch (error) {
-	            // The target might have been closed before the initialization finished.
-	            if (!this.#cdpClient.isCloseError(error)) {
-	                this.#unblocked.resolve({
-	                    kind: 'error',
-	                    error,
-	                });
-	                return;
-	            }
-	        }
-	        this.#unblocked.resolve({
-	            kind: 'success',
-	            value: undefined,
-	        });
-	    }
-	    async toggleFetchIfNeeded() {
-	        const stages = this.#networkStorage.getInterceptionStages(this.id);
-	        if (
-	        // Only toggle interception when Network is enabled
-	        !this.#networkDomainEnabled ||
-	            (this.#fetchDomainStages.request === stages.request &&
-	                this.#fetchDomainStages.response === stages.response &&
-	                this.#fetchDomainStages.auth === stages.auth)) {
-	            return;
-	        }
-	        const patterns = [];
-	        this.#fetchDomainStages = stages;
-	        if (stages.request || stages.auth) {
-	            // CDP quirk we need request interception when we intercept auth
-	            patterns.push({
-	                urlPattern: '*',
-	                requestStage: 'Request',
-	            });
-	        }
-	        if (stages.response) {
-	            patterns.push({
-	                urlPattern: '*',
-	                requestStage: 'Response',
-	            });
-	        }
-	        if (patterns.length) {
-	            await this.#cdpClient.sendCommand('Fetch.enable', {
-	                patterns,
-	                handleAuthRequests: stages.auth,
-	            });
-	        }
-	        else {
-	            await this.#cdpClient.sendCommand('Fetch.disable');
-	        }
-	    }
-	    async toggleNetworkIfNeeded(enabled) {
-	        if (enabled === this.#networkDomainEnabled) {
-	            return;
-	        }
-	        this.#networkDomainEnabled = enabled;
-	        try {
-	            await Promise.all([
-	                this.#cdpClient.sendCommand(enabled ? 'Network.enable' : 'Network.disable'),
-	                this.toggleFetchIfNeeded(),
-	            ]);
-	        }
-	        catch (err) {
-	            this.#networkDomainEnabled = !enabled;
-	        }
-	    }
-	    #setEventListeners() {
-	        this.#cdpClient.on('*', (event, params) => {
-	            // We may encounter uses for EventEmitter other than CDP events,
-	            // which we want to skip.
-	            if (typeof event !== 'string') {
-	                return;
-	            }
-	            this.#eventManager.registerEvent({
-	                type: 'event',
-	                method: `cdp.${event}`,
-	                params: {
-	                    event,
-	                    params,
-	                    session: this.cdpSessionId,
-	                },
-	            }, this.id);
-	        });
-	    }
-	    /**
-	     * All the ProxyChannels from all the preload scripts of the given
-	     * BrowsingContext.
-	     */
-	    getChannels() {
-	        return this.#preloadScriptStorage
-	            .find()
-	            .flatMap((script) => script.channels);
-	    }
-	    /** Loads all top-level preload scripts. */
-	    async #initAndEvaluatePreloadScripts() {
-	        for (const script of this.#preloadScriptStorage.find({
-	            global: true,
-	        })) {
-	            await script.initInTarget(this, true);
-	        }
-	    }
-	    isSubscribedTo(moduleOrEvent) {
-	        return this.#eventManager.subscriptionManager.isSubscribedTo(moduleOrEvent, this.id);
-	    }
-	}
-	CdpTarget$1.CdpTarget = CdpTarget;
-
-	Object.defineProperty(BrowsingContextProcessor$1, "__esModule", { value: true });
-	BrowsingContextProcessor$1.BrowsingContextProcessor = void 0;
-	const protocol_js_1$f = protocol;
-	const log_js_1$8 = log$1;
-	const WorkerRealm_js_1 = WorkerRealm$1;
-	const BrowsingContextImpl_js_1 = BrowsingContextImpl$1;
-	const CdpTarget_js_1 = CdpTarget$1;
-	const cdpToBidiTargetTypes = {
-	    service_worker: 'service-worker',
-	    shared_worker: 'shared-worker',
-	    worker: 'dedicated-worker',
-	};
-	class BrowsingContextProcessor {
-	    #browserCdpClient;
-	    #cdpConnection;
-	    #selfTargetId;
-	    #eventManager;
-	    #browsingContextStorage;
-	    #networkStorage;
-	    #acceptInsecureCerts;
-	    #sharedIdWithFrame;
-	    #preloadScriptStorage;
-	    #realmStorage;
-	    #defaultUserContextId;
-	    #logger;
-	    constructor(cdpConnection, browserCdpClient, selfTargetId, eventManager, browsingContextStorage, realmStorage, networkStorage, preloadScriptStorage, acceptInsecureCerts, sharedIdWithFrame, defaultUserContextId, logger) {
-	        this.#acceptInsecureCerts = acceptInsecureCerts;
-	        this.#cdpConnection = cdpConnection;
-	        this.#browserCdpClient = browserCdpClient;
-	        this.#selfTargetId = selfTargetId;
-	        this.#eventManager = eventManager;
-	        this.#browsingContextStorage = browsingContextStorage;
-	        this.#preloadScriptStorage = preloadScriptStorage;
-	        this.#networkStorage = networkStorage;
-	        this.#realmStorage = realmStorage;
-	        this.#sharedIdWithFrame = sharedIdWithFrame;
-	        this.#defaultUserContextId = defaultUserContextId;
-	        this.#logger = logger;
-	        this.#setEventListeners(browserCdpClient);
-	    }
-	    getTree(params) {
-	        const resultContexts = params.root === undefined
-	            ? this.#browsingContextStorage.getTopLevelContexts()
-	            : [this.#browsingContextStorage.getContext(params.root)];
-	        return {
-	            contexts: resultContexts.map((c) => c.serializeToBidiValue(params.maxDepth ?? Number.MAX_VALUE)),
-	        };
-	    }
-	    async create(params) {
-	        let referenceContext;
-	        let userContext = 'default';
-	        if (params.referenceContext !== undefined) {
-	            referenceContext = this.#browsingContextStorage.getContext(params.referenceContext);
-	            if (!referenceContext.isTopLevelContext()) {
-	                throw new protocol_js_1$f.InvalidArgumentException(`referenceContext should be a top-level context`);
-	            }
-	            userContext = referenceContext.userContext;
-	        }
-	        if (params.userContext !== undefined) {
-	            userContext = params.userContext;
-	        }
-	        let newWindow = false;
-	        switch (params.type) {
-	            case "tab" /* BrowsingContext.CreateType.Tab */:
-	                newWindow = false;
-	                break;
-	            case "window" /* BrowsingContext.CreateType.Window */:
-	                newWindow = true;
-	                break;
-	        }
-	        if (userContext !== 'default') {
-	            const existingContexts = this.#browsingContextStorage
-	                .getAllContexts()
-	                .filter((context) => context.userContext === userContext);
-	            if (!existingContexts.length) {
-	                // If there are no contexts in the given user context, we need to set
-	                // newWindow to true as newWindow=false will be rejected.
-	                newWindow = true;
-	            }
-	        }
-	        let result;
-	        try {
-	            result = await this.#browserCdpClient.sendCommand('Target.createTarget', {
-	                url: 'about:blank',
-	                newWindow,
-	                browserContextId: userContext === 'default' ? undefined : userContext,
-	            });
-	        }
-	        catch (err) {
-	            if (
-	            // See https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/devtools/protocol/target_handler.cc;l=90;drc=e80392ac11e48a691f4309964cab83a3a59e01c8
-	            err.message.startsWith('Failed to find browser context with id') ||
-	                // See https://source.chromium.org/chromium/chromium/src/+/main:headless/lib/browser/protocol/target_handler.cc;l=49;drc=e80392ac11e48a691f4309964cab83a3a59e01c8
-	                err.message === 'browserContextId') {
-	                throw new protocol_js_1$f.NoSuchUserContextException(`The context ${userContext} was not found`);
-	            }
-	            throw err;
-	        }
-	        // Wait for the new tab to be loaded to avoid race conditions in the
-	        // `browsingContext` events, when the `browsingContext.domContentLoaded` and
-	        // `browsingContext.load` events from the initial `about:blank` navigation
-	        // are emitted after the next navigation is started.
-	        // Details: https://github.com/web-platform-tests/wpt/issues/35846
-	        const contextId = result.targetId;
-	        const context = this.#browsingContextStorage.getContext(contextId);
-	        await context.lifecycleLoaded();
-	        return { context: context.id };
-	    }
-	    navigate(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        return context.navigate(params.url, params.wait ?? "none" /* BrowsingContext.ReadinessState.None */);
-	    }
-	    reload(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        return context.reload(params.ignoreCache ?? false, params.wait ?? "none" /* BrowsingContext.ReadinessState.None */);
-	    }
-	    async activate(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        if (!context.isTopLevelContext()) {
-	            throw new protocol_js_1$f.InvalidArgumentException('Activation is only supported on the top-level context');
-	        }
-	        await context.activate();
-	        return {};
-	    }
-	    async captureScreenshot(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        return await context.captureScreenshot(params);
-	    }
-	    async print(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        return await context.print(params);
-	    }
-	    async setViewport(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        if (!context.isTopLevelContext()) {
-	            throw new protocol_js_1$f.InvalidArgumentException('Emulating viewport is only supported on the top-level context');
-	        }
-	        await context.setViewport(params.viewport, params.devicePixelRatio);
-	        return {};
-	    }
-	    async traverseHistory(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        if (!context) {
-	            throw new protocol_js_1$f.InvalidArgumentException(`No browsing context with id ${params.context}`);
-	        }
-	        await context.traverseHistory(params.delta);
-	        return {};
-	    }
-	    async handleUserPrompt(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        await context.handleUserPrompt(params);
-	        return {};
-	    }
-	    async close(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        if (!context.isTopLevelContext()) {
-	            throw new protocol_js_1$f.InvalidArgumentException(`Non top-level browsing context ${context.id} cannot be closed.`);
-	        }
-	        try {
-	            const detachedFromTargetPromise = new Promise((resolve) => {
-	                const onContextDestroyed = (event) => {
-	                    if (event.targetId === params.context) {
-	                        this.#browserCdpClient.off('Target.detachedFromTarget', onContextDestroyed);
-	                        resolve();
-	                    }
-	                };
-	                this.#browserCdpClient.on('Target.detachedFromTarget', onContextDestroyed);
-	            });
-	            if (params.promptUnload) {
-	                await context.close();
-	            }
-	            else {
-	                await this.#browserCdpClient.sendCommand('Target.closeTarget', {
-	                    targetId: params.context,
-	                });
-	            }
-	            // Sometimes CDP command finishes before `detachedFromTarget` event,
-	            // sometimes after. Wait for the CDP command to be finished, and then wait
-	            // for `detachedFromTarget` if it hasn't emitted.
-	            await detachedFromTargetPromise;
-	        }
-	        catch (error) {
-	            // Swallow error that arise from the page being destroyed
-	            // Example is navigating to faulty SSL certificate
-	            if (!(error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
-	                error.message === 'Not attached to an active page')) {
-	                throw error;
-	            }
-	        }
-	        return {};
-	    }
-	    async locateNodes(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        return await context.locateNodes(params);
-	    }
-	    /**
-	     * This method is called for each CDP session, since this class is responsible
-	     * for creating and destroying all targets and browsing contexts.
-	     */
-	    #setEventListeners(cdpClient) {
-	        cdpClient.on('Target.attachedToTarget', (params) => {
-	            this.#handleAttachedToTargetEvent(params, cdpClient);
-	        });
-	        cdpClient.on('Target.detachedFromTarget', (params) => {
-	            this.#handleDetachedFromTargetEvent(params);
-	        });
-	        cdpClient.on('Target.targetInfoChanged', (params) => {
-	            this.#handleTargetInfoChangedEvent(params);
-	        });
-	        cdpClient.on('Inspector.targetCrashed', () => {
-	            this.#handleTargetCrashedEvent(cdpClient);
-	        });
-	        cdpClient.on('Page.frameAttached', (params) => {
-	            this.#handleFrameAttachedEvent(params);
-	        });
-	        cdpClient.on('Page.frameDetached', (params) => {
-	            this.#handleFrameDetachedEvent(params);
-	        });
-	    }
-	    #handleFrameAttachedEvent(params) {
-	        const parentBrowsingContext = this.#browsingContextStorage.findContext(params.parentFrameId);
-	        if (parentBrowsingContext !== undefined) {
-	            BrowsingContextImpl_js_1.BrowsingContextImpl.create(parentBrowsingContext.cdpTarget, this.#realmStorage, params.frameId, params.parentFrameId, parentBrowsingContext.userContext, this.#eventManager, this.#browsingContextStorage, this.#sharedIdWithFrame, this.#logger);
-	        }
-	    }
-	    #handleFrameDetachedEvent(params) {
-	        // In case of OOPiF no need in deleting BrowsingContext.
-	        if (params.reason === 'swap') {
-	            return;
-	        }
-	        this.#browsingContextStorage.findContext(params.frameId)?.dispose();
-	    }
-	    #handleAttachedToTargetEvent(params, parentSessionCdpClient) {
-	        const { sessionId, targetInfo } = params;
-	        const targetCdpClient = this.#cdpConnection.getCdpClient(sessionId);
-	        this.#logger?.(log_js_1$8.LogType.debugInfo, 'AttachedToTarget event received:', params);
-	        switch (targetInfo.type) {
-	            case 'page':
-	            case 'iframe': {
-	                if (targetInfo.targetId === this.#selfTargetId) {
-	                    break;
-	                }
-	                const cdpTarget = this.#createCdpTarget(targetCdpClient, targetInfo);
-	                const maybeContext = this.#browsingContextStorage.findContext(targetInfo.targetId);
-	                if (maybeContext) {
-	                    // OOPiF.
-	                    maybeContext.updateCdpTarget(cdpTarget);
-	                }
-	                else {
-	                    // New context.
-	                    BrowsingContextImpl_js_1.BrowsingContextImpl.create(cdpTarget, this.#realmStorage, targetInfo.targetId, null, targetInfo.browserContextId &&
-	                        targetInfo.browserContextId !== this.#defaultUserContextId
-	                        ? targetInfo.browserContextId
-	                        : 'default', this.#eventManager, this.#browsingContextStorage, this.#sharedIdWithFrame, this.#logger);
-	                }
-	                return;
-	            }
-	            case 'service_worker':
-	            case 'worker': {
-	                const realm = this.#realmStorage.findRealm({
-	                    cdpSessionId: parentSessionCdpClient.sessionId,
-	                });
-	                // If there is no browsing context, this worker is already terminated.
-	                if (!realm) {
-	                    break;
-	                }
-	                const cdpTarget = this.#createCdpTarget(targetCdpClient, targetInfo);
-	                this.#handleWorkerTarget(cdpToBidiTargetTypes[targetInfo.type], cdpTarget, realm);
-	                return;
-	            }
-	            // In CDP, we only emit shared workers on the browser and not the set of
-	            // frames that use the shared worker. If we change this in the future to
-	            // behave like service workers (emits on both browser and frame targets),
-	            // we can remove this block and merge service workers with the above one.
-	            case 'shared_worker': {
-	                const cdpTarget = this.#createCdpTarget(targetCdpClient, targetInfo);
-	                this.#handleWorkerTarget(cdpToBidiTargetTypes[targetInfo.type], cdpTarget);
-	                return;
-	            }
-	        }
-	        // DevTools or some other not supported by BiDi target. Just release
-	        // debugger and ignore them.
-	        targetCdpClient
-	            .sendCommand('Runtime.runIfWaitingForDebugger')
-	            .then(() => parentSessionCdpClient.sendCommand('Target.detachFromTarget', params))
-	            .catch((error) => this.#logger?.(log_js_1$8.LogType.debugError, error));
-	    }
-	    #createCdpTarget(targetCdpClient, targetInfo) {
-	        this.#setEventListeners(targetCdpClient);
-	        const target = CdpTarget_js_1.CdpTarget.create(targetInfo.targetId, targetCdpClient, this.#browserCdpClient, this.#realmStorage, this.#eventManager, this.#preloadScriptStorage, this.#networkStorage, this.#acceptInsecureCerts, this.#logger);
-	        this.#networkStorage.onCdpTargetCreated(target);
-	        return target;
-	    }
-	    #workers = new Map();
-	    #handleWorkerTarget(realmType, cdpTarget, ownerRealm) {
-	        cdpTarget.cdpClient.on('Runtime.executionContextCreated', (params) => {
-	            const { uniqueId, id, origin } = params.context;
-	            const workerRealm = new WorkerRealm_js_1.WorkerRealm(cdpTarget.cdpClient, this.#eventManager, id, this.#logger, (0, BrowsingContextImpl_js_1.serializeOrigin)(origin), ownerRealm ? [ownerRealm] : [], uniqueId, this.#realmStorage, realmType);
-	            this.#workers.set(cdpTarget.cdpSessionId, workerRealm);
-	        });
-	    }
-	    #handleDetachedFromTargetEvent(params) {
-	        const context = this.#browsingContextStorage.findContextBySession(params.sessionId);
-	        if (context) {
-	            context.dispose();
-	            this.#preloadScriptStorage
-	                .find({ targetId: context.id })
-	                .map((preloadScript) => preloadScript.dispose(context.id));
-	            return;
-	        }
-	        const worker = this.#workers.get(params.sessionId);
-	        if (worker) {
-	            this.#realmStorage.deleteRealms({
-	                cdpSessionId: worker.cdpClient.sessionId,
-	            });
-	        }
-	    }
-	    #handleTargetInfoChangedEvent(params) {
-	        const context = this.#browsingContextStorage.findContext(params.targetInfo.targetId);
-	        if (context) {
-	            context.onTargetInfoChanged(params);
-	        }
-	    }
-	    #handleTargetCrashedEvent(cdpClient) {
-	        // This is primarily used for service and shared workers. CDP tends to not
-	        // signal they closed gracefully and instead says they crashed to signal
-	        // they are closed.
-	        const realms = this.#realmStorage.findRealms({
-	            cdpSessionId: cdpClient.sessionId,
-	        });
-	        for (const realm of realms) {
-	            realm.dispose();
-	        }
-	    }
-	}
-	BrowsingContextProcessor$1.BrowsingContextProcessor = BrowsingContextProcessor;
-
-	var InputProcessor$1 = {};
-
-	var ActionDispatcher$1 = {};
-
-	var InputSource = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(InputSource, "__esModule", { value: true });
-	InputSource.WheelSource = InputSource.PointerSource = InputSource.KeySource = InputSource.NoneSource = void 0;
-	class NoneSource {
-	    type = "none" /* SourceType.None */;
-	}
-	InputSource.NoneSource = NoneSource;
-	class KeySource {
-	    type = "key" /* SourceType.Key */;
-	    pressed = new Set();
-	    // This is a bitfield that matches the modifiers parameter of
-	    // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchKeyEvent
-	    #modifiers = 0;
-	    get modifiers() {
-	        return this.#modifiers;
-	    }
-	    get alt() {
-	        return (this.#modifiers & 1) === 1;
-	    }
-	    set alt(value) {
-	        this.#setModifier(value, 1);
-	    }
-	    get ctrl() {
-	        return (this.#modifiers & 2) === 2;
-	    }
-	    set ctrl(value) {
-	        this.#setModifier(value, 2);
-	    }
-	    get meta() {
-	        return (this.#modifiers & 4) === 4;
-	    }
-	    set meta(value) {
-	        this.#setModifier(value, 4);
-	    }
-	    get shift() {
-	        return (this.#modifiers & 8) === 8;
-	    }
-	    set shift(value) {
-	        this.#setModifier(value, 8);
-	    }
-	    #setModifier(value, bit) {
-	        if (value) {
-	            this.#modifiers |= bit;
-	        }
-	        else {
-	            this.#modifiers &= ~bit;
-	        }
-	    }
-	}
-	InputSource.KeySource = KeySource;
-	class PointerSource {
-	    type = "pointer" /* SourceType.Pointer */;
-	    subtype;
-	    pointerId;
-	    pressed = new Set();
-	    x = 0;
-	    y = 0;
-	    constructor(id, subtype) {
-	        this.pointerId = id;
-	        this.subtype = subtype;
-	    }
-	    // This is a bitfield that matches the buttons parameter of
-	    // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchMouseEvent
-	    get buttons() {
-	        let buttons = 0;
-	        for (const button of this.pressed) {
-	            switch (button) {
-	                case 0:
-	                    buttons |= 1;
-	                    break;
-	                case 1:
-	                    buttons |= 4;
-	                    break;
-	                case 2:
-	                    buttons |= 2;
-	                    break;
-	                case 3:
-	                    buttons |= 8;
-	                    break;
-	                case 4:
-	                    buttons |= 16;
-	                    break;
-	            }
-	        }
-	        return buttons;
-	    }
-	    // --- Platform-specific code starts here ---
-	    // Input.dispatchMouseEvent doesn't know the concept of double click, so we
-	    // need to create the logic, similar to how it's done for OSes:
-	    // https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/events/event.cc;l=479
-	    static ClickContext = class ClickContext {
-	        static #DOUBLE_CLICK_TIME_MS = 500;
-	        static #MAX_DOUBLE_CLICK_RADIUS = 2;
-	        count = 0;
-	        #x;
-	        #y;
-	        #time;
-	        constructor(x, y, time) {
-	            this.#x = x;
-	            this.#y = y;
-	            this.#time = time;
-	        }
-	        compare(context) {
-	            return (
-	            // The click needs to be within a certain amount of ms.
-	            context.#time - this.#time > ClickContext.#DOUBLE_CLICK_TIME_MS ||
-	                // The click needs to be within a certain square radius.
-	                Math.abs(context.#x - this.#x) >
-	                    ClickContext.#MAX_DOUBLE_CLICK_RADIUS ||
-	                Math.abs(context.#y - this.#y) > ClickContext.#MAX_DOUBLE_CLICK_RADIUS);
-	        }
-	    };
-	    #clickContexts = new Map();
-	    setClickCount(button, context) {
-	        let storedContext = this.#clickContexts.get(button);
-	        if (!storedContext || storedContext.compare(context)) {
-	            storedContext = context;
-	        }
-	        ++storedContext.count;
-	        this.#clickContexts.set(button, storedContext);
-	        return storedContext.count;
-	    }
-	    getClickCount(button) {
-	        return this.#clickContexts.get(button)?.count ?? 0;
-	    }
-	}
-	InputSource.PointerSource = PointerSource;
-	class WheelSource {
-	    type = "wheel" /* SourceType.Wheel */;
-	}
-	InputSource.WheelSource = WheelSource;
-
-	var keyUtils = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(keyUtils, "__esModule", { value: true });
-	keyUtils.getKeyLocation = keyUtils.getKeyCode = keyUtils.getNormalizedKey = void 0;
-	function getNormalizedKey(value) {
-	    switch (value) {
-	        case '\uE000':
-	            return 'Unidentified';
-	        case '\uE001':
-	            return 'Cancel';
-	        case '\uE002':
-	            return 'Help';
-	        case '\uE003':
-	            return 'Backspace';
-	        case '\uE004':
-	            return 'Tab';
-	        case '\uE005':
-	            return 'Clear';
-	        case '\uE006':
-	            return 'Return';
-	        case '\uE007':
-	            return 'Enter';
-	        case '\uE008':
-	            return 'Shift';
-	        case '\uE009':
-	            return 'Control';
-	        case '\uE00A':
-	            return 'Alt';
-	        case '\uE00B':
-	            return 'Pause';
-	        case '\uE00C':
-	            return 'Escape';
-	        case '\uE00D':
-	            return ' ';
-	        case '\uE00E':
-	            return 'PageUp';
-	        case '\uE00F':
-	            return 'PageDown';
-	        case '\uE010':
-	            return 'End';
-	        case '\uE011':
-	            return 'Home';
-	        case '\uE012':
-	            return 'ArrowLeft';
-	        case '\uE013':
-	            return 'ArrowUp';
-	        case '\uE014':
-	            return 'ArrowRight';
-	        case '\uE015':
-	            return 'ArrowDown';
-	        case '\uE016':
-	            return 'Insert';
-	        case '\uE017':
-	            return 'Delete';
-	        case '\uE018':
-	            return ';';
-	        case '\uE019':
-	            return '=';
-	        case '\uE01A':
-	            return '0';
-	        case '\uE01B':
-	            return '1';
-	        case '\uE01C':
-	            return '2';
-	        case '\uE01D':
-	            return '3';
-	        case '\uE01E':
-	            return '4';
-	        case '\uE01F':
-	            return '5';
-	        case '\uE020':
-	            return '6';
-	        case '\uE021':
-	            return '7';
-	        case '\uE022':
-	            return '8';
-	        case '\uE023':
-	            return '9';
-	        case '\uE024':
-	            return '*';
-	        case '\uE025':
-	            return '+';
-	        case '\uE026':
-	            return ',';
-	        case '\uE027':
-	            return '-';
-	        case '\uE028':
-	            return '.';
-	        case '\uE029':
-	            return '/';
-	        case '\uE031':
-	            return 'F1';
-	        case '\uE032':
-	            return 'F2';
-	        case '\uE033':
-	            return 'F3';
-	        case '\uE034':
-	            return 'F4';
-	        case '\uE035':
-	            return 'F5';
-	        case '\uE036':
-	            return 'F6';
-	        case '\uE037':
-	            return 'F7';
-	        case '\uE038':
-	            return 'F8';
-	        case '\uE039':
-	            return 'F9';
-	        case '\uE03A':
-	            return 'F10';
-	        case '\uE03B':
-	            return 'F11';
-	        case '\uE03C':
-	            return 'F12';
-	        case '\uE03D':
-	            return 'Meta';
-	        case '\uE040':
-	            return 'ZenkakuHankaku';
-	        case '\uE050':
-	            return 'Shift';
-	        case '\uE051':
-	            return 'Control';
-	        case '\uE052':
-	            return 'Alt';
-	        case '\uE053':
-	            return 'Meta';
-	        case '\uE054':
-	            return 'PageUp';
-	        case '\uE055':
-	            return 'PageDown';
-	        case '\uE056':
-	            return 'End';
-	        case '\uE057':
-	            return 'Home';
-	        case '\uE058':
-	            return 'ArrowLeft';
-	        case '\uE059':
-	            return 'ArrowUp';
-	        case '\uE05A':
-	            return 'ArrowRight';
-	        case '\uE05B':
-	            return 'ArrowDown';
-	        case '\uE05C':
-	            return 'Insert';
-	        case '\uE05D':
-	            return 'Delete';
-	        default:
-	            return value;
-	    }
-	}
-	keyUtils.getNormalizedKey = getNormalizedKey;
-	function getKeyCode(key) {
-	    switch (key) {
-	        case '`':
-	        case '~':
-	            return 'Backquote';
-	        case '\\':
-	        case '|':
-	            return 'Backslash';
-	        case '\uE003':
-	            return 'Backspace';
-	        case '[':
-	        case '{':
-	            return 'BracketLeft';
-	        case ']':
-	        case '}':
-	            return 'BracketRight';
-	        case ',':
-	        case '<':
-	            return 'Comma';
-	        case '0':
-	        case ')':
-	            return 'Digit0';
-	        case '1':
-	        case '!':
-	            return 'Digit1';
-	        case '2':
-	        case '@':
-	            return 'Digit2';
-	        case '3':
-	        case '#':
-	            return 'Digit3';
-	        case '4':
-	        case '$':
-	            return 'Digit4';
-	        case '5':
-	        case '%':
-	            return 'Digit5';
-	        case '6':
-	        case '^':
-	            return 'Digit6';
-	        case '7':
-	        case '&':
-	            return 'Digit7';
-	        case '8':
-	        case '*':
-	            return 'Digit8';
-	        case '9':
-	        case '(':
-	            return 'Digit9';
-	        case '=':
-	        case '+':
-	            return 'Equal';
-	        case 'a':
-	        case 'A':
-	            return 'KeyA';
-	        case 'b':
-	        case 'B':
-	            return 'KeyB';
-	        case 'c':
-	        case 'C':
-	            return 'KeyC';
-	        case 'd':
-	        case 'D':
-	            return 'KeyD';
-	        case 'e':
-	        case 'E':
-	            return 'KeyE';
-	        case 'f':
-	        case 'F':
-	            return 'KeyF';
-	        case 'g':
-	        case 'G':
-	            return 'KeyG';
-	        case 'h':
-	        case 'H':
-	            return 'KeyH';
-	        case 'i':
-	        case 'I':
-	            return 'KeyI';
-	        case 'j':
-	        case 'J':
-	            return 'KeyJ';
-	        case 'k':
-	        case 'K':
-	            return 'KeyK';
-	        case 'l':
-	        case 'L':
-	            return 'KeyL';
-	        case 'm':
-	        case 'M':
-	            return 'KeyM';
-	        case 'n':
-	        case 'N':
-	            return 'KeyN';
-	        case 'o':
-	        case 'O':
-	            return 'KeyO';
-	        case 'p':
-	        case 'P':
-	            return 'KeyP';
-	        case 'q':
-	        case 'Q':
-	            return 'KeyQ';
-	        case 'r':
-	        case 'R':
-	            return 'KeyR';
-	        case 's':
-	        case 'S':
-	            return 'KeyS';
-	        case 't':
-	        case 'T':
-	            return 'KeyT';
-	        case 'u':
-	        case 'U':
-	            return 'KeyU';
-	        case 'v':
-	        case 'V':
-	            return 'KeyV';
-	        case 'w':
-	        case 'W':
-	            return 'KeyW';
-	        case 'x':
-	        case 'X':
-	            return 'KeyX';
-	        case 'y':
-	        case 'Y':
-	            return 'KeyY';
-	        case 'z':
-	        case 'Z':
-	            return 'KeyZ';
-	        case '-':
-	        case '_':
-	            return 'Minus';
-	        case '.':
-	            return 'Period';
-	        case "'":
-	        case '"':
-	            return 'Quote';
-	        case ';':
-	        case ':':
-	            return 'Semicolon';
-	        case '/':
-	        case '?':
-	            return 'Slash';
-	        case '\uE00A':
-	            return 'AltLeft';
-	        case '\uE052':
-	            return 'AltRight';
-	        case '\uE009':
-	            return 'ControlLeft';
-	        case '\uE051':
-	            return 'ControlRight';
-	        case '\uE006':
-	            return 'Enter';
-	        case '\uE03D':
-	            return 'MetaLeft';
-	        case '\uE053':
-	            return 'MetaRight';
-	        case '\uE008':
-	            return 'ShiftLeft';
-	        case '\uE050':
-	            return 'ShiftRight';
-	        case ' ':
-	        case '\uE00D':
-	            return 'Space';
-	        case '\uE004':
-	            return 'Tab';
-	        case '\uE017':
-	            return 'Delete';
-	        case '\uE010':
-	            return 'End';
-	        case '\uE002':
-	            return 'Help';
-	        case '\uE011':
-	            return 'Home';
-	        case '\uE016':
-	            return 'Insert';
-	        case '\uE00F':
-	            return 'PageDown';
-	        case '\uE00E':
-	            return 'PageUp';
-	        case '\uE015':
-	            return 'ArrowDown';
-	        case '\uE012':
-	            return 'ArrowLeft';
-	        case '\uE014':
-	            return 'ArrowRight';
-	        case '\uE013':
-	            return 'ArrowUp';
-	        case '\uE00C':
-	            return 'Escape';
-	        case '\uE031':
-	            return 'F1';
-	        case '\uE032':
-	            return 'F2';
-	        case '\uE033':
-	            return 'F3';
-	        case '\uE034':
-	            return 'F4';
-	        case '\uE035':
-	            return 'F5';
-	        case '\uE036':
-	            return 'F6';
-	        case '\uE037':
-	            return 'F7';
-	        case '\uE038':
-	            return 'F8';
-	        case '\uE039':
-	            return 'F9';
-	        case '\uE03A':
-	            return 'F10';
-	        case '\uE03B':
-	            return 'F11';
-	        case '\uE03C':
-	            return 'F12';
-	        case '\uE01A':
-	        case '\uE05C':
-	            return 'Numpad0';
-	        case '\uE01B':
-	        case '\uE056':
-	            return 'Numpad1';
-	        case '\uE01C':
-	        case '\uE05B':
-	            return 'Numpad2';
-	        case '\uE01D':
-	        case '\uE055':
-	            return 'Numpad3';
-	        case '\uE01E':
-	        case '\uE058':
-	            return 'Numpad4';
-	        case '\uE01F':
-	            return 'Numpad5';
-	        case '\uE020':
-	        case '\uE05A':
-	            return 'Numpad6';
-	        case '\uE021':
-	        case '\uE057':
-	            return 'Numpad7';
-	        case '\uE022':
-	        case '\uE059':
-	            return 'Numpad8';
-	        case '\uE023':
-	        case '\uE054':
-	            return 'Numpad9';
-	        case '\uE025':
-	            return 'NumpadAdd';
-	        case '\uE026':
-	            return 'NumpadComma';
-	        case '\uE028':
-	        case '\uE05D':
-	            return 'NumpadDecimal';
-	        case '\uE029':
-	            return 'NumpadDivide';
-	        case '\uE007':
-	            return 'NumpadEnter';
-	        case '\uE024':
-	            return 'NumpadMultiply';
-	        case '\uE027':
-	            return 'NumpadSubtract';
-	        default:
-	            return;
-	    }
-	}
-	keyUtils.getKeyCode = getKeyCode;
-	function getKeyLocation(key) {
-	    switch (key) {
-	        case '\uE007':
-	        case '\uE008':
-	        case '\uE009':
-	        case '\uE00A':
-	        case '\uE03D':
-	            return 1;
-	        case '\uE01A':
-	        case '\uE01B':
-	        case '\uE01C':
-	        case '\uE01D':
-	        case '\uE01E':
-	        case '\uE01F':
-	        case '\uE020':
-	        case '\uE021':
-	        case '\uE022':
-	        case '\uE023':
-	        case '\uE024':
-	        case '\uE025':
-	        case '\uE026':
-	        case '\uE027':
-	        case '\uE028':
-	        case '\uE029':
-	        case '\uE054':
-	        case '\uE055':
-	        case '\uE056':
-	        case '\uE057':
-	        case '\uE058':
-	        case '\uE059':
-	        case '\uE05A':
-	        case '\uE05B':
-	        case '\uE05C':
-	        case '\uE05D':
-	            return 3;
-	        case '\uE050':
-	        case '\uE051':
-	        case '\uE052':
-	        case '\uE053':
-	            return 2;
-	        default:
-	            return 0;
-	    }
-	}
-	keyUtils.getKeyLocation = getKeyLocation;
-
-	var USKeyboardLayout = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(USKeyboardLayout, "__esModule", { value: true });
-	USKeyboardLayout.KeyToKeyCode = void 0;
-	// TODO: Remove this once https://crrev.com/c/4548290 is stably in Chromium.
-	// `Input.dispatchKeyboardEvent` will automatically handle these conversions.
-	USKeyboardLayout.KeyToKeyCode = {
-	    '0': 48,
-	    '1': 49,
-	    '2': 50,
-	    '3': 51,
-	    '4': 52,
-	    '5': 53,
-	    '6': 54,
-	    '7': 55,
-	    '8': 56,
-	    '9': 57,
-	    Abort: 3,
-	    Help: 6,
-	    Backspace: 8,
-	    Tab: 9,
-	    Numpad5: 12,
-	    NumpadEnter: 13,
-	    Enter: 13,
-	    '\\r': 13,
-	    '\\n': 13,
-	    ShiftLeft: 16,
-	    ShiftRight: 16,
-	    ControlLeft: 17,
-	    ControlRight: 17,
-	    AltLeft: 18,
-	    AltRight: 18,
-	    Pause: 19,
-	    CapsLock: 20,
-	    Escape: 27,
-	    Convert: 28,
-	    NonConvert: 29,
-	    Space: 32,
-	    Numpad9: 33,
-	    PageUp: 33,
-	    Numpad3: 34,
-	    PageDown: 34,
-	    End: 35,
-	    Numpad1: 35,
-	    Home: 36,
-	    Numpad7: 36,
-	    ArrowLeft: 37,
-	    Numpad4: 37,
-	    Numpad8: 38,
-	    ArrowUp: 38,
-	    ArrowRight: 39,
-	    Numpad6: 39,
-	    Numpad2: 40,
-	    ArrowDown: 40,
-	    Select: 41,
-	    Open: 43,
-	    PrintScreen: 44,
-	    Insert: 45,
-	    Numpad0: 45,
-	    Delete: 46,
-	    NumpadDecimal: 46,
-	    Digit0: 48,
-	    Digit1: 49,
-	    Digit2: 50,
-	    Digit3: 51,
-	    Digit4: 52,
-	    Digit5: 53,
-	    Digit6: 54,
-	    Digit7: 55,
-	    Digit8: 56,
-	    Digit9: 57,
-	    KeyA: 65,
-	    KeyB: 66,
-	    KeyC: 67,
-	    KeyD: 68,
-	    KeyE: 69,
-	    KeyF: 70,
-	    KeyG: 71,
-	    KeyH: 72,
-	    KeyI: 73,
-	    KeyJ: 74,
-	    KeyK: 75,
-	    KeyL: 76,
-	    KeyM: 77,
-	    KeyN: 78,
-	    KeyO: 79,
-	    KeyP: 80,
-	    KeyQ: 81,
-	    KeyR: 82,
-	    KeyS: 83,
-	    KeyT: 84,
-	    KeyU: 85,
-	    KeyV: 86,
-	    KeyW: 87,
-	    KeyX: 88,
-	    KeyY: 89,
-	    KeyZ: 90,
-	    MetaLeft: 91,
-	    MetaRight: 92,
-	    ContextMenu: 93,
-	    NumpadMultiply: 106,
-	    NumpadAdd: 107,
-	    NumpadSubtract: 109,
-	    NumpadDivide: 111,
-	    F1: 112,
-	    F2: 113,
-	    F3: 114,
-	    F4: 115,
-	    F5: 116,
-	    F6: 117,
-	    F7: 118,
-	    F8: 119,
-	    F9: 120,
-	    F10: 121,
-	    F11: 122,
-	    F12: 123,
-	    F13: 124,
-	    F14: 125,
-	    F15: 126,
-	    F16: 127,
-	    F17: 128,
-	    F18: 129,
-	    F19: 130,
-	    F20: 131,
-	    F21: 132,
-	    F22: 133,
-	    F23: 134,
-	    F24: 135,
-	    NumLock: 144,
-	    ScrollLock: 145,
-	    AudioVolumeMute: 173,
-	    AudioVolumeDown: 174,
-	    AudioVolumeUp: 175,
-	    MediaTrackNext: 176,
-	    MediaTrackPrevious: 177,
-	    MediaStop: 178,
-	    MediaPlayPause: 179,
-	    Semicolon: 186,
-	    Equal: 187,
-	    NumpadEqual: 187,
-	    Comma: 188,
-	    Minus: 189,
-	    Period: 190,
-	    Slash: 191,
-	    Backquote: 192,
-	    BracketLeft: 219,
-	    Backslash: 220,
-	    BracketRight: 221,
-	    Quote: 222,
-	    AltGraph: 225,
-	    Props: 247,
-	    Cancel: 3,
-	    Clear: 12,
-	    Shift: 16,
-	    Control: 17,
-	    Alt: 18,
-	    Accept: 30,
-	    ModeChange: 31,
-	    ' ': 32,
-	    Print: 42,
-	    Execute: 43,
-	    '\\u0000': 46,
-	    a: 65,
-	    b: 66,
-	    c: 67,
-	    d: 68,
-	    e: 69,
-	    f: 70,
-	    g: 71,
-	    h: 72,
-	    i: 73,
-	    j: 74,
-	    k: 75,
-	    l: 76,
-	    m: 77,
-	    n: 78,
-	    o: 79,
-	    p: 80,
-	    q: 81,
-	    r: 82,
-	    s: 83,
-	    t: 84,
-	    u: 85,
-	    v: 86,
-	    w: 87,
-	    x: 88,
-	    y: 89,
-	    z: 90,
-	    Meta: 91,
-	    '*': 106,
-	    '+': 107,
-	    '-': 109,
-	    '/': 111,
-	    ';': 186,
-	    '=': 187,
-	    ',': 188,
-	    '.': 190,
-	    '`': 192,
-	    '[': 219,
-	    '\\\\': 220,
-	    ']': 221,
-	    "'": 222,
-	    Attn: 246,
-	    CrSel: 247,
-	    ExSel: 248,
-	    EraseEof: 249,
-	    Play: 250,
-	    ZoomOut: 251,
-	    ')': 48,
-	    '!': 49,
-	    '@': 50,
-	    '#': 51,
-	    $: 52,
-	    '%': 53,
-	    '^': 54,
-	    '&': 55,
-	    '(': 57,
-	    A: 65,
-	    B: 66,
-	    C: 67,
-	    D: 68,
-	    E: 69,
-	    F: 70,
-	    G: 71,
-	    H: 72,
-	    I: 73,
-	    J: 74,
-	    K: 75,
-	    L: 76,
-	    M: 77,
-	    N: 78,
-	    O: 79,
-	    P: 80,
-	    Q: 81,
-	    R: 82,
-	    S: 83,
-	    T: 84,
-	    U: 85,
-	    V: 86,
-	    W: 87,
-	    X: 88,
-	    Y: 89,
-	    Z: 90,
-	    ':': 186,
-	    '<': 188,
-	    _: 189,
-	    '>': 190,
-	    '?': 191,
-	    '~': 192,
-	    '{': 219,
-	    '|': 220,
-	    '}': 221,
-	    '"': 222,
-	    Camera: 44,
-	    EndCall: 95,
-	    VolumeDown: 182,
-	    VolumeUp: 183,
-	};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(ActionDispatcher$1, "__esModule", { value: true });
-	ActionDispatcher$1.ActionDispatcher = void 0;
-	const protocol_js_1$e = protocol;
-	const assert_js_1$5 = assert$1;
-	const InputSource_js_1$1 = InputSource;
-	const keyUtils_js_1 = keyUtils;
-	const USKeyboardLayout_js_1 = USKeyboardLayout;
-	/** https://w3c.github.io/webdriver/#dfn-center-point */
-	const CALCULATE_IN_VIEW_CENTER_PT_DECL = ((i) => {
-	    const t = i.getClientRects()[0], e = Math.max(0, Math.min(t.x, t.x + t.width)), n = Math.min(window.innerWidth, Math.max(t.x, t.x + t.width)), h = Math.max(0, Math.min(t.y, t.y + t.height)), m = Math.min(window.innerHeight, Math.max(t.y, t.y + t.height));
-	    return [e + ((n - e) >> 1), h + ((m - h) >> 1)];
-	}).toString();
-	const IS_MAC_DECL = (() => {
-	    return navigator.platform.toLowerCase().includes('mac');
-	}).toString();
-	async function getElementCenter(context, element) {
-	    const sandbox = await context.getOrCreateSandbox(undefined);
-	    const result = await sandbox.callFunction(CALCULATE_IN_VIEW_CENTER_PT_DECL, { type: 'undefined' }, [element], false, "none" /* Script.ResultOwnership.None */, {});
-	    if (result.type === 'exception') {
-	        throw new protocol_js_1$e.NoSuchElementException(`Origin element ${element.sharedId} was not found`);
-	    }
-	    (0, assert_js_1$5.assert)(result.result.type === 'array');
-	    (0, assert_js_1$5.assert)(result.result.value?.[0]?.type === 'number');
-	    (0, assert_js_1$5.assert)(result.result.value?.[1]?.type === 'number');
-	    const { result: { value: [{ value: x }, { value: y }], }, } = result;
-	    return { x: x, y: y };
-	}
-	class ActionDispatcher {
-	    static isMacOS = async (context) => {
-	        const result = await (await context.getOrCreateSandbox(undefined)).callFunction(IS_MAC_DECL, { type: 'undefined' }, [], false, "none" /* Script.ResultOwnership.None */, {});
-	        (0, assert_js_1$5.assert)(result.type !== 'exception');
-	        (0, assert_js_1$5.assert)(result.result.type === 'boolean');
-	        return result.result.value;
-	    };
-	    #tickStart = 0;
-	    #tickDuration = 0;
-	    #inputState;
-	    #context;
-	    #isMacOS;
-	    constructor(inputState, context, isMacOS) {
-	        this.#inputState = inputState;
-	        this.#context = context;
-	        this.#isMacOS = isMacOS;
-	    }
-	    async dispatchActions(optionsByTick) {
-	        await this.#inputState.queue.run(async () => {
-	            for (const options of optionsByTick) {
-	                await this.dispatchTickActions(options);
-	            }
-	        });
-	    }
-	    async dispatchTickActions(options) {
-	        this.#tickStart = performance.now();
-	        this.#tickDuration = 0;
-	        for (const { action } of options) {
-	            if ('duration' in action && action.duration !== undefined) {
-	                this.#tickDuration = Math.max(this.#tickDuration, action.duration);
-	            }
-	        }
-	        const promises = [
-	            new Promise((resolve) => setTimeout(resolve, this.#tickDuration)),
-	        ];
-	        for (const option of options) {
-	            // In theory we have to wait for each action to happen, but CDP is serial,
-	            // so as an optimization, we queue all CDP commands at once and await all
-	            // of them.
-	            promises.push(this.#dispatchAction(option));
-	        }
-	        await Promise.all(promises);
-	    }
-	    async #dispatchAction({ id, action }) {
-	        const source = this.#inputState.get(id);
-	        const keyState = this.#inputState.getGlobalKeyState();
-	        switch (action.type) {
-	            case 'keyDown': {
-	                // SAFETY: The source is validated before.
-	                await this.#dispatchKeyDownAction(source, action);
-	                this.#inputState.cancelList.push({
-	                    id,
-	                    action: {
-	                        ...action,
-	                        type: 'keyUp',
-	                    },
-	                });
-	                break;
-	            }
-	            case 'keyUp': {
-	                // SAFETY: The source is validated before.
-	                await this.#dispatchKeyUpAction(source, action);
-	                break;
-	            }
-	            case 'pause': {
-	                // TODO: Implement waiting on the input source.
-	                break;
-	            }
-	            case 'pointerDown': {
-	                // SAFETY: The source is validated before.
-	                await this.#dispatchPointerDownAction(source, keyState, action);
-	                this.#inputState.cancelList.push({
-	                    id,
-	                    action: {
-	                        ...action,
-	                        type: 'pointerUp',
-	                    },
-	                });
-	                break;
-	            }
-	            case 'pointerMove': {
-	                // SAFETY: The source is validated before.
-	                await this.#dispatchPointerMoveAction(source, keyState, action);
-	                break;
-	            }
-	            case 'pointerUp': {
-	                // SAFETY: The source is validated before.
-	                await this.#dispatchPointerUpAction(source, keyState, action);
-	                break;
-	            }
-	            case 'scroll': {
-	                // SAFETY: The source is validated before.
-	                await this.#dispatchScrollAction(source, keyState, action);
-	                break;
-	            }
-	        }
-	    }
-	    #dispatchPointerDownAction(source, keyState, action) {
-	        const { button } = action;
-	        if (source.pressed.has(button)) {
-	            return;
-	        }
-	        source.pressed.add(button);
-	        const { x, y, subtype: pointerType } = source;
-	        const { width, height, pressure, twist, tangentialPressure } = action;
-	        const { tiltX, tiltY } = getTilt(action);
-	        // --- Platform-specific code begins here ---
-	        const { modifiers } = keyState;
-	        switch (pointerType) {
-	            case "mouse" /* Input.PointerType.Mouse */:
-	            case "pen" /* Input.PointerType.Pen */:
-	                // TODO: Implement width and height when available.
-	                return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
-	                    type: 'mousePressed',
-	                    x,
-	                    y,
-	                    modifiers,
-	                    button: getCdpButton(button),
-	                    buttons: source.buttons,
-	                    clickCount: source.setClickCount(button, new InputSource_js_1$1.PointerSource.ClickContext(x, y, performance.now())),
-	                    pointerType,
-	                    tangentialPressure,
-	                    tiltX,
-	                    tiltY,
-	                    twist,
-	                    force: pressure,
-	                });
-	            case "touch" /* Input.PointerType.Touch */:
-	                return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchTouchEvent', {
-	                    type: 'touchStart',
-	                    touchPoints: [
-	                        {
-	                            x,
-	                            y,
-	                            ...getRadii(width ?? 1, height ?? 1),
-	                            tangentialPressure,
-	                            tiltX,
-	                            tiltY,
-	                            twist,
-	                            force: pressure,
-	                            id: source.pointerId,
-	                        },
-	                    ],
-	                    modifiers,
-	                });
-	        }
-	        // --- Platform-specific code ends here ---
-	    }
-	    #dispatchPointerUpAction(source, keyState, action) {
-	        const { button } = action;
-	        if (!source.pressed.has(button)) {
-	            return;
-	        }
-	        source.pressed.delete(button);
-	        const { x, y, subtype: pointerType } = source;
-	        // --- Platform-specific code begins here ---
-	        const { modifiers } = keyState;
-	        switch (pointerType) {
-	            case "mouse" /* Input.PointerType.Mouse */:
-	            case "pen" /* Input.PointerType.Pen */:
-	                // TODO: Implement width and height when available.
-	                return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
-	                    type: 'mouseReleased',
-	                    x,
-	                    y,
-	                    modifiers,
-	                    button: getCdpButton(button),
-	                    buttons: source.buttons,
-	                    clickCount: source.getClickCount(button),
-	                    pointerType,
-	                });
-	            case "touch" /* Input.PointerType.Touch */:
-	                return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchTouchEvent', {
-	                    type: 'touchEnd',
-	                    touchPoints: [
-	                        {
-	                            x,
-	                            y,
-	                            id: source.pointerId,
-	                        },
-	                    ],
-	                    modifiers,
-	                });
-	        }
-	        // --- Platform-specific code ends here ---
-	    }
-	    async #dispatchPointerMoveAction(source, keyState, action) {
-	        const { x: startX, y: startY, subtype: pointerType } = source;
-	        const { width, height, pressure, twist, tangentialPressure, x: offsetX, y: offsetY, origin = 'viewport', duration = this.#tickDuration, } = action;
-	        const { tiltX, tiltY } = getTilt(action);
-	        const { targetX, targetY } = await this.#getCoordinateFromOrigin(origin, offsetX, offsetY, startX, startY);
-	        if (targetX < 0 || targetY < 0) {
-	            throw new protocol_js_1$e.MoveTargetOutOfBoundsException(`Cannot move beyond viewport (x: ${targetX}, y: ${targetY})`);
-	        }
-	        let last;
-	        do {
-	            const ratio = duration > 0 ? (performance.now() - this.#tickStart) / duration : 1;
-	            last = ratio >= 1;
-	            let x;
-	            let y;
-	            if (last) {
-	                x = targetX;
-	                y = targetY;
-	            }
-	            else {
-	                x = Math.round(ratio * (targetX - startX) + startX);
-	                y = Math.round(ratio * (targetY - startY) + startY);
-	            }
-	            if (source.x !== x || source.y !== y) {
-	                // --- Platform-specific code begins here ---
-	                const { modifiers } = keyState;
-	                switch (pointerType) {
-	                    case "mouse" /* Input.PointerType.Mouse */:
-	                        // TODO: Implement width and height when available.
-	                        await this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
-	                            type: 'mouseMoved',
-	                            x,
-	                            y,
-	                            modifiers,
-	                            clickCount: 0,
-	                            button: getCdpButton(source.pressed.values().next().value ?? 5),
-	                            buttons: source.buttons,
-	                            pointerType,
-	                            tangentialPressure,
-	                            tiltX,
-	                            tiltY,
-	                            twist,
-	                            force: pressure,
-	                        });
-	                        break;
-	                    case "pen" /* Input.PointerType.Pen */:
-	                        if (source.pressed.size !== 0) {
-	                            // TODO: Implement width and height when available.
-	                            await this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
-	                                type: 'mouseMoved',
-	                                x,
-	                                y,
-	                                modifiers,
-	                                clickCount: 0,
-	                                button: getCdpButton(source.pressed.values().next().value ?? 5),
-	                                buttons: source.buttons,
-	                                pointerType,
-	                                tangentialPressure,
-	                                tiltX,
-	                                tiltY,
-	                                twist,
-	                                force: pressure,
-	                            });
-	                        }
-	                        break;
-	                    case "touch" /* Input.PointerType.Touch */:
-	                        if (source.pressed.size !== 0) {
-	                            await this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchTouchEvent', {
-	                                type: 'touchMove',
-	                                touchPoints: [
-	                                    {
-	                                        x,
-	                                        y,
-	                                        ...getRadii(width ?? 1, height ?? 1),
-	                                        tangentialPressure,
-	                                        tiltX,
-	                                        tiltY,
-	                                        twist,
-	                                        force: pressure,
-	                                        id: source.pointerId,
-	                                    },
-	                                ],
-	                                modifiers,
-	                            });
-	                        }
-	                        break;
-	                }
-	                // --- Platform-specific code ends here ---
-	                source.x = x;
-	                source.y = y;
-	            }
-	        } while (!last);
-	    }
-	    async #getCoordinateFromOrigin(origin, offsetX, offsetY, startX, startY) {
-	        let targetX;
-	        let targetY;
-	        switch (origin) {
-	            case 'viewport':
-	                targetX = offsetX;
-	                targetY = offsetY;
-	                break;
-	            case 'pointer':
-	                targetX = startX + offsetX;
-	                targetY = startY + offsetY;
-	                break;
-	            default: {
-	                const { x: posX, y: posY } = await getElementCenter(this.#context, origin.element);
-	                // SAFETY: These can never be special numbers.
-	                targetX = posX + offsetX;
-	                targetY = posY + offsetY;
-	                break;
-	            }
-	        }
-	        return { targetX, targetY };
-	    }
-	    async #dispatchScrollAction(_source, keyState, action) {
-	        const { deltaX: targetDeltaX, deltaY: targetDeltaY, x: offsetX, y: offsetY, origin = 'viewport', duration = this.#tickDuration, } = action;
-	        if (origin === 'pointer') {
-	            throw new protocol_js_1$e.InvalidArgumentException('"pointer" origin is invalid for scrolling.');
-	        }
-	        const { targetX, targetY } = await this.#getCoordinateFromOrigin(origin, offsetX, offsetY, 0, 0);
-	        if (targetX < 0 || targetY < 0) {
-	            throw new protocol_js_1$e.MoveTargetOutOfBoundsException(`Cannot move beyond viewport (x: ${targetX}, y: ${targetY})`);
-	        }
-	        let currentDeltaX = 0;
-	        let currentDeltaY = 0;
-	        let last;
-	        do {
-	            const ratio = duration > 0 ? (performance.now() - this.#tickStart) / duration : 1;
-	            last = ratio >= 1;
-	            let deltaX;
-	            let deltaY;
-	            if (last) {
-	                deltaX = targetDeltaX - currentDeltaX;
-	                deltaY = targetDeltaY - currentDeltaY;
-	            }
-	            else {
-	                deltaX = Math.round(ratio * targetDeltaX - currentDeltaX);
-	                deltaY = Math.round(ratio * targetDeltaY - currentDeltaY);
-	            }
-	            if (deltaX !== 0 || deltaY !== 0) {
-	                // --- Platform-specific code begins here ---
-	                const { modifiers } = keyState;
-	                await this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
-	                    type: 'mouseWheel',
-	                    deltaX,
-	                    deltaY,
-	                    x: targetX,
-	                    y: targetY,
-	                    modifiers,
-	                });
-	                // --- Platform-specific code ends here ---
-	                currentDeltaX += deltaX;
-	                currentDeltaY += deltaY;
-	            }
-	        } while (!last);
-	    }
-	    async #dispatchKeyDownAction(source, action) {
-	        if ([...action.value].length > 1) {
-	            throw new protocol_js_1$e.InvalidArgumentException(`Invalid key value: ${action.value}`);
-	        }
-	        const rawKey = action.value;
-	        const key = (0, keyUtils_js_1.getNormalizedKey)(rawKey);
-	        const repeat = source.pressed.has(key);
-	        const code = (0, keyUtils_js_1.getKeyCode)(rawKey);
-	        const location = (0, keyUtils_js_1.getKeyLocation)(rawKey);
-	        switch (key) {
-	            case 'Alt':
-	                source.alt = true;
-	                break;
-	            case 'Shift':
-	                source.shift = true;
-	                break;
-	            case 'Control':
-	                source.ctrl = true;
-	                break;
-	            case 'Meta':
-	                source.meta = true;
-	                break;
-	        }
-	        source.pressed.add(key);
-	        const { modifiers } = source;
-	        // --- Platform-specific code begins here ---
-	        // The spread is a little hack so JS gives us an array of unicode characters
-	        // to measure.
-	        const unmodifiedText = getKeyEventUnmodifiedText(key, source);
-	        const text = getKeyEventText(code ?? '', source) ?? unmodifiedText;
-	        let command;
-	        // The following commands need to be declared because Chromium doesn't
-	        // handle them. See
-	        // https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/blink/renderer/core/editing/editing_behavior.cc;l=169;drc=b8143cf1dfd24842890fcd831c4f5d909bef4fc4;bpv=0;bpt=1.
-	        if (this.#isMacOS && source.meta) {
-	            switch (code) {
-	                case 'KeyA':
-	                    command = 'SelectAll';
-	                    break;
-	                case 'KeyC':
-	                    command = 'Copy';
-	                    break;
-	                case 'KeyV':
-	                    command = source.shift ? 'PasteAndMatchStyle' : 'Paste';
-	                    break;
-	                case 'KeyX':
-	                    command = 'Cut';
-	                    break;
-	                case 'KeyZ':
-	                    command = source.shift ? 'Redo' : 'Undo';
-	                    break;
-	                // Intentionally empty.
-	            }
-	        }
-	        const promises = [
-	            this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchKeyEvent', {
-	                type: text ? 'keyDown' : 'rawKeyDown',
-	                windowsVirtualKeyCode: USKeyboardLayout_js_1.KeyToKeyCode[key],
-	                key,
-	                code,
-	                text,
-	                unmodifiedText,
-	                autoRepeat: repeat,
-	                isSystemKey: source.alt || undefined,
-	                location: location < 3 ? location : undefined,
-	                isKeypad: location === 3,
-	                modifiers,
-	                commands: command ? [command] : undefined,
-	            }),
-	        ];
-	        // Drag cancelling happens on escape.
-	        if (key === 'Escape') {
-	            if (!source.alt &&
-	                ((this.#isMacOS && !source.ctrl && !source.meta) || !this.#isMacOS)) {
-	                promises.push(this.#context.cdpTarget.cdpClient.sendCommand('Input.cancelDragging'));
-	            }
-	        }
-	        await Promise.all(promises);
-	        // --- Platform-specific code ends here ---
-	    }
-	    #dispatchKeyUpAction(source, action) {
-	        if ([...action.value].length > 1) {
-	            throw new protocol_js_1$e.InvalidArgumentException(`Invalid key value: ${action.value}`);
-	        }
-	        const rawKey = action.value;
-	        const key = (0, keyUtils_js_1.getNormalizedKey)(rawKey);
-	        if (!source.pressed.has(key)) {
-	            return;
-	        }
-	        const code = (0, keyUtils_js_1.getKeyCode)(rawKey);
-	        const location = (0, keyUtils_js_1.getKeyLocation)(rawKey);
-	        switch (key) {
-	            case 'Alt':
-	                source.alt = false;
-	                break;
-	            case 'Shift':
-	                source.shift = false;
-	                break;
-	            case 'Control':
-	                source.ctrl = false;
-	                break;
-	            case 'Meta':
-	                source.meta = false;
-	                break;
-	        }
-	        source.pressed.delete(key);
-	        const { modifiers } = source;
-	        // --- Platform-specific code begins here ---
-	        // The spread is a little hack so JS gives us an array of unicode characters
-	        // to measure.
-	        const unmodifiedText = getKeyEventUnmodifiedText(key, source);
-	        const text = getKeyEventText(code ?? '', source) ?? unmodifiedText;
-	        return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchKeyEvent', {
-	            type: 'keyUp',
-	            windowsVirtualKeyCode: USKeyboardLayout_js_1.KeyToKeyCode[key],
-	            key,
-	            code,
-	            text,
-	            unmodifiedText,
-	            location: location < 3 ? location : undefined,
-	            isSystemKey: source.alt || undefined,
-	            isKeypad: location === 3,
-	            modifiers,
-	        });
-	        // --- Platform-specific code ends here ---
-	    }
-	}
-	ActionDispatcher$1.ActionDispatcher = ActionDispatcher;
-	const getKeyEventUnmodifiedText = (key, source) => {
-	    if (key === 'Enter') {
-	        return '\r';
-	    }
-	    return [...key].length === 1
-	        ? source.shift
-	            ? key.toLocaleUpperCase('en-US')
-	            : key
-	        : undefined;
-	};
-	const getKeyEventText = (code, source) => {
-	    if (source.ctrl) {
-	        switch (code) {
-	            case 'Digit2':
-	                if (source.shift) {
-	                    return '\x00';
-	                }
-	                break;
-	            case 'KeyA':
-	                return '\x01';
-	            case 'KeyB':
-	                return '\x02';
-	            case 'KeyC':
-	                return '\x03';
-	            case 'KeyD':
-	                return '\x04';
-	            case 'KeyE':
-	                return '\x05';
-	            case 'KeyF':
-	                return '\x06';
-	            case 'KeyG':
-	                return '\x07';
-	            case 'KeyH':
-	                return '\x08';
-	            case 'KeyI':
-	                return '\x09';
-	            case 'KeyJ':
-	                return '\x0A';
-	            case 'KeyK':
-	                return '\x0B';
-	            case 'KeyL':
-	                return '\x0C';
-	            case 'KeyM':
-	                return '\x0D';
-	            case 'KeyN':
-	                return '\x0E';
-	            case 'KeyO':
-	                return '\x0F';
-	            case 'KeyP':
-	                return '\x10';
-	            case 'KeyQ':
-	                return '\x11';
-	            case 'KeyR':
-	                return '\x12';
-	            case 'KeyS':
-	                return '\x13';
-	            case 'KeyT':
-	                return '\x14';
-	            case 'KeyU':
-	                return '\x15';
-	            case 'KeyV':
-	                return '\x16';
-	            case 'KeyW':
-	                return '\x17';
-	            case 'KeyX':
-	                return '\x18';
-	            case 'KeyY':
-	                return '\x19';
-	            case 'KeyZ':
-	                return '\x1A';
-	            case 'BracketLeft':
-	                return '\x1B';
-	            case 'Backslash':
-	                return '\x1C';
-	            case 'BracketRight':
-	                return '\x1D';
-	            case 'Digit6':
-	                if (source.shift) {
-	                    return '\x1E';
-	                }
-	                break;
-	            case 'Minus':
-	                return '\x1F';
-	        }
-	        return '';
-	    }
-	    if (source.alt) {
-	        return '';
-	    }
-	    return;
-	};
-	function getCdpButton(button) {
-	    switch (button) {
-	        case 0:
-	            return 'left';
-	        case 1:
-	            return 'middle';
-	        case 2:
-	            return 'right';
-	        case 3:
-	            return 'back';
-	        case 4:
-	            return 'forward';
-	        default:
-	            return 'none';
-	    }
-	}
-	function getTilt(action) {
-	    // https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
-	    const altitudeAngle = action.altitudeAngle ?? 0;
-	    const azimuthAngle = action.azimuthAngle ?? 0;
-	    let tiltXRadians = 0;
-	    let tiltYRadians = 0;
-	    if (altitudeAngle === 0) {
-	        // the pen is in the X-Y plane
-	        if (azimuthAngle === 0 || azimuthAngle === 2 * Math.PI) {
-	            // pen is on positive X axis
-	            tiltXRadians = Math.PI / 2;
-	        }
-	        if (azimuthAngle === Math.PI / 2) {
-	            // pen is on positive Y axis
-	            tiltYRadians = Math.PI / 2;
-	        }
-	        if (azimuthAngle === Math.PI) {
-	            // pen is on negative X axis
-	            tiltXRadians = -Math.PI / 2;
-	        }
-	        if (azimuthAngle === (3 * Math.PI) / 2) {
-	            // pen is on negative Y axis
-	            tiltYRadians = -Math.PI / 2;
-	        }
-	        if (azimuthAngle > 0 && azimuthAngle < Math.PI / 2) {
-	            tiltXRadians = Math.PI / 2;
-	            tiltYRadians = Math.PI / 2;
-	        }
-	        if (azimuthAngle > Math.PI / 2 && azimuthAngle < Math.PI) {
-	            tiltXRadians = -Math.PI / 2;
-	            tiltYRadians = Math.PI / 2;
-	        }
-	        if (azimuthAngle > Math.PI && azimuthAngle < (3 * Math.PI) / 2) {
-	            tiltXRadians = -Math.PI / 2;
-	            tiltYRadians = -Math.PI / 2;
-	        }
-	        if (azimuthAngle > (3 * Math.PI) / 2 && azimuthAngle < 2 * Math.PI) {
-	            tiltXRadians = Math.PI / 2;
-	            tiltYRadians = -Math.PI / 2;
-	        }
-	    }
-	    if (altitudeAngle !== 0) {
-	        const tanAlt = Math.tan(altitudeAngle);
-	        tiltXRadians = Math.atan(Math.cos(azimuthAngle) / tanAlt);
-	        tiltYRadians = Math.atan(Math.sin(azimuthAngle) / tanAlt);
-	    }
-	    const factor = 180 / Math.PI;
-	    return {
-	        tiltX: Math.round(tiltXRadians * factor),
-	        tiltY: Math.round(tiltYRadians * factor),
-	    };
-	}
-	function getRadii(width, height) {
-	    return {
-	        radiusX: width ? width / 2 : 0.5,
-	        radiusY: height ? height / 2 : 0.5,
-	    };
-	}
-
-	var InputStateManager$1 = {};
-
-	var InputState$1 = {};
-
-	var Mutex$1 = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 * Copyright 2022 The Chromium 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(Mutex$1, "__esModule", { value: true });
-	Mutex$1.Mutex = void 0;
-	/**
-	 * Use Mutex class to coordinate local concurrent operations.
-	 * Once `acquire` promise resolves, you hold the lock and must
-	 * call `release` function returned by `acquire` to release the
-	 * lock. Failing to `release` the lock may lead to deadlocks.
-	 */
-	class Mutex {
-	    #locked = false;
-	    #acquirers = [];
-	    // This is FIFO.
-	    acquire() {
-	        const state = { resolved: false };
-	        if (this.#locked) {
-	            return new Promise((resolve) => {
-	                this.#acquirers.push(() => resolve(this.#release.bind(this, state)));
-	            });
-	        }
-	        this.#locked = true;
-	        return Promise.resolve(this.#release.bind(this, state));
-	    }
-	    #release(state) {
-	        if (state.resolved) {
-	            throw new Error('Cannot release more than once.');
-	        }
-	        state.resolved = true;
-	        const resolve = this.#acquirers.shift();
-	        if (!resolve) {
-	            this.#locked = false;
-	            return;
-	        }
-	        resolve();
-	    }
-	    async run(action) {
-	        const release = await this.acquire();
-	        try {
-	            // Note we need to await here because we want the await to release AFTER
-	            // that await happens. Returning action() will trigger the release
-	            // immediately which is counter to what we want.
-	            const result = await action();
-	            return result;
-	        }
-	        finally {
-	            release();
-	        }
-	    }
-	}
-	Mutex$1.Mutex = Mutex;
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(InputState$1, "__esModule", { value: true });
-	InputState$1.InputState = void 0;
-	const protocol_js_1$d = protocol;
-	const Mutex_js_1 = Mutex$1;
-	const InputSource_js_1 = InputSource;
-	class InputState {
-	    cancelList = [];
-	    #sources = new Map();
-	    #mutex = new Mutex_js_1.Mutex();
-	    getOrCreate(id, type, subtype) {
-	        let source = this.#sources.get(id);
-	        if (!source) {
-	            switch (type) {
-	                case "none" /* SourceType.None */:
-	                    source = new InputSource_js_1.NoneSource();
-	                    break;
-	                case "key" /* SourceType.Key */:
-	                    source = new InputSource_js_1.KeySource();
-	                    break;
-	                case "pointer" /* SourceType.Pointer */: {
-	                    let pointerId = subtype === "mouse" /* Input.PointerType.Mouse */ ? 0 : 2;
-	                    const pointerIds = new Set();
-	                    for (const [, source] of this.#sources) {
-	                        if (source.type === "pointer" /* SourceType.Pointer */) {
-	                            pointerIds.add(source.pointerId);
-	                        }
-	                    }
-	                    while (pointerIds.has(pointerId)) {
-	                        ++pointerId;
-	                    }
-	                    source = new InputSource_js_1.PointerSource(pointerId, subtype);
-	                    break;
-	                }
-	                case "wheel" /* SourceType.Wheel */:
-	                    source = new InputSource_js_1.WheelSource();
-	                    break;
-	                default:
-	                    throw new protocol_js_1$d.InvalidArgumentException(`Expected "${"none" /* SourceType.None */}", "${"key" /* SourceType.Key */}", "${"pointer" /* SourceType.Pointer */}", or "${"wheel" /* SourceType.Wheel */}". Found unknown source type ${type}.`);
-	            }
-	            this.#sources.set(id, source);
-	            return source;
-	        }
-	        if (source.type !== type) {
-	            throw new protocol_js_1$d.InvalidArgumentException(`Input source type of ${id} is ${source.type}, but received ${type}.`);
-	        }
-	        return source;
-	    }
-	    get(id) {
-	        const source = this.#sources.get(id);
-	        if (!source) {
-	            throw new protocol_js_1$d.UnknownErrorException(`Internal error.`);
-	        }
-	        return source;
-	    }
-	    getGlobalKeyState() {
-	        const state = new InputSource_js_1.KeySource();
-	        for (const [, source] of this.#sources) {
-	            if (source.type !== "key" /* SourceType.Key */) {
-	                continue;
-	            }
-	            for (const pressed of source.pressed) {
-	                state.pressed.add(pressed);
-	            }
-	            state.alt ||= source.alt;
-	            state.ctrl ||= source.ctrl;
-	            state.meta ||= source.meta;
-	            state.shift ||= source.shift;
-	        }
-	        return state;
-	    }
-	    get queue() {
-	        return this.#mutex;
-	    }
-	}
-	InputState$1.InputState = InputState;
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(InputStateManager$1, "__esModule", { value: true });
-	InputStateManager$1.InputStateManager = void 0;
-	const assert_js_1$4 = assert$1;
-	const InputState_js_1 = InputState$1;
-	// We use a weak map here as specified here:
-	// https://www.w3.org/TR/webdriver/#dfn-browsing-context-input-state-map
-	class InputStateManager extends WeakMap {
-	    get(context) {
-	        (0, assert_js_1$4.assert)(context.isTopLevelContext());
-	        if (!this.has(context)) {
-	            this.set(context, new InputState_js_1.InputState());
-	        }
-	        return super.get(context);
-	    }
-	}
-	InputStateManager$1.InputStateManager = InputStateManager;
-
-	Object.defineProperty(InputProcessor$1, "__esModule", { value: true });
-	InputProcessor$1.InputProcessor = void 0;
-	/*
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	const protocol_js_1$c = protocol;
-	const assert_js_1$3 = assert$1;
-	const ActionDispatcher_js_1 = ActionDispatcher$1;
-	const InputStateManager_js_1 = InputStateManager$1;
-	class InputProcessor {
-	    #browsingContextStorage;
-	    #realmStorage;
-	    #inputStateManager = new InputStateManager_js_1.InputStateManager();
-	    constructor(browsingContextStorage, realmStorage) {
-	        this.#browsingContextStorage = browsingContextStorage;
-	        this.#realmStorage = realmStorage;
-	    }
-	    async performActions(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        const inputState = this.#inputStateManager.get(context.top);
-	        const actionsByTick = this.#getActionsByTick(params, inputState);
-	        const dispatcher = new ActionDispatcher_js_1.ActionDispatcher(inputState, context, await ActionDispatcher_js_1.ActionDispatcher.isMacOS(context).catch(() => false));
-	        await dispatcher.dispatchActions(actionsByTick);
-	        return {};
-	    }
-	    async releaseActions(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        const topContext = context.top;
-	        const inputState = this.#inputStateManager.get(topContext);
-	        const dispatcher = new ActionDispatcher_js_1.ActionDispatcher(inputState, context, await ActionDispatcher_js_1.ActionDispatcher.isMacOS(context).catch(() => false));
-	        await dispatcher.dispatchTickActions(inputState.cancelList.reverse());
-	        this.#inputStateManager.delete(topContext);
-	        return {};
-	    }
-	    async setFiles(params) {
-	        const context = this.#browsingContextStorage.getContext(params.context);
-	        const realm = await context.getOrCreateSandbox(undefined);
-	        let result;
-	        try {
-	            result = await realm.callFunction(String(function getFiles(fileListLength) {
-	                if (!(this instanceof HTMLInputElement)) {
-	                    return 0 /* ErrorCode.Object */;
-	                }
-	                if (this.type !== 'file') {
-	                    return 1 /* ErrorCode.Type */;
-	                }
-	                if (this.disabled) {
-	                    return 2 /* ErrorCode.Disabled */;
-	                }
-	                if (fileListLength > 1 && !this.multiple) {
-	                    return 3 /* ErrorCode.Multiple */;
-	                }
-	                return;
-	            }), params.element, [{ type: 'number', value: params.files.length }], false, "none" /* Script.ResultOwnership.None */, {}, false);
-	        }
-	        catch {
-	            throw new protocol_js_1$c.NoSuchElementException(`Could not find element ${params.element.sharedId}`);
-	        }
-	        (0, assert_js_1$3.assert)(result.type === 'success');
-	        if (result.result.type === 'number') {
-	            switch (result.result.value) {
-	                case 0 /* ErrorCode.Object */: {
-	                    throw new protocol_js_1$c.NoSuchElementException(`Could not find element ${params.element.sharedId}`);
-	                }
-	                case 1 /* ErrorCode.Type */: {
-	                    throw new protocol_js_1$c.UnableToSetFileInputException(`Element ${params.element.sharedId} is not a file input`);
-	                }
-	                case 2 /* ErrorCode.Disabled */: {
-	                    throw new protocol_js_1$c.UnableToSetFileInputException(`Input element ${params.element.sharedId} is disabled`);
-	                }
-	                case 3 /* ErrorCode.Multiple */: {
-	                    throw new protocol_js_1$c.UnableToSetFileInputException(`Cannot set multiple files on a non-multiple input element`);
-	                }
-	            }
-	        }
-	        /**
-	         * The zero-length array is a special case, it seems that
-	         * DOM.setFileInputFiles does not actually update the files in that case, so
-	         * the solution is to eval the element value to a new FileList directly.
-	         */
-	        if (params.files.length === 0) {
-	            // XXX: These events should converted to trusted events. Perhaps do this
-	            // in `DOM.setFileInputFiles`?
-	            await realm.callFunction(String(function dispatchEvent() {
-	                if (this.files?.length === 0) {
-	                    this.dispatchEvent(new Event('cancel', {
-	                        bubbles: true,
-	                    }));
-	                    return;
-	                }
-	                this.files = new DataTransfer().files;
-	                // Dispatch events for this case because it should behave akin to a user action.
-	                this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
-	                this.dispatchEvent(new Event('change', { bubbles: true }));
-	            }), params.element, [], false, "none" /* Script.ResultOwnership.None */, {}, false);
-	            return {};
-	        }
-	        // Our goal here is to iterate over the input element files and get their
-	        // file paths.
-	        const paths = [];
-	        for (let i = 0; i < params.files.length; ++i) {
-	            const result = await realm.callFunction(String(function getFiles(index) {
-	                return this.files?.item(index);
-	            }), params.element, [{ type: 'number', value: 0 }], false, "root" /* Script.ResultOwnership.Root */, {}, false);
-	            (0, assert_js_1$3.assert)(result.type === 'success');
-	            if (result.result.type !== 'object') {
-	                break;
-	            }
-	            const { handle } = result.result;
-	            (0, assert_js_1$3.assert)(handle !== undefined);
-	            const { path } = await realm.cdpClient.sendCommand('DOM.getFileInfo', {
-	                objectId: handle,
-	            });
-	            paths.push(path);
-	            // Cleanup the handle.
-	            void realm.disown(handle).catch(undefined);
-	        }
-	        paths.sort();
-	        // We create a new array so we preserve the order of the original files.
-	        const sortedFiles = [...params.files].sort();
-	        if (paths.length !== params.files.length ||
-	            sortedFiles.some((path, index) => {
-	                return paths[index] !== path;
-	            })) {
-	            const { objectId } = await realm.deserializeForCdp(params.element);
-	            // This cannot throw since this was just used in `callFunction` above.
-	            (0, assert_js_1$3.assert)(objectId !== undefined);
-	            await realm.cdpClient.sendCommand('DOM.setFileInputFiles', {
-	                files: params.files,
-	                objectId,
-	            });
-	        }
-	        else {
-	            // XXX: We should dispatch a trusted event.
-	            await realm.callFunction(String(function dispatchEvent() {
-	                this.dispatchEvent(new Event('cancel', {
-	                    bubbles: true,
-	                }));
-	            }), params.element, [], false, "none" /* Script.ResultOwnership.None */, {}, false);
-	        }
-	        return {};
-	    }
-	    #getActionsByTick(params, inputState) {
-	        const actionsByTick = [];
-	        for (const action of params.actions) {
-	            switch (action.type) {
-	                case "pointer" /* SourceType.Pointer */: {
-	                    action.parameters ??= { pointerType: "mouse" /* Input.PointerType.Mouse */ };
-	                    action.parameters.pointerType ??= "mouse" /* Input.PointerType.Mouse */;
-	                    const source = inputState.getOrCreate(action.id, "pointer" /* SourceType.Pointer */, action.parameters.pointerType);
-	                    if (source.subtype !== action.parameters.pointerType) {
-	                        throw new protocol_js_1$c.InvalidArgumentException(`Expected input source ${action.id} to be ${source.subtype}; got ${action.parameters.pointerType}.`);
-	                    }
-	                    break;
-	                }
-	                default:
-	                    inputState.getOrCreate(action.id, action.type);
-	            }
-	            const actions = action.actions.map((item) => ({
-	                id: action.id,
-	                action: item,
-	            }));
-	            for (let i = 0; i < actions.length; i++) {
-	                if (actionsByTick.length === i) {
-	                    actionsByTick.push([]);
-	                }
-	                actionsByTick[i].push(actions[i]);
-	            }
-	        }
-	        return actionsByTick;
-	    }
-	}
-	InputProcessor$1.InputProcessor = InputProcessor;
-
-	var NetworkProcessor$1 = {};
-
-	var NetworkUtils = {};
-
-	var Base64 = {};
-
-	/**
-	 * Copyright 2024 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(Base64, "__esModule", { value: true });
-	Base64.base64ToString = void 0;
-	/**
-	 * Encodes a string to base64.
-	 *
-	 * Uses the native Web API if available, otherwise falls back to a NodeJS Buffer.
-	 * @param {string} base64Str
-	 * @return {string}
-	 */
-	function base64ToString(base64Str) {
-	    // Available only if run in a browser context.
-	    if ('atob' in globalThis) {
-	        return globalThis.atob(base64Str);
-	    }
-	    // Available only if run in a NodeJS context.
-	    return Buffer.from(base64Str, 'base64').toString('ascii');
-	}
-	Base64.base64ToString = base64ToString;
-
-	var UrlPattern = {};
-
-	var M=Object.defineProperty;var Pe=Object.getOwnPropertyDescriptor;var Re=Object.getOwnPropertyNames;var Ee=Object.prototype.hasOwnProperty;var Oe=(e,t)=>{for(var r in t)M(e,r,{get:t[r],enumerable:!0});},ke=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Re(t))!Ee.call(e,a)&&a!==r&&M(e,a,{get:()=>t[a],enumerable:!(n=Pe(t,a))||n.enumerable});return e};var Te=e=>ke(M({},"__esModule",{value:!0}),e);var Ne={};Oe(Ne,{URLPattern:()=>Y});var urlpattern=Te(Ne);var R=class{type=3;name="";prefix="";value="";suffix="";modifier=3;constructor(t,r,n,a,c,l){this.type=t,this.name=r,this.prefix=n,this.value=a,this.suffix=c,this.modifier=l;}hasCustomName(){return this.name!==""&&typeof this.name!="number"}},Ae=/[$_\p{ID_Start}]/u,ye=/[$_\u200C\u200D\p{ID_Continue}]/u,v=".*";function we(e,t){return (t?/^[\x00-\xFF]*$/:/^[\x00-\x7F]*$/).test(e)}function D(e,t=!1){let r=[],n=0;for(;n<e.length;){let a=e[n],c=function(l){if(!t)throw new TypeError(l);r.push({type:"INVALID_CHAR",index:n,value:e[n++]});};if(a==="*"){r.push({type:"ASTERISK",index:n,value:e[n++]});continue}if(a==="+"||a==="?"){r.push({type:"OTHER_MODIFIER",index:n,value:e[n++]});continue}if(a==="\\"){r.push({type:"ESCAPED_CHAR",index:n++,value:e[n++]});continue}if(a==="{"){r.push({type:"OPEN",index:n,value:e[n++]});continue}if(a==="}"){r.push({type:"CLOSE",index:n,value:e[n++]});continue}if(a===":"){let l="",s=n+1;for(;s<e.length;){let i=e.substr(s,1);if(s===n+1&&Ae.test(i)||s!==n+1&&ye.test(i)){l+=e[s++];continue}break}if(!l){c(`Missing parameter name at ${n}`);continue}r.push({type:"NAME",index:n,value:l}),n=s;continue}if(a==="("){let l=1,s="",i=n+1,o=!1;if(e[i]==="?"){c(`Pattern cannot start with "?" at ${i}`);continue}for(;i<e.length;){if(!we(e[i],!1)){c(`Invalid character '${e[i]}' at ${i}.`),o=!0;break}if(e[i]==="\\"){s+=e[i++]+e[i++];continue}if(e[i]===")"){if(l--,l===0){i++;break}}else if(e[i]==="("&&(l++,e[i+1]!=="?")){c(`Capturing groups are not allowed at ${i}`),o=!0;break}s+=e[i++];}if(o)continue;if(l){c(`Unbalanced pattern at ${n}`);continue}if(!s){c(`Missing pattern at ${n}`);continue}r.push({type:"REGEX",index:n,value:s}),n=i;continue}r.push({type:"CHAR",index:n,value:e[n++]});}return r.push({type:"END",index:n,value:""}),r}function F(e,t={}){let r=D(e);t.delimiter??="/#?",t.prefixes??="./";let n=`[^${S(t.delimiter)}]+?`,a=[],c=0,l=0,i=new Set,o=h=>{if(l<r.length&&r[l].type===h)return r[l++].value},f=()=>o("OTHER_MODIFIER")??o("ASTERISK"),d=h=>{let u=o(h);if(u!==void 0)return u;let{type:p,index:A}=r[l];throw new TypeError(`Unexpected ${p} at ${A}, expected ${h}`)},T=()=>{let h="",u;for(;u=o("CHAR")??o("ESCAPED_CHAR");)h+=u;return h},xe=h=>h,L=t.encodePart||xe,I="",U=h=>{I+=h;},$=()=>{I.length&&(a.push(new R(3,"","",L(I),"",3)),I="");},X=(h,u,p,A,Z)=>{let g=3;switch(Z){case"?":g=1;break;case"*":g=0;break;case"+":g=2;break}if(!u&&!p&&g===3){U(h);return}if($(),!u&&!p){if(!h)return;a.push(new R(3,"","",L(h),"",g));return}let m;p?p==="*"?m=v:m=p:m=n;let O=2;m===n?(O=1,m=""):m===v&&(O=0,m="");let P;if(u?P=u:p&&(P=c++),i.has(P))throw new TypeError(`Duplicate name '${P}'.`);i.add(P),a.push(new R(O,P,L(h),m,L(A),g));};for(;l<r.length;){let h=o("CHAR"),u=o("NAME"),p=o("REGEX");if(!u&&!p&&(p=o("ASTERISK")),u||p){let g=h??"";t.prefixes.indexOf(g)===-1&&(U(g),g=""),$();let m=f();X(g,u,p,"",m);continue}let A=h??o("ESCAPED_CHAR");if(A){U(A);continue}if(o("OPEN")){let g=T(),m=o("NAME"),O=o("REGEX");!m&&!O&&(O=o("ASTERISK"));let P=T();d("CLOSE");let be=f();X(g,m,O,P,be);continue}$(),d("END");}return a}function S(e){return e.replace(/([.+*?^${}()[\]|/\\])/g,"\\$1")}function B(e){return e&&e.ignoreCase?"ui":"u"}function q(e,t,r){return W(F(e,r),t,r)}function k(e){switch(e){case 0:return "*";case 1:return "?";case 2:return "+";case 3:return ""}}function W(e,t,r={}){r.delimiter??="/#?",r.prefixes??="./",r.sensitive??=!1,r.strict??=!1,r.end??=!0,r.start??=!0,r.endsWith="";let n=r.start?"^":"";for(let s of e){if(s.type===3){s.modifier===3?n+=S(s.value):n+=`(?:${S(s.value)})${k(s.modifier)}`;continue}t&&t.push(s.name);let i=`[^${S(r.delimiter)}]+?`,o=s.value;if(s.type===1?o=i:s.type===0&&(o=v),!s.prefix.length&&!s.suffix.length){s.modifier===3||s.modifier===1?n+=`(${o})${k(s.modifier)}`:n+=`((?:${o})${k(s.modifier)})`;continue}if(s.modifier===3||s.modifier===1){n+=`(?:${S(s.prefix)}(${o})${S(s.suffix)})`,n+=k(s.modifier);continue}n+=`(?:${S(s.prefix)}`,n+=`((?:${o})(?:`,n+=S(s.suffix),n+=S(s.prefix),n+=`(?:${o}))*)${S(s.suffix)})`,s.modifier===0&&(n+="?");}let a=`[${S(r.endsWith)}]|$`,c=`[${S(r.delimiter)}]`;if(r.end)return r.strict||(n+=`${c}?`),r.endsWith.length?n+=`(?=${a})`:n+="$",new RegExp(n,B(r));r.strict||(n+=`(?:${c}(?=${a}))?`);let l=!1;if(e.length){let s=e[e.length-1];s.type===3&&s.modifier===3&&(l=r.delimiter.indexOf(s)>-1);}return l||(n+=`(?=${c}|${a})`),new RegExp(n,B(r))}var x={delimiter:"",prefixes:"",sensitive:!0,strict:!0},J={delimiter:".",prefixes:"",sensitive:!0,strict:!0},Q={delimiter:"/",prefixes:"/",sensitive:!0,strict:!0};function ee(e,t){return e.length?e[0]==="/"?!0:!t||e.length<2?!1:(e[0]=="\\"||e[0]=="{")&&e[1]=="/":!1}function te(e,t){return e.startsWith(t)?e.substring(t.length,e.length):e}function Ce(e,t){return e.endsWith(t)?e.substr(0,e.length-t.length):e}function _(e){return !e||e.length<2?!1:e[0]==="["||(e[0]==="\\"||e[0]==="{")&&e[1]==="["}var re=["ftp","file","http","https","ws","wss"];function N(e){if(!e)return !0;for(let t of re)if(e.test(t))return !0;return !1}function ne(e,t){if(e=te(e,"#"),t||e==="")return e;let r=new URL("https://example.com");return r.hash=e,r.hash?r.hash.substring(1,r.hash.length):""}function se(e,t){if(e=te(e,"?"),t||e==="")return e;let r=new URL("https://example.com");return r.search=e,r.search?r.search.substring(1,r.search.length):""}function ie(e,t){return t||e===""?e:_(e)?K(e):j(e)}function ae(e,t){if(t||e==="")return e;let r=new URL("https://example.com");return r.password=e,r.password}function oe(e,t){if(t||e==="")return e;let r=new URL("https://example.com");return r.username=e,r.username}function ce(e,t,r){if(r||e==="")return e;if(t&&!re.includes(t))return new URL(`${t}:${e}`).pathname;let n=e[0]=="/";return e=new URL(n?e:"/-"+e,"https://example.com").pathname,n||(e=e.substring(2,e.length)),e}function le(e,t,r){return z(t)===e&&(e=""),r||e===""?e:G(e)}function fe(e,t){return e=Ce(e,":"),t||e===""?e:y(e)}function z(e){switch(e){case"ws":case"http":return "80";case"wws":case"https":return "443";case"ftp":return "21";default:return ""}}function y(e){if(e==="")return e;if(/^[-+.A-Za-z0-9]*$/.test(e))return e.toLowerCase();throw new TypeError(`Invalid protocol '${e}'.`)}function he(e){if(e==="")return e;let t=new URL("https://example.com");return t.username=e,t.username}function ue(e){if(e==="")return e;let t=new URL("https://example.com");return t.password=e,t.password}function j(e){if(e==="")return e;if(/[\t\n\r #%/:<>?@[\]^\\|]/g.test(e))throw new TypeError(`Invalid hostname '${e}'`);let t=new URL("https://example.com");return t.hostname=e,t.hostname}function K(e){if(e==="")return e;if(/[^0-9a-fA-F[\]:]/g.test(e))throw new TypeError(`Invalid IPv6 hostname '${e}'`);return e.toLowerCase()}function G(e){if(e===""||/^[0-9]*$/.test(e)&&parseInt(e)<=65535)return e;throw new TypeError(`Invalid port '${e}'.`)}function de(e){if(e==="")return e;let t=new URL("https://example.com");return t.pathname=e[0]!=="/"?"/-"+e:e,e[0]!=="/"?t.pathname.substring(2,t.pathname.length):t.pathname}function pe(e){return e===""?e:new URL(`data:${e}`).pathname}function ge(e){if(e==="")return e;let t=new URL("https://example.com");return t.search=e,t.search.substring(1,t.search.length)}function me(e){if(e==="")return e;let t=new URL("https://example.com");return t.hash=e,t.hash.substring(1,t.hash.length)}var H=class{#i;#n=[];#t={};#e=0;#s=1;#l=0;#o=0;#d=0;#p=0;#g=!1;constructor(t){this.#i=t;}get result(){return this.#t}parse(){for(this.#n=D(this.#i,!0);this.#e<this.#n.length;this.#e+=this.#s){if(this.#s=1,this.#n[this.#e].type==="END"){if(this.#o===0){this.#b(),this.#f()?this.#r(9,1):this.#h()?this.#r(8,1):this.#r(7,0);continue}else if(this.#o===2){this.#u(5);continue}this.#r(10,0);break}if(this.#d>0)if(this.#A())this.#d-=1;else continue;if(this.#T()){this.#d+=1;continue}switch(this.#o){case 0:this.#P()&&this.#u(1);break;case 1:if(this.#P()){this.#C();let t=7,r=1;this.#E()?(t=2,r=3):this.#g&&(t=2),this.#r(t,r);}break;case 2:this.#S()?this.#u(3):(this.#x()||this.#h()||this.#f())&&this.#u(5);break;case 3:this.#O()?this.#r(4,1):this.#S()&&this.#r(5,1);break;case 4:this.#S()&&this.#r(5,1);break;case 5:this.#y()?this.#p+=1:this.#w()&&(this.#p-=1),this.#k()&&!this.#p?this.#r(6,1):this.#x()?this.#r(7,0):this.#h()?this.#r(8,1):this.#f()&&this.#r(9,1);break;case 6:this.#x()?this.#r(7,0):this.#h()?this.#r(8,1):this.#f()&&this.#r(9,1);break;case 7:this.#h()?this.#r(8,1):this.#f()&&this.#r(9,1);break;case 8:this.#f()&&this.#r(9,1);break;}}this.#t.hostname!==void 0&&this.#t.port===void 0&&(this.#t.port="");}#r(t,r){switch(this.#o){case 0:break;case 1:this.#t.protocol=this.#c();break;case 2:break;case 3:this.#t.username=this.#c();break;case 4:this.#t.password=this.#c();break;case 5:this.#t.hostname=this.#c();break;case 6:this.#t.port=this.#c();break;case 7:this.#t.pathname=this.#c();break;case 8:this.#t.search=this.#c();break;case 9:this.#t.hash=this.#c();break;}this.#o!==0&&t!==10&&([1,2,3,4].includes(this.#o)&&[6,7,8,9].includes(t)&&(this.#t.hostname??=""),[1,2,3,4,5,6].includes(this.#o)&&[8,9].includes(t)&&(this.#t.pathname??=this.#g?"/":""),[1,2,3,4,5,6,7].includes(this.#o)&&t===9&&(this.#t.search??="")),this.#R(t,r);}#R(t,r){this.#o=t,this.#l=this.#e+r,this.#e+=r,this.#s=0;}#b(){this.#e=this.#l,this.#s=0;}#u(t){this.#b(),this.#o=t;}#m(t){return t<0&&(t=this.#n.length-t),t<this.#n.length?this.#n[t]:this.#n[this.#n.length-1]}#a(t,r){let n=this.#m(t);return n.value===r&&(n.type==="CHAR"||n.type==="ESCAPED_CHAR"||n.type==="INVALID_CHAR")}#P(){return this.#a(this.#e,":")}#E(){return this.#a(this.#e+1,"/")&&this.#a(this.#e+2,"/")}#S(){return this.#a(this.#e,"@")}#O(){return this.#a(this.#e,":")}#k(){return this.#a(this.#e,":")}#x(){return this.#a(this.#e,"/")}#h(){if(this.#a(this.#e,"?"))return !0;if(this.#n[this.#e].value!=="?")return !1;let t=this.#m(this.#e-1);return t.type!=="NAME"&&t.type!=="REGEX"&&t.type!=="CLOSE"&&t.type!=="ASTERISK"}#f(){return this.#a(this.#e,"#")}#T(){return this.#n[this.#e].type=="OPEN"}#A(){return this.#n[this.#e].type=="CLOSE"}#y(){return this.#a(this.#e,"[")}#w(){return this.#a(this.#e,"]")}#c(){let t=this.#n[this.#e],r=this.#m(this.#l).index;return this.#i.substring(r,t.index)}#C(){let t={};Object.assign(t,x),t.encodePart=y;let r=q(this.#c(),void 0,t);this.#g=N(r);}};var V=["protocol","username","password","hostname","port","pathname","search","hash"],E="*";function Se(e,t){if(typeof e!="string")throw new TypeError("parameter 1 is not of type 'string'.");let r=new URL(e,t);return {protocol:r.protocol.substring(0,r.protocol.length-1),username:r.username,password:r.password,hostname:r.hostname,port:r.port,pathname:r.pathname,search:r.search!==""?r.search.substring(1,r.search.length):void 0,hash:r.hash!==""?r.hash.substring(1,r.hash.length):void 0}}function b(e,t){return t?C(e):e}function w(e,t,r){let n;if(typeof t.baseURL=="string")try{n=new URL(t.baseURL),t.protocol===void 0&&(e.protocol=b(n.protocol.substring(0,n.protocol.length-1),r)),!r&&t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.username===void 0&&(e.username=b(n.username,r)),!r&&t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.username===void 0&&t.password===void 0&&(e.password=b(n.password,r)),t.protocol===void 0&&t.hostname===void 0&&(e.hostname=b(n.hostname,r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&(e.port=b(n.port,r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.pathname===void 0&&(e.pathname=b(n.pathname,r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.pathname===void 0&&t.search===void 0&&(e.search=b(n.search.substring(1,n.search.length),r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.pathname===void 0&&t.search===void 0&&t.hash===void 0&&(e.hash=b(n.hash.substring(1,n.hash.length),r));}catch{throw new TypeError(`invalid baseURL '${t.baseURL}'.`)}if(typeof t.protocol=="string"&&(e.protocol=fe(t.protocol,r)),typeof t.username=="string"&&(e.username=oe(t.username,r)),typeof t.password=="string"&&(e.password=ae(t.password,r)),typeof t.hostname=="string"&&(e.hostname=ie(t.hostname,r)),typeof t.port=="string"&&(e.port=le(t.port,e.protocol,r)),typeof t.pathname=="string"){if(e.pathname=t.pathname,n&&!ee(e.pathname,r)){let a=n.pathname.lastIndexOf("/");a>=0&&(e.pathname=b(n.pathname.substring(0,a+1),r)+e.pathname);}e.pathname=ce(e.pathname,e.protocol,r);}return typeof t.search=="string"&&(e.search=se(t.search,r)),typeof t.hash=="string"&&(e.hash=ne(t.hash,r)),e}function C(e){return e.replace(/([+*?:{}()\\])/g,"\\$1")}function Le(e){return e.replace(/([.+*?^${}()[\]|/\\])/g,"\\$1")}function Ie(e,t){t.delimiter??="/#?",t.prefixes??="./",t.sensitive??=!1,t.strict??=!1,t.end??=!0,t.start??=!0,t.endsWith="";let r=".*",n=`[^${Le(t.delimiter)}]+?`,a=/[$_\u200C\u200D\p{ID_Continue}]/u,c="";for(let l=0;l<e.length;++l){let s=e[l];if(s.type===3){if(s.modifier===3){c+=C(s.value);continue}c+=`{${C(s.value)}}${k(s.modifier)}`;continue}let i=s.hasCustomName(),o=!!s.suffix.length||!!s.prefix.length&&(s.prefix.length!==1||!t.prefixes.includes(s.prefix)),f=l>0?e[l-1]:null,d=l<e.length-1?e[l+1]:null;if(!o&&i&&s.type===1&&s.modifier===3&&d&&!d.prefix.length&&!d.suffix.length)if(d.type===3){let T=d.value.length>0?d.value[0]:"";o=a.test(T);}else o=!d.hasCustomName();if(!o&&!s.prefix.length&&f&&f.type===3){let T=f.value[f.value.length-1];o=t.prefixes.includes(T);}o&&(c+="{"),c+=C(s.prefix),i&&(c+=`:${s.name}`),s.type===2?c+=`(${s.value})`:s.type===1?i||(c+=`(${n})`):s.type===0&&(!i&&(!f||f.type===3||f.modifier!==3||o||s.prefix!=="")?c+="*":c+=`(${r})`),s.type===1&&i&&s.suffix.length&&a.test(s.suffix[0])&&(c+="\\"),c+=C(s.suffix),o&&(c+="}"),s.modifier!==3&&(c+=k(s.modifier));}return c}var Y=class{#i;#n={};#t={};#e={};#s={};#l=!1;constructor(t={},r,n){try{let a;if(typeof r=="string"?a=r:n=r,typeof t=="string"){let i=new H(t);if(i.parse(),t=i.result,a===void 0&&typeof t.protocol!="string")throw new TypeError("A base URL must be provided for a relative constructor string.");t.baseURL=a;}else {if(!t||typeof t!="object")throw new TypeError("parameter 1 is not of type 'string' and cannot convert to dictionary.");if(a)throw new TypeError("parameter 1 is not of type 'string'.")}typeof n>"u"&&(n={ignoreCase:!1});let c={ignoreCase:n.ignoreCase===!0},l={pathname:E,protocol:E,username:E,password:E,hostname:E,port:E,search:E,hash:E};this.#i=w(l,t,!0),z(this.#i.protocol)===this.#i.port&&(this.#i.port="");let s;for(s of V){if(!(s in this.#i))continue;let i={},o=this.#i[s];switch(this.#t[s]=[],s){case"protocol":Object.assign(i,x),i.encodePart=y;break;case"username":Object.assign(i,x),i.encodePart=he;break;case"password":Object.assign(i,x),i.encodePart=ue;break;case"hostname":Object.assign(i,J),_(o)?i.encodePart=K:i.encodePart=j;break;case"port":Object.assign(i,x),i.encodePart=G;break;case"pathname":N(this.#n.protocol)?(Object.assign(i,Q,c),i.encodePart=de):(Object.assign(i,x,c),i.encodePart=pe);break;case"search":Object.assign(i,x,c),i.encodePart=ge;break;case"hash":Object.assign(i,x,c),i.encodePart=me;break}try{this.#s[s]=F(o,i),this.#n[s]=W(this.#s[s],this.#t[s],i),this.#e[s]=Ie(this.#s[s],i),this.#l=this.#l||this.#s[s].some(f=>f.type===2);}catch{throw new TypeError(`invalid ${s} pattern '${this.#i[s]}'.`)}}}catch(a){throw new TypeError(`Failed to construct 'URLPattern': ${a.message}`)}}test(t={},r){let n={pathname:"",protocol:"",username:"",password:"",hostname:"",port:"",search:"",hash:""};if(typeof t!="string"&&r)throw new TypeError("parameter 1 is not of type 'string'.");if(typeof t>"u")return !1;try{typeof t=="object"?n=w(n,t,!1):n=w(n,Se(t,r),!1);}catch{return !1}let a;for(a of V)if(!this.#n[a].exec(n[a]))return !1;return !0}exec(t={},r){let n={pathname:"",protocol:"",username:"",password:"",hostname:"",port:"",search:"",hash:""};if(typeof t!="string"&&r)throw new TypeError("parameter 1 is not of type 'string'.");if(typeof t>"u")return;try{typeof t=="object"?n=w(n,t,!1):n=w(n,Se(t,r),!1);}catch{return null}let a={};r?a.inputs=[t,r]:a.inputs=[t];let c;for(c of V){let l=this.#n[c].exec(n[c]);if(!l)return null;let s={};for(let[i,o]of this.#t[c].entries())if(typeof o=="string"||typeof o=="number"){let f=l[i+1];s[o]=f;}a[c]={input:n[c]??"",groups:s};}return a}static compareComponent(t,r,n){let a=(i,o)=>{for(let f of ["type","modifier","prefix","value","suffix"]){if(i[f]<o[f])return -1;if(i[f]===o[f])continue;return 1}return 0},c=new R(3,"","","","",3),l=new R(0,"","","","",3),s=(i,o)=>{let f=0;for(;f<Math.min(i.length,o.length);++f){let d=a(i[f],o[f]);if(d)return d}return i.length===o.length?0:a(i[f]??c,o[f]??c)};return !r.#e[t]&&!n.#e[t]?0:r.#e[t]&&!n.#e[t]?s(r.#s[t],[l]):!r.#e[t]&&n.#e[t]?s([l],n.#s[t]):s(r.#s[t],n.#s[t])}get protocol(){return this.#e.protocol}get username(){return this.#e.username}get password(){return this.#e.password}get hostname(){return this.#e.hostname}get port(){return this.#e.port}get pathname(){return this.#e.pathname}get search(){return this.#e.search}get hash(){return this.#e.hash}get hasRegExpGroups(){return this.#l}};
-
-	const { URLPattern: URLPattern$2 } = urlpattern;
-
-	var urlpatternPolyfill = { URLPattern: URLPattern$2 };
-
-	if (!globalThis.URLPattern) {
-	  globalThis.URLPattern = URLPattern$2;
-	}
-
-	Object.defineProperty(UrlPattern, "__esModule", { value: true });
-	UrlPattern.URLPattern = void 0;
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	const urlpattern_polyfill_1 = urlpatternPolyfill;
-	// XXX: Switch to native URLPattern when available.
-	// https://github.com/nodejs/node/issues/40844
-	let URLPattern$1 = urlpattern_polyfill_1.URLPattern;
-	UrlPattern.URLPattern = URLPattern$1;
-	if ('URLPattern' in globalThis) {
-	    UrlPattern.URLPattern = URLPattern$1 = globalThis.URLPattern;
-	}
-
-	/*
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 *
-	 */
-	Object.defineProperty(NetworkUtils, "__esModule", { value: true });
-	NetworkUtils.matchUrlPattern = NetworkUtils.isSpecialScheme = NetworkUtils.sameSiteBiDiToCdp = NetworkUtils.bidiToCdpCookie = NetworkUtils.deserializeByteValue = NetworkUtils.cdpToBiDiCookie = NetworkUtils.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction = NetworkUtils.cdpFetchHeadersFromBidiNetworkHeaders = NetworkUtils.bidiNetworkHeadersFromCdpFetchHeaders = NetworkUtils.cdpNetworkHeadersFromBidiNetworkHeaders = NetworkUtils.bidiNetworkHeadersFromCdpNetworkHeaders = NetworkUtils.computeHeadersSize = void 0;
-	const ErrorResponse_js_1 = ErrorResponse;
-	const Base64_js_1 = Base64;
-	const UrlPattern_js_1 = UrlPattern;
-	function computeHeadersSize(headers) {
-	    const requestHeaders = headers.reduce((acc, header) => {
-	        return `${acc}${header.name}: ${header.value.value}\r\n`;
-	    }, '');
-	    return new TextEncoder().encode(requestHeaders).length;
-	}
-	NetworkUtils.computeHeadersSize = computeHeadersSize;
-	/** Converts from CDP Network domain headers to Bidi network headers. */
-	function bidiNetworkHeadersFromCdpNetworkHeaders(headers) {
-	    if (!headers) {
-	        return [];
-	    }
-	    return Object.entries(headers).map(([name, value]) => ({
-	        name,
-	        value: {
-	            type: 'string',
-	            value,
-	        },
-	    }));
-	}
-	NetworkUtils.bidiNetworkHeadersFromCdpNetworkHeaders = bidiNetworkHeadersFromCdpNetworkHeaders;
-	/** Converts from Bidi network headers to CDP Network domain headers. */
-	function cdpNetworkHeadersFromBidiNetworkHeaders(headers) {
-	    if (headers === undefined) {
-	        return undefined;
-	    }
-	    return headers.reduce((result, header) => {
-	        // TODO: Distinguish between string and bytes?
-	        result[header.name] = header.value.value;
-	        return result;
-	    }, {});
-	}
-	NetworkUtils.cdpNetworkHeadersFromBidiNetworkHeaders = cdpNetworkHeadersFromBidiNetworkHeaders;
-	/** Converts from CDP Fetch domain header entries to Bidi network headers. */
-	function bidiNetworkHeadersFromCdpFetchHeaders(headers) {
-	    if (!headers) {
-	        return [];
-	    }
-	    return headers.map(({ name, value }) => ({
-	        name,
-	        value: {
-	            type: 'string',
-	            value,
-	        },
-	    }));
-	}
-	NetworkUtils.bidiNetworkHeadersFromCdpFetchHeaders = bidiNetworkHeadersFromCdpFetchHeaders;
-	/** Converts from Bidi network headers to CDP Fetch domain header entries. */
-	function cdpFetchHeadersFromBidiNetworkHeaders(headers) {
-	    if (headers === undefined) {
-	        return undefined;
-	    }
-	    return headers.map(({ name, value }) => ({
-	        name,
-	        value: value.value,
-	    }));
-	}
-	NetworkUtils.cdpFetchHeadersFromBidiNetworkHeaders = cdpFetchHeadersFromBidiNetworkHeaders;
-	/** Converts from Bidi auth action to CDP auth challenge response. */
-	function cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction(action) {
-	    switch (action) {
-	        case 'default':
-	            return 'Default';
-	        case 'cancel':
-	            return 'CancelAuth';
-	        case 'provideCredentials':
-	            return 'ProvideCredentials';
-	    }
-	}
-	NetworkUtils.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction = cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction;
-	/**
-	 * Converts from CDP Network domain cookie to BiDi network cookie.
-	 * * https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-Cookie
-	 * * https://w3c.github.io/webdriver-bidi/#type-network-Cookie
-	 */
-	function cdpToBiDiCookie(cookie) {
-	    const result = {
-	        name: cookie.name,
-	        value: { type: 'string', value: cookie.value },
-	        domain: cookie.domain,
-	        path: cookie.path,
-	        size: cookie.size,
-	        httpOnly: cookie.httpOnly,
-	        secure: cookie.secure,
-	        sameSite: cookie.sameSite === undefined
-	            ? "none" /* Network.SameSite.None */
-	            : sameSiteCdpToBiDi(cookie.sameSite),
-	        ...(cookie.expires >= 0 ? { expiry: cookie.expires } : undefined),
-	    };
-	    // Extending with CDP-specific properties with `goog:` prefix.
-	    result[`goog:session`] = cookie.session;
-	    result[`goog:priority`] = cookie.priority;
-	    result[`goog:sameParty`] = cookie.sameParty;
-	    result[`goog:sourceScheme`] = cookie.sourceScheme;
-	    result[`goog:sourcePort`] = cookie.sourcePort;
-	    if (cookie.partitionKey !== undefined) {
-	        result[`goog:partitionKey`] = cookie.partitionKey;
-	    }
-	    if (cookie.partitionKeyOpaque !== undefined) {
-	        result[`goog:partitionKeyOpaque`] = cookie.partitionKeyOpaque;
-	    }
-	    return result;
-	}
-	NetworkUtils.cdpToBiDiCookie = cdpToBiDiCookie;
-	/**
-	 * Decodes a byte value to a string.
-	 * @param {Network.BytesValue} value
-	 * @return {string}
-	 */
-	function deserializeByteValue(value) {
-	    if (value.type === 'base64') {
-	        return (0, Base64_js_1.base64ToString)(value.value);
-	    }
-	    return value.value;
-	}
-	NetworkUtils.deserializeByteValue = deserializeByteValue;
-	/**
-	 * Converts from BiDi set network cookie params to CDP Network domain cookie.
-	 * * https://w3c.github.io/webdriver-bidi/#type-network-Cookie
-	 * * https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-CookieParam
-	 */
-	function bidiToCdpCookie(params, partitionKey) {
-	    const deserializedValue = deserializeByteValue(params.cookie.value);
-	    const result = {
-	        name: params.cookie.name,
-	        value: deserializedValue,
-	        domain: params.cookie.domain,
-	        path: params.cookie.path ?? '/',
-	        secure: params.cookie.secure ?? false,
-	        httpOnly: params.cookie.httpOnly ?? false,
-	        // CDP's `partitionKey` is the BiDi's `partition.sourceOrigin`.
-	        ...(partitionKey.sourceOrigin !== undefined && {
-	            partitionKey: partitionKey.sourceOrigin,
-	        }),
-	        ...(params.cookie.expiry !== undefined && {
-	            expires: params.cookie.expiry,
-	        }),
-	        ...(params.cookie.sameSite !== undefined && {
-	            sameSite: sameSiteBiDiToCdp(params.cookie.sameSite),
-	        }),
-	    };
-	    // Extending with CDP-specific properties with `goog:` prefix.
-	    if (params.cookie[`goog:url`] !== undefined) {
-	        result.url = params.cookie[`goog:url`];
-	    }
-	    if (params.cookie[`goog:priority`] !== undefined) {
-	        result.priority = params.cookie[`goog:priority`];
-	    }
-	    if (params.cookie[`goog:sameParty`] !== undefined) {
-	        result.sameParty = params.cookie[`goog:sameParty`];
-	    }
-	    if (params.cookie[`goog:sourceScheme`] !== undefined) {
-	        result.sourceScheme = params.cookie[`goog:sourceScheme`];
-	    }
-	    if (params.cookie[`goog:sourcePort`] !== undefined) {
-	        result.sourcePort = params.cookie[`goog:sourcePort`];
-	    }
-	    return result;
-	}
-	NetworkUtils.bidiToCdpCookie = bidiToCdpCookie;
-	function sameSiteCdpToBiDi(sameSite) {
-	    switch (sameSite) {
-	        case 'Strict':
-	            return "strict" /* Network.SameSite.Strict */;
-	        case 'None':
-	            return "none" /* Network.SameSite.None */;
-	        case 'Lax':
-	            return "lax" /* Network.SameSite.Lax */;
-	        default:
-	            // Defaults to `Lax`:
-	            // https://web.dev/articles/samesite-cookies-explained#samesitelax_by_default
-	            return "lax" /* Network.SameSite.Lax */;
-	    }
-	}
-	function sameSiteBiDiToCdp(sameSite) {
-	    switch (sameSite) {
-	        case "strict" /* Network.SameSite.Strict */:
-	            return 'Strict';
-	        case "lax" /* Network.SameSite.Lax */:
-	            return 'Lax';
-	        case "none" /* Network.SameSite.None */:
-	            return 'None';
-	    }
-	    throw new ErrorResponse_js_1.InvalidArgumentException(`Unknown 'sameSite' value ${sameSite}`);
-	}
-	NetworkUtils.sameSiteBiDiToCdp = sameSiteBiDiToCdp;
-	/**
-	 * Returns true if the given protocol is special.
-	 * Special protocols are those that have a default port.
-	 *
-	 * Example inputs: 'http', 'http:'
-	 *
-	 * @see https://url.spec.whatwg.org/#special-scheme
-	 */
-	function isSpecialScheme(protocol) {
-	    return ['ftp', 'file', 'http', 'https', 'ws', 'wss'].includes(protocol.replace(/:$/, ''));
-	}
-	NetworkUtils.isSpecialScheme = isSpecialScheme;
-	/** Matches the given URLPattern against the given URL. */
-	function matchUrlPattern(urlPattern, url) {
-	    switch (urlPattern.type) {
-	        case 'string': {
-	            const pattern = new UrlPattern_js_1.URLPattern(urlPattern.pattern);
-	            return new UrlPattern_js_1.URLPattern({
-	                protocol: pattern.protocol,
-	                hostname: pattern.hostname,
-	                port: pattern.port,
-	                pathname: pattern.pathname,
-	                search: pattern.search,
-	            }).test(url);
-	        }
-	        case 'pattern':
-	            return new UrlPattern_js_1.URLPattern(urlPattern).test(url);
-	    }
-	}
-	NetworkUtils.matchUrlPattern = matchUrlPattern;
-
-	Object.defineProperty(NetworkProcessor$1, "__esModule", { value: true });
-	NetworkProcessor$1.NetworkProcessor = void 0;
-	const protocol_js_1$b = protocol;
-	const assert_js_1$2 = assert$1;
-	const NetworkUtils_js_1$3 = NetworkUtils;
-	/** Dispatches Network domain commands. */
-	class NetworkProcessor {
-	    #browsingContextStorage;
-	    #networkStorage;
-	    constructor(browsingContextStorage, networkStorage) {
-	        this.#browsingContextStorage = browsingContextStorage;
-	        this.#networkStorage = networkStorage;
-	    }
-	    async addIntercept(params) {
-	        this.#browsingContextStorage.verifyContextsList(params.contexts);
-	        const urlPatterns = params.urlPatterns ?? [];
-	        const parsedUrlPatterns = NetworkProcessor.parseUrlPatterns(urlPatterns);
-	        const intercept = this.#networkStorage.addIntercept({
-	            urlPatterns: parsedUrlPatterns,
-	            phases: params.phases,
-	            contexts: params.contexts,
-	        });
-	        await Promise.all(
-	        // TODO: We should to this for other CDP targets as well
-	        this.#browsingContextStorage.getTopLevelContexts().map((context) => {
-	            return context.cdpTarget.toggleFetchIfNeeded();
-	        }));
-	        return {
-	            intercept,
-	        };
-	    }
-	    async continueRequest(params) {
-	        const networkId = params.request;
-	        if (params.url !== undefined) {
-	            NetworkProcessor.parseUrlString(params.url);
-	        }
-	        const request = this.#getBlockedRequestOrFail(networkId, [
-	            "beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */,
-	        ]);
-	        const { url, method, headers } = params;
-	        // TODO: Set / expand.
-	        // ; Step 9. cookies
-	        // ; Step 10. body
-	        const requestHeaders = (0, NetworkUtils_js_1$3.cdpFetchHeadersFromBidiNetworkHeaders)(headers);
-	        await request.continueRequest(url, method, requestHeaders);
-	        return {};
-	    }
-	    async continueResponse(params) {
-	        const { request: networkId, statusCode, reasonPhrase, headers } = params;
-	        const responseHeaders = (0, NetworkUtils_js_1$3.cdpFetchHeadersFromBidiNetworkHeaders)(headers);
-	        const request = this.#getBlockedRequestOrFail(networkId, [
-	            "authRequired" /* Network.InterceptPhase.AuthRequired */,
-	            "responseStarted" /* Network.InterceptPhase.ResponseStarted */,
-	        ]);
-	        if (request.interceptPhase === "authRequired" /* Network.InterceptPhase.AuthRequired */) {
-	            if (params.credentials) {
-	                await Promise.all([
-	                    request.waitNextPhase,
-	                    request.continueWithAuth({
-	                        response: 'ProvideCredentials',
-	                        username: params.credentials.username,
-	                        password: params.credentials.password,
-	                    }),
-	                ]);
-	            }
-	            else {
-	                // We need to use `ProvideCredentials`
-	                // As `Default` may cancel the request
-	                await request.continueWithAuth({
-	                    response: 'ProvideCredentials',
-	                });
-	                return {};
-	            }
-	        }
-	        if (request.interceptPhase === "responseStarted" /* Network.InterceptPhase.ResponseStarted */) {
-	            // TODO: Set / expand.
-	            // ; Step 10. cookies
-	            await request.continueResponse({
-	                responseCode: statusCode,
-	                responsePhrase: reasonPhrase,
-	                responseHeaders,
-	            });
-	        }
-	        return {};
-	    }
-	    async continueWithAuth(params) {
-	        const networkId = params.request;
-	        const request = this.#getBlockedRequestOrFail(networkId, [
-	            "authRequired" /* Network.InterceptPhase.AuthRequired */,
-	        ]);
-	        let username;
-	        let password;
-	        if (params.action === 'provideCredentials') {
-	            const { credentials } = params;
-	            username = credentials.username;
-	            password = credentials.password;
-	            // TODO: This should be invalid argument exception.
-	            // Spec may need to be updated.
-	            (0, assert_js_1$2.assert)(credentials.type === 'password', `Credentials type ${credentials.type} must be 'password'`);
-	        }
-	        const response = (0, NetworkUtils_js_1$3.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction)(params.action);
-	        await request.continueWithAuth({
-	            response,
-	            username,
-	            password,
-	        });
-	        return {};
-	    }
-	    async failRequest({ request: networkId, }) {
-	        const request = this.#getRequestOrFail(networkId);
-	        if (request.interceptPhase === "authRequired" /* Network.InterceptPhase.AuthRequired */) {
-	            throw new protocol_js_1$b.InvalidArgumentException(`Request '${networkId}' in 'authRequired' phase cannot be failed`);
-	        }
-	        if (!request.interceptPhase) {
-	            throw new protocol_js_1$b.NoSuchRequestException(`No blocked request found for network id '${networkId}'`);
-	        }
-	        await request.failRequest('Failed');
-	        return {};
-	    }
-	    async provideResponse(params) {
-	        const { statusCode, reasonPhrase, headers, body, request: networkId, } = params;
-	        // TODO: Step 6
-	        // https://w3c.github.io/webdriver-bidi/#command-network-continueResponse
-	        const responseHeaders = (0, NetworkUtils_js_1$3.cdpFetchHeadersFromBidiNetworkHeaders)(headers);
-	        // TODO: Set / expand.
-	        // ; Step 10. cookies
-	        // ; Step 11. credentials
-	        const request = this.#getBlockedRequestOrFail(networkId, [
-	            "beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */,
-	            "responseStarted" /* Network.InterceptPhase.ResponseStarted */,
-	            "authRequired" /* Network.InterceptPhase.AuthRequired */,
-	        ]);
-	        await request.provideResponse({
-	            responseCode: statusCode ?? request.statusCode,
-	            responsePhrase: reasonPhrase,
-	            responseHeaders,
-	            body: body?.value, // TODO: Differ base64 / string
-	        });
-	        return {};
-	    }
-	    async removeIntercept(params) {
-	        this.#networkStorage.removeIntercept(params.intercept);
-	        await Promise.all(
-	        // TODO: We should to this for other CDP targets as well
-	        this.#browsingContextStorage.getTopLevelContexts().map((context) => {
-	            return context.cdpTarget.toggleFetchIfNeeded();
-	        }));
-	        return {};
-	    }
-	    #getRequestOrFail(id) {
-	        const request = this.#networkStorage.getRequestById(id);
-	        if (!request) {
-	            throw new protocol_js_1$b.NoSuchRequestException(`Network request with ID '${id}' doesn't exist`);
-	        }
-	        return request;
-	    }
-	    #getBlockedRequestOrFail(id, phases) {
-	        const request = this.#getRequestOrFail(id);
-	        if (!request.interceptPhase) {
-	            throw new protocol_js_1$b.NoSuchRequestException(`No blocked request found for network id '${id}'`);
-	        }
-	        if (request.interceptPhase && !phases.includes(request.interceptPhase)) {
-	            throw new protocol_js_1$b.InvalidArgumentException(`Blocked request for network id '${id}' is in '${request.interceptPhase}' phase`);
-	        }
-	        return request;
-	    }
-	    /**
-	     * Attempts to parse the given url.
-	     * Throws an InvalidArgumentException if the url is invalid.
-	     */
-	    static parseUrlString(url) {
-	        try {
-	            return new URL(url);
-	        }
-	        catch (error) {
-	            throw new protocol_js_1$b.InvalidArgumentException(`Invalid URL '${url}': ${error}`);
-	        }
-	    }
-	    static parseUrlPatterns(urlPatterns) {
-	        return urlPatterns.map((urlPattern) => {
-	            switch (urlPattern.type) {
-	                case 'string': {
-	                    NetworkProcessor.parseUrlString(urlPattern.pattern);
-	                    return urlPattern;
-	                }
-	                case 'pattern':
-	                    // No params signifies intercept all
-	                    if (urlPattern.protocol === undefined &&
-	                        urlPattern.hostname === undefined &&
-	                        urlPattern.port === undefined &&
-	                        urlPattern.pathname === undefined &&
-	                        urlPattern.search === undefined) {
-	                        return urlPattern;
-	                    }
-	                    if (urlPattern.protocol) {
-	                        urlPattern.protocol = unescapeURLPattern(urlPattern.protocol);
-	                        if (!urlPattern.protocol.match(/^[a-zA-Z+-.]+$/)) {
-	                            throw new protocol_js_1$b.InvalidArgumentException('Forbidden characters');
-	                        }
-	                    }
-	                    if (urlPattern.hostname) {
-	                        urlPattern.hostname = unescapeURLPattern(urlPattern.hostname);
-	                    }
-	                    if (urlPattern.port) {
-	                        urlPattern.port = unescapeURLPattern(urlPattern.port);
-	                    }
-	                    if (urlPattern.pathname) {
-	                        urlPattern.pathname = unescapeURLPattern(urlPattern.pathname);
-	                        if (urlPattern.pathname[0] !== '/') {
-	                            urlPattern.pathname = `/${urlPattern.pathname}`;
-	                        }
-	                        if (urlPattern.pathname.includes('#') ||
-	                            urlPattern.pathname.includes('?')) {
-	                            throw new protocol_js_1$b.InvalidArgumentException('Forbidden characters');
-	                        }
-	                    }
-	                    else if (urlPattern.pathname === '') {
-	                        urlPattern.pathname = '/';
-	                    }
-	                    if (urlPattern.search) {
-	                        urlPattern.search = unescapeURLPattern(urlPattern.search);
-	                        if (urlPattern.search[0] !== '?') {
-	                            urlPattern.search = `?${urlPattern.search}`;
-	                        }
-	                        if (urlPattern.search.includes('#')) {
-	                            throw new protocol_js_1$b.InvalidArgumentException('Forbidden characters');
-	                        }
-	                    }
-	                    if (urlPattern.protocol === '') {
-	                        throw new protocol_js_1$b.InvalidArgumentException(`URL pattern must specify a protocol`);
-	                    }
-	                    if (urlPattern.hostname === '') {
-	                        throw new protocol_js_1$b.InvalidArgumentException(`URL pattern must specify a hostname`);
-	                    }
-	                    if ((urlPattern.hostname?.length ?? 0) > 0) {
-	                        if (urlPattern.protocol?.match(/^file/i)) {
-	                            throw new protocol_js_1$b.InvalidArgumentException(`URL pattern protocol cannot be 'file'`);
-	                        }
-	                        if (urlPattern.hostname?.includes(':')) {
-	                            throw new protocol_js_1$b.InvalidArgumentException(`URL pattern hostname must not contain a colon`);
-	                        }
-	                    }
-	                    if (urlPattern.port === '') {
-	                        throw new protocol_js_1$b.InvalidArgumentException(`URL pattern must specify a port`);
-	                    }
-	                    try {
-	                        new URLPattern(urlPattern);
-	                    }
-	                    catch (error) {
-	                        throw new protocol_js_1$b.InvalidArgumentException(`${error}`);
-	                    }
-	                    return urlPattern;
-	            }
-	        });
-	    }
-	}
-	NetworkProcessor$1.NetworkProcessor = NetworkProcessor;
-	/**
-	 * See https://w3c.github.io/webdriver-bidi/#unescape-url-pattern
-	 */
-	function unescapeURLPattern(pattern) {
-	    const forbidden = new Set(['(', ')', '*', '{', '}']);
-	    let result = '';
-	    let isEscaped = false;
-	    for (const c of pattern) {
-	        if (!isEscaped) {
-	            if (forbidden.has(c)) {
-	                throw new protocol_js_1$b.InvalidArgumentException('Forbidden characters');
-	            }
-	            if (c === '\\') {
-	                isEscaped = true;
-	                continue;
-	            }
-	        }
-	        result += c;
-	        isEscaped = false;
-	    }
-	    return result;
-	}
-
-	var NetworkStorage$1 = {};
-
-	var NetworkRequest$1 = {};
-
-	/*
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 *
-	 */
-	Object.defineProperty(NetworkRequest$1, "__esModule", { value: true });
-	NetworkRequest$1.NetworkRequest = void 0;
-	const protocol_js_1$a = protocol;
-	const assert_js_1$1 = assert$1;
-	const Deferred_js_1 = Deferred$1;
-	const log_js_1$7 = log$1;
-	const NetworkUtils_js_1$2 = NetworkUtils;
-	const REALM_REGEX = /(?<=realm=").*(?=")/;
-	/** Abstracts one individual network request. */
-	class NetworkRequest {
-	    static unknownParameter = 'UNKNOWN';
-	    /**
-	     * Each network request has an associated request id, which is a string
-	     * uniquely identifying that request.
-	     *
-	     * The identifier for a request resulting from a redirect matches that of the
-	     * request that initiated it.
-	     */
-	    #id;
-	    #fetchId;
-	    /**
-	     * Indicates the network intercept phase, if the request is currently blocked.
-	     * Undefined necessarily implies that the request is not blocked.
-	     */
-	    #interceptPhase;
-	    #servedFromCache = false;
-	    #redirectCount;
-	    #request = {};
-	    #response = {};
-	    #eventManager;
-	    #networkStorage;
-	    #cdpTarget;
-	    #logger;
-	    #emittedEvents = {
-	        [protocol_js_1$a.ChromiumBidi.Network.EventNames.AuthRequired]: false,
-	        [protocol_js_1$a.ChromiumBidi.Network.EventNames.BeforeRequestSent]: false,
-	        [protocol_js_1$a.ChromiumBidi.Network.EventNames.FetchError]: false,
-	        [protocol_js_1$a.ChromiumBidi.Network.EventNames.ResponseCompleted]: false,
-	        [protocol_js_1$a.ChromiumBidi.Network.EventNames.ResponseStarted]: false,
-	    };
-	    waitNextPhase = new Deferred_js_1.Deferred();
-	    constructor(id, eventManager, networkStorage, cdpTarget, redirectCount = 0, logger) {
-	        this.#id = id;
-	        this.#eventManager = eventManager;
-	        this.#networkStorage = networkStorage;
-	        this.#cdpTarget = cdpTarget;
-	        this.#redirectCount = redirectCount;
-	        this.#logger = logger;
-	    }
-	    get id() {
-	        return this.#id;
-	    }
-	    get fetchId() {
-	        return this.#fetchId;
-	    }
-	    /**
-	     * When blocked returns the phase for it
-	     */
-	    get interceptPhase() {
-	        return this.#interceptPhase;
-	    }
-	    get url() {
-	        return (this.#response.info?.url ??
-	            this.#response.paused?.request.url ??
-	            this.#request.auth?.request.url ??
-	            this.#request.info?.request.url ??
-	            this.#request.paused?.request.url ??
-	            NetworkRequest.unknownParameter);
-	    }
-	    get method() {
-	        return (this.#request.info?.request.method ??
-	            this.#request.paused?.request.method ??
-	            this.#request.auth?.request.method ??
-	            this.#response.paused?.request.method ??
-	            NetworkRequest.unknownParameter);
-	    }
-	    get redirectCount() {
-	        return this.#redirectCount;
-	    }
-	    get cdpTarget() {
-	        return this.#cdpTarget;
-	    }
-	    get cdpClient() {
-	        return this.#cdpTarget.cdpClient;
-	    }
-	    isRedirecting() {
-	        return Boolean(this.#request.info);
-	    }
-	    #phaseChanged() {
-	        this.waitNextPhase.resolve();
-	        this.waitNextPhase = new Deferred_js_1.Deferred();
-	    }
-	    #interceptsInPhase(phase) {
-	        if (!this.#cdpTarget.isSubscribedTo(`network.${phase}`)) {
-	            return new Set();
-	        }
-	        return this.#networkStorage.getInterceptsForPhase(this, phase);
-	    }
-	    #isBlockedInPhase(phase) {
-	        return this.#interceptsInPhase(phase).size > 0;
-	    }
-	    handleRedirect(event) {
-	        this.#response.hasExtraInfo = event.redirectHasExtraInfo;
-	        this.#response.info = event.redirectResponse;
-	        this.#emitEventsIfReady({
-	            wasRedirected: true,
-	        });
-	    }
-	    #emitEventsIfReady(options = {}) {
-	        const requestExtraInfoCompleted = 
-	        // Flush redirects
-	        options.wasRedirected ||
-	            options.hasFailed ||
-	            Boolean(this.#request.extraInfo) ||
-	            // Requests from cache don't have extra info
-	            this.#servedFromCache ||
-	            // Sometimes there is no extra info and the response
-	            // is the only place we can find out
-	            Boolean(this.#response.info && !this.#response.hasExtraInfo);
-	        const requestInterceptionExpected = this.#isBlockedInPhase("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */);
-	        const requestInterceptionCompleted = !requestInterceptionExpected ||
-	            (requestInterceptionExpected && Boolean(this.#request.paused));
-	        if (Boolean(this.#request.info) &&
-	            (requestInterceptionExpected
-	                ? requestInterceptionCompleted
-	                : requestExtraInfoCompleted)) {
-	            this.#emitEvent(this.#getBeforeRequestEvent.bind(this));
-	        }
-	        const responseExtraInfoCompleted = Boolean(this.#response.extraInfo) ||
-	            // Response from cache don't have extra info
-	            this.#servedFromCache ||
-	            // Don't expect extra info if the flag is false
-	            Boolean(this.#response.info && !this.#response.hasExtraInfo);
-	        const responseInterceptionExpected = this.#isBlockedInPhase("responseStarted" /* Network.InterceptPhase.ResponseStarted */);
-	        if (this.#response.info ||
-	            (responseInterceptionExpected && Boolean(this.#response.paused))) {
-	            this.#emitEvent(this.#getResponseStartedEvent.bind(this));
-	        }
-	        const responseInterceptionCompleted = !responseInterceptionExpected ||
-	            (responseInterceptionExpected && Boolean(this.#response.paused));
-	        if (Boolean(this.#response.info) &&
-	            responseExtraInfoCompleted &&
-	            responseInterceptionCompleted) {
-	            this.#emitEvent(this.#getResponseReceivedEvent.bind(this));
-	        }
-	    }
-	    onRequestWillBeSentEvent(event) {
-	        this.#request.info = event;
-	        this.#emitEventsIfReady();
-	    }
-	    onRequestWillBeSentExtraInfoEvent(event) {
-	        this.#request.extraInfo = event;
-	        this.#emitEventsIfReady();
-	    }
-	    onResponseReceivedExtraInfoEvent(event) {
-	        this.#response.extraInfo = event;
-	        this.#emitEventsIfReady();
-	    }
-	    onResponseReceivedEvent(event) {
-	        this.#response.hasExtraInfo = event.hasExtraInfo;
-	        this.#response.info = event.response;
-	        this.#emitEventsIfReady();
-	    }
-	    onServedFromCache() {
-	        this.#servedFromCache = true;
-	        this.#emitEventsIfReady();
-	    }
-	    onLoadingFailedEvent(event) {
-	        this.#emitEventsIfReady({
-	            hasFailed: true,
-	        });
-	        this.#emitEvent(() => {
-	            return {
-	                method: protocol_js_1$a.ChromiumBidi.Network.EventNames.FetchError,
-	                params: {
-	                    ...this.#getBaseEventParams(),
-	                    errorText: event.errorText,
-	                },
-	            };
-	        });
-	    }
-	    /** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-failRequest */
-	    async failRequest(errorReason) {
-	        (0, assert_js_1$1.assert)(this.#fetchId, 'Network Interception not set-up.');
-	        this.#interceptPhase = undefined;
-	        await this.cdpClient.sendCommand('Fetch.failRequest', {
-	            requestId: this.#fetchId,
-	            errorReason,
-	        });
-	    }
-	    onRequestPaused(event) {
-	        this.#fetchId = event.requestId;
-	        // CDP https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#event-requestPaused
-	        if (event.responseStatusCode || event.responseErrorReason) {
-	            this.#response.paused = event;
-	            if (this.#isBlockedInPhase("responseStarted" /* Network.InterceptPhase.ResponseStarted */)) {
-	                this.#interceptPhase = "responseStarted" /* Network.InterceptPhase.ResponseStarted */;
-	            }
-	            else {
-	                void this.continueResponse();
-	            }
-	        }
-	        else {
-	            this.#request.paused = event;
-	            if (this.#isBlockedInPhase("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */)) {
-	                this.#interceptPhase = "beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */;
-	            }
-	            else {
-	                void this.continueRequest();
-	            }
-	        }
-	        this.#emitEventsIfReady();
-	    }
-	    onAuthRequired(event) {
-	        this.#fetchId = event.requestId;
-	        this.#request.auth = event;
-	        if (this.#isBlockedInPhase("authRequired" /* Network.InterceptPhase.AuthRequired */)) {
-	            this.#interceptPhase = "authRequired" /* Network.InterceptPhase.AuthRequired */;
-	        }
-	        else {
-	            void this.continueWithAuth();
-	        }
-	        this.#emitEvent(() => {
-	            return {
-	                method: protocol_js_1$a.ChromiumBidi.Network.EventNames.AuthRequired,
-	                params: {
-	                    ...this.#getBaseEventParams("authRequired" /* Network.InterceptPhase.AuthRequired */),
-	                    response: this.#getResponseEventParams(),
-	                },
-	            };
-	        });
-	    }
-	    /** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueRequest */
-	    async continueRequest(url, method, headers) {
-	        (0, assert_js_1$1.assert)(this.#fetchId, 'Network Interception not set-up.');
-	        this.#interceptPhase = undefined;
-	        await this.cdpClient.sendCommand('Fetch.continueRequest', {
-	            requestId: this.#fetchId,
-	            url,
-	            method,
-	            headers,
-	            // TODO: Set?
-	            // postData:,
-	            // interceptResponse:,
-	        });
-	    }
-	    /** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueResponse */
-	    async continueResponse({ responseCode, responsePhrase, responseHeaders, } = {}) {
-	        (0, assert_js_1$1.assert)(this.#fetchId, 'Network Interception not set-up.');
-	        this.#interceptPhase = undefined;
-	        await this.cdpClient.sendCommand('Fetch.continueResponse', {
-	            requestId: this.#fetchId,
-	            responseCode,
-	            responsePhrase,
-	            responseHeaders,
-	        });
-	    }
-	    /** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueWithAuth */
-	    async continueWithAuth(authChallengeResponse = {
-	        response: 'Default',
-	    }) {
-	        (0, assert_js_1$1.assert)(this.#fetchId, 'Network Interception not set-up.');
-	        this.#interceptPhase = undefined;
-	        await this.cdpClient.sendCommand('Fetch.continueWithAuth', {
-	            requestId: this.#fetchId,
-	            authChallengeResponse,
-	        });
-	    }
-	    /** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-provideResponse */
-	    async provideResponse({ responseCode, responsePhrase, responseHeaders, body, }) {
-	        (0, assert_js_1$1.assert)(this.#fetchId, 'Network Interception not set-up.');
-	        this.#interceptPhase = undefined;
-	        await this.cdpClient.sendCommand('Fetch.fulfillRequest', {
-	            requestId: this.#fetchId,
-	            responseCode,
-	            responsePhrase,
-	            responseHeaders,
-	            ...(body ? { body: btoa(body) } : {}), // TODO: Double-check if btoa usage is correct.
-	        });
-	    }
-	    get #context() {
-	        return (this.#response.paused?.frameId ??
-	            this.#request.info?.frameId ??
-	            this.#request.paused?.frameId ??
-	            this.#request.auth?.frameId ??
-	            null);
-	    }
-	    /** Returns the HTTP status code associated with this request if any. */
-	    get statusCode() {
-	        return (this.#response.paused?.responseStatusCode ??
-	            this.#response.extraInfo?.statusCode ??
-	            this.#response.info?.status ??
-	            -1 // TODO: Throw an exception or use some other status code?
-	        );
-	    }
-	    #emitEvent(getEvent) {
-	        let event;
-	        try {
-	            event = getEvent();
-	        }
-	        catch (error) {
-	            this.#logger?.(log_js_1$7.LogType.debugError, error);
-	            return;
-	        }
-	        if (this.#isIgnoredEvent() ||
-	            (this.#emittedEvents[event.method] &&
-	                // Special case this event can be emitted multiple times
-	                event.method !== protocol_js_1$a.ChromiumBidi.Network.EventNames.AuthRequired)) {
-	            return;
-	        }
-	        this.#phaseChanged();
-	        this.#emittedEvents[event.method] = true;
-	        this.#eventManager.registerEvent(Object.assign(event, {
-	            type: 'event',
-	        }), this.#context);
-	    }
-	    #getBaseEventParams(phase) {
-	        const interceptProps = {
-	            isBlocked: false,
-	        };
-	        if (phase) {
-	            const blockedBy = this.#interceptsInPhase(phase);
-	            interceptProps.isBlocked = blockedBy.size > 0;
-	            if (interceptProps.isBlocked) {
-	                interceptProps.intercepts = [...blockedBy];
-	            }
-	        }
-	        return {
-	            context: this.#context,
-	            navigation: this.#getNavigationId(),
-	            redirectCount: this.#redirectCount,
-	            request: this.#getRequestData(),
-	            // Timestamp should be in milliseconds, while CDP provides it in seconds.
-	            timestamp: Math.round((this.#request.info?.wallTime ?? 0) * 1000),
-	            // Contains isBlocked and intercepts
-	            ...interceptProps,
-	        };
-	    }
-	    #getResponseEventParams() {
-	        // Chromium sends wrong extraInfo events for responses served from cache.
-	        // See https://github.com/puppeteer/puppeteer/issues/9965 and
-	        // https://crbug.com/1340398.
-	        if (this.#response.info?.fromDiskCache) {
-	            this.#response.extraInfo = undefined;
-	        }
-	        // TODO: get headers from Fetch.requestPaused
-	        const headers = (0, NetworkUtils_js_1$2.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#response.info?.headers);
-	        const authChallenges = this.#authChallenges(this.#response.info?.headers ?? {});
-	        return {
-	            url: this.url,
-	            protocol: this.#response.info?.protocol ?? '',
-	            status: this.statusCode,
-	            statusText: this.#response.info?.statusText ||
-	                this.#response.paused?.responseStatusText ||
-	                '',
-	            fromCache: this.#response.info?.fromDiskCache ||
-	                this.#response.info?.fromPrefetchCache ||
-	                this.#servedFromCache,
-	            headers,
-	            mimeType: this.#response.info?.mimeType || '',
-	            bytesReceived: this.#response.info?.encodedDataLength || 0,
-	            headersSize: (0, NetworkUtils_js_1$2.computeHeadersSize)(headers),
-	            // TODO: consider removing from spec.
-	            bodySize: 0,
-	            content: {
-	                // TODO: consider removing from spec.
-	                size: 0,
-	            },
-	            ...(authChallenges ? { authChallenges } : {}),
-	        };
-	    }
-	    #getNavigationId() {
-	        if (!this.#request.info ||
-	            !this.#request.info.loaderId ||
-	            // When we navigate all CDP network events have `loaderId`
-	            // CDP's `loaderId` and `requestId` match when
-	            // that request triggered the loading
-	            this.#request.info.loaderId !== this.#request.info.requestId) {
-	            return null;
-	        }
-	        return this.#request.info.loaderId;
-	    }
-	    #getRequestData() {
-	        const cookies = this.#request.extraInfo
-	            ? NetworkRequest.#getCookies(this.#request.extraInfo.associatedCookies)
-	            : [];
-	        const headers = (0, NetworkUtils_js_1$2.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#request.info?.request.headers);
-	        return {
-	            request: this.#id,
-	            url: this.url,
-	            method: this.method,
-	            headers,
-	            cookies,
-	            headersSize: (0, NetworkUtils_js_1$2.computeHeadersSize)(headers),
-	            // TODO: implement.
-	            bodySize: 0,
-	            timings: this.#getTimings(),
-	        };
-	    }
-	    // TODO: implement.
-	    #getTimings() {
-	        return {
-	            timeOrigin: 0,
-	            requestTime: 0,
-	            redirectStart: 0,
-	            redirectEnd: 0,
-	            fetchStart: 0,
-	            dnsStart: 0,
-	            dnsEnd: 0,
-	            connectStart: 0,
-	            connectEnd: 0,
-	            tlsStart: 0,
-	            requestStart: 0,
-	            responseStart: 0,
-	            responseEnd: 0,
-	        };
-	    }
-	    #getBeforeRequestEvent() {
-	        (0, assert_js_1$1.assert)(this.#request.info, 'RequestWillBeSentEvent is not set');
-	        return {
-	            method: protocol_js_1$a.ChromiumBidi.Network.EventNames.BeforeRequestSent,
-	            params: {
-	                ...this.#getBaseEventParams("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */),
-	                initiator: {
-	                    type: NetworkRequest.#getInitiatorType(this.#request.info.initiator.type),
-	                },
-	            },
-	        };
-	    }
-	    #getResponseStartedEvent() {
-	        (0, assert_js_1$1.assert)(this.#request.info, 'RequestWillBeSentEvent is not set');
-	        (0, assert_js_1$1.assert)(
-	        // The response paused comes before any data for the response
-	        this.#response.paused || this.#response.info, 'ResponseReceivedEvent is not set');
-	        return {
-	            method: protocol_js_1$a.ChromiumBidi.Network.EventNames.ResponseStarted,
-	            params: {
-	                ...this.#getBaseEventParams("responseStarted" /* Network.InterceptPhase.ResponseStarted */),
-	                response: this.#getResponseEventParams(),
-	            },
-	        };
-	    }
-	    #getResponseReceivedEvent() {
-	        (0, assert_js_1$1.assert)(this.#request.info, 'RequestWillBeSentEvent is not set');
-	        (0, assert_js_1$1.assert)(this.#response.info, 'ResponseReceivedEvent is not set');
-	        return {
-	            method: protocol_js_1$a.ChromiumBidi.Network.EventNames.ResponseCompleted,
-	            params: {
-	                ...this.#getBaseEventParams(),
-	                response: this.#getResponseEventParams(),
-	            },
-	        };
-	    }
-	    #isIgnoredEvent() {
-	        const faviconUrl = '/favicon.ico';
-	        return (this.#request.paused?.request.url.endsWith(faviconUrl) ??
-	            this.#request.info?.request.url.endsWith(faviconUrl) ??
-	            false);
-	    }
-	    #authChallenges(headers) {
-	        if (!(this.statusCode === 401 || this.statusCode === 407)) {
-	            return undefined;
-	        }
-	        const headerName = this.statusCode === 401 ? 'WWW-Authenticate' : 'Proxy-Authenticate';
-	        const authChallenges = [];
-	        for (const [header, value] of Object.entries(headers)) {
-	            // TODO: Do a proper match based on https://httpwg.org/specs/rfc9110.html#credentials
-	            // Or verify this works
-	            if (header.localeCompare(headerName, undefined, { sensitivity: 'base' }) === 0) {
-	                authChallenges.push({
-	                    scheme: value.split(' ').at(0) ?? '',
-	                    realm: value.match(REALM_REGEX)?.at(0) ?? '',
-	                });
-	            }
-	        }
-	        return authChallenges;
-	    }
-	    static #getInitiatorType(initiatorType) {
-	        switch (initiatorType) {
-	            case 'parser':
-	            case 'script':
-	            case 'preflight':
-	                return initiatorType;
-	            default:
-	                return 'other';
-	        }
-	    }
-	    static #getCookies(associatedCookies) {
-	        return associatedCookies
-	            .filter(({ blockedReasons }) => {
-	            return !Array.isArray(blockedReasons) || blockedReasons.length === 0;
-	        })
-	            .map(({ cookie }) => (0, NetworkUtils_js_1$2.cdpToBiDiCookie)(cookie));
-	    }
-	}
-	NetworkRequest$1.NetworkRequest = NetworkRequest;
-
-	Object.defineProperty(NetworkStorage$1, "__esModule", { value: true });
-	NetworkStorage$1.NetworkStorage = void 0;
-	const protocol_js_1$9 = protocol;
-	const uuid_js_1$1 = uuid;
-	const NetworkRequest_js_1 = NetworkRequest$1;
-	const NetworkUtils_js_1$1 = NetworkUtils;
-	/** Stores network and intercept maps. */
-	class NetworkStorage {
-	    #eventManager;
-	    #logger;
-	    /**
-	     * A map from network request ID to Network Request objects.
-	     * Needed as long as information about requests comes from different events.
-	     */
-	    #requests = new Map();
-	    /** A map from intercept ID to track active network intercepts. */
-	    #intercepts = new Map();
-	    constructor(eventManager, browserClient, logger) {
-	        this.#eventManager = eventManager;
-	        browserClient.on('Target.detachedFromTarget', ({ sessionId }) => {
-	            this.disposeRequestMap(sessionId);
-	        });
-	        this.#logger = logger;
-	    }
-	    /**
-	     * Gets the network request with the given ID, if any.
-	     * Otherwise, creates a new network request with the given ID and cdp target.
-	     */
-	    #getOrCreateNetworkRequest(id, cdpTarget, redirectCount) {
-	        let request = this.getRequestById(id);
-	        if (request) {
-	            return request;
-	        }
-	        request = new NetworkRequest_js_1.NetworkRequest(id, this.#eventManager, this, cdpTarget, redirectCount, this.#logger);
-	        this.addRequest(request);
-	        return request;
-	    }
-	    onCdpTargetCreated(cdpTarget) {
-	        const cdpClient = cdpTarget.cdpClient;
-	        // TODO: Wrap into object
-	        const listeners = [
-	            [
-	                'Network.requestWillBeSent',
-	                (params) => {
-	                    const request = this.getRequestById(params.requestId);
-	                    if (request && request.isRedirecting()) {
-	                        request.handleRedirect(params);
-	                        this.deleteRequest(params.requestId);
-	                        this.#getOrCreateNetworkRequest(params.requestId, cdpTarget, request.redirectCount + 1).onRequestWillBeSentEvent(params);
-	                    }
-	                    else {
-	                        this.#getOrCreateNetworkRequest(params.requestId, cdpTarget).onRequestWillBeSentEvent(params);
-	                    }
-	                },
-	            ],
-	            [
-	                'Network.requestWillBeSentExtraInfo',
-	                (params) => {
-	                    this.#getOrCreateNetworkRequest(params.requestId, cdpTarget).onRequestWillBeSentExtraInfoEvent(params);
-	                },
-	            ],
-	            [
-	                'Network.responseReceived',
-	                (params) => {
-	                    this.#getOrCreateNetworkRequest(params.requestId, cdpTarget).onResponseReceivedEvent(params);
-	                },
-	            ],
-	            [
-	                'Network.responseReceivedExtraInfo',
-	                (params) => {
-	                    this.#getOrCreateNetworkRequest(params.requestId, cdpTarget).onResponseReceivedExtraInfoEvent(params);
-	                },
-	            ],
-	            [
-	                'Network.requestServedFromCache',
-	                (params) => {
-	                    this.#getOrCreateNetworkRequest(params.requestId, cdpTarget).onServedFromCache();
-	                },
-	            ],
-	            [
-	                'Network.loadingFailed',
-	                (params) => {
-	                    this.#getOrCreateNetworkRequest(params.requestId, cdpTarget).onLoadingFailedEvent(params);
-	                },
-	            ],
-	            [
-	                'Fetch.requestPaused',
-	                (event) => {
-	                    this.#getOrCreateNetworkRequest(
-	                    // CDP quirk if the Network domain is not present this is undefined
-	                    event.networkId ?? event.requestId, cdpTarget).onRequestPaused(event);
-	                },
-	            ],
-	            [
-	                'Fetch.authRequired',
-	                (event) => {
-	                    let request = this.getRequestByFetchId(event.requestId);
-	                    if (!request) {
-	                        request = this.#getOrCreateNetworkRequest(event.requestId, cdpTarget);
-	                    }
-	                    request.onAuthRequired(event);
-	                },
-	            ],
-	        ];
-	        for (const [event, listener] of listeners) {
-	            cdpClient.on(event, listener);
-	        }
-	    }
-	    getInterceptionStages(browsingContextId) {
-	        const stages = {
-	            request: false,
-	            response: false,
-	            auth: false,
-	        };
-	        for (const intercept of this.#intercepts.values()) {
-	            if (intercept.contexts &&
-	                !intercept.contexts.includes(browsingContextId)) {
-	                continue;
-	            }
-	            stages.request ||= intercept.phases.includes("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */);
-	            stages.response ||= intercept.phases.includes("responseStarted" /* Network.InterceptPhase.ResponseStarted */);
-	            stages.auth ||= intercept.phases.includes("authRequired" /* Network.InterceptPhase.AuthRequired */);
-	        }
-	        return stages;
-	    }
-	    getInterceptsForPhase(request, phase) {
-	        if (request.url === NetworkRequest_js_1.NetworkRequest.unknownParameter) {
-	            return new Set();
-	        }
-	        const intercepts = new Set();
-	        for (const [interceptId, intercept] of this.#intercepts.entries()) {
-	            if (!intercept.phases.includes(phase) ||
-	                (intercept.contexts &&
-	                    !intercept.contexts.includes(request.cdpTarget.id))) {
-	                continue;
-	            }
-	            if (intercept.urlPatterns.length === 0) {
-	                intercepts.add(interceptId);
-	                continue;
-	            }
-	            for (const pattern of intercept.urlPatterns) {
-	                if ((0, NetworkUtils_js_1$1.matchUrlPattern)(pattern, request.url)) {
-	                    intercepts.add(interceptId);
-	                    break;
-	                }
-	            }
-	        }
-	        return intercepts;
-	    }
-	    disposeRequestMap(sessionId) {
-	        for (const request of this.#requests.values()) {
-	            if (request.cdpClient.sessionId === sessionId) {
-	                this.#requests.delete(request.id);
-	            }
-	        }
-	    }
-	    /**
-	     * Adds the given entry to the intercept map.
-	     * URL patterns are assumed to be parsed.
-	     *
-	     * @return The intercept ID.
-	     */
-	    addIntercept(value) {
-	        const interceptId = (0, uuid_js_1$1.uuidv4)();
-	        this.#intercepts.set(interceptId, value);
-	        return interceptId;
-	    }
-	    /**
-	     * Removes the given intercept from the intercept map.
-	     * Throws NoSuchInterceptException if the intercept does not exist.
-	     */
-	    removeIntercept(intercept) {
-	        if (!this.#intercepts.has(intercept)) {
-	            throw new protocol_js_1$9.NoSuchInterceptException(`Intercept '${intercept}' does not exist.`);
-	        }
-	        this.#intercepts.delete(intercept);
-	    }
-	    getRequestById(id) {
-	        return this.#requests.get(id);
-	    }
-	    getRequestByFetchId(fetchId) {
-	        for (const request of this.#requests.values()) {
-	            if (request.fetchId === fetchId) {
-	                return request;
-	            }
-	        }
-	        return;
-	    }
-	    addRequest(request) {
-	        this.#requests.set(request.id, request);
-	    }
-	    deleteRequest(id) {
-	        this.#requests.delete(id);
-	    }
-	}
-	NetworkStorage$1.NetworkStorage = NetworkStorage;
-
-	var PermissionsProcessor$1 = {};
-
-	/**
-	 * Copyright 2024 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(PermissionsProcessor$1, "__esModule", { value: true });
-	PermissionsProcessor$1.PermissionsProcessor = void 0;
-	const protocol_js_1$8 = protocol;
-	class PermissionsProcessor {
-	    #browserCdpClient;
-	    constructor(browserCdpClient) {
-	        this.#browserCdpClient = browserCdpClient;
-	    }
-	    async setPermissions(params) {
-	        try {
-	            const userContextId = params['goog:userContext'];
-	            await this.#browserCdpClient.sendCommand('Browser.setPermission', {
-	                origin: params.origin,
-	                browserContextId: userContextId && userContextId !== 'default'
-	                    ? userContextId
-	                    : undefined,
-	                permission: {
-	                    name: params.descriptor.name,
-	                },
-	                setting: params.state,
-	            });
-	        }
-	        catch (err) {
-	            if (err.message ===
-	                `Permission can't be granted to opaque origins.`) {
-	                // Return success if the origin is not valid (does not match any
-	                // existing origins).
-	                return {};
-	            }
-	            throw new protocol_js_1$8.InvalidArgumentException(err.message);
-	        }
-	        return {};
-	    }
-	}
-	PermissionsProcessor$1.PermissionsProcessor = PermissionsProcessor;
-
-	var PreloadScriptStorage$1 = {};
-
-	Object.defineProperty(PreloadScriptStorage$1, "__esModule", { value: true });
-	PreloadScriptStorage$1.PreloadScriptStorage = void 0;
-	/**
-	 * Container class for preload scripts.
-	 */
-	class PreloadScriptStorage {
-	    /** Tracks all BiDi preload scripts.  */
-	    #scripts = new Set();
-	    /** Finds all entries that match the given filter. */
-	    find(filter) {
-	        if (!filter) {
-	            return [...this.#scripts];
-	        }
-	        return [...this.#scripts].filter((script) => {
-	            if (filter.id !== undefined && filter.id !== script.id) {
-	                return false;
-	            }
-	            if (filter.targetId !== undefined &&
-	                !script.targetIds.has(filter.targetId)) {
-	                return false;
-	            }
-	            if (filter.global !== undefined &&
-	                // Global scripts have no contexts
-	                ((filter.global && script.contexts !== undefined) ||
-	                    // Non global scripts always have contexts
-	                    (!filter.global && script.contexts === undefined))) {
-	                return false;
-	            }
-	            return true;
-	        });
-	    }
-	    add(preloadScript) {
-	        this.#scripts.add(preloadScript);
-	    }
-	    /** Deletes all BiDi preload script entries that match the given filter. */
-	    remove(filter) {
-	        for (const preloadScript of this.find(filter)) {
-	            this.#scripts.delete(preloadScript);
-	        }
-	    }
-	}
-	PreloadScriptStorage$1.PreloadScriptStorage = PreloadScriptStorage;
-
-	var ScriptProcessor$1 = {};
-
-	var PreloadScript$1 = {};
-
-	/*
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 *
-	 */
-	Object.defineProperty(PreloadScript$1, "__esModule", { value: true });
-	PreloadScript$1.PreloadScript = void 0;
-	const uuid_js_1 = uuid;
-	const ChannelProxy_js_1 = ChannelProxy$1;
-	/**
-	 * BiDi IDs are generated by the server and are unique within contexts.
-	 *
-	 * CDP preload script IDs are generated by the client and are unique
-	 * within sessions.
-	 *
-	 * The mapping between BiDi and CDP preload script IDs is 1:many.
-	 * BiDi IDs are needed by the mapper to keep track of potential multiple CDP IDs
-	 * in the client.
-	 */
-	class PreloadScript {
-	    /** BiDi ID, an automatically generated UUID. */
-	    #id = (0, uuid_js_1.uuidv4)();
-	    /** CDP preload scripts. */
-	    #cdpPreloadScripts = [];
-	    /** The script itself, in a format expected by the spec i.e. a function. */
-	    #functionDeclaration;
-	    /** Targets, in which the preload script is initialized. */
-	    #targetIds = new Set();
-	    /** Channels to be added as arguments to functionDeclaration. */
-	    #channels;
-	    /** The script sandbox / world name. */
-	    #sandbox;
-	    /** The browsing contexts to execute the preload scripts in, if any. */
-	    #contexts;
-	    get id() {
-	        return this.#id;
-	    }
-	    get targetIds() {
-	        return this.#targetIds;
-	    }
-	    constructor(params, logger) {
-	        this.#channels =
-	            params.arguments?.map((a) => new ChannelProxy_js_1.ChannelProxy(a.value, logger)) ?? [];
-	        this.#functionDeclaration = params.functionDeclaration;
-	        this.#sandbox = params.sandbox;
-	        this.#contexts = params.contexts;
-	    }
-	    /** Channels of the preload script. */
-	    get channels() {
-	        return this.#channels;
-	    }
-	    /** Contexts of the preload script, if any */
-	    get contexts() {
-	        return this.#contexts;
-	    }
-	    /**
-	     * String to be evaluated. Wraps user-provided function so that the following
-	     * steps are run:
-	     * 1. Create channels.
-	     * 2. Store the created channels in window.
-	     * 3. Call the user-provided function with channels as arguments.
-	     */
-	    #getEvaluateString() {
-	        const channelsArgStr = `[${this.channels
-            .map((c) => c.getEvalInWindowStr())
-            .join(', ')}]`;
-	        return `(()=>{(${this.#functionDeclaration})(...${channelsArgStr})})()`;
-	    }
-	    /**
-	     * Adds the script to the given CDP targets by calling the
-	     * `Page.addScriptToEvaluateOnNewDocument` command.
-	     */
-	    async initInTargets(cdpTargets, runImmediately) {
-	        await Promise.all(Array.from(cdpTargets).map((cdpTarget) => this.initInTarget(cdpTarget, runImmediately)));
-	    }
-	    /**
-	     * Adds the script to the given CDP target by calling the
-	     * `Page.addScriptToEvaluateOnNewDocument` command.
-	     */
-	    async initInTarget(cdpTarget, runImmediately) {
-	        const addCdpPreloadScriptResult = await cdpTarget.cdpClient.sendCommand('Page.addScriptToEvaluateOnNewDocument', {
-	            source: this.#getEvaluateString(),
-	            worldName: this.#sandbox,
-	            runImmediately,
-	        });
-	        this.#cdpPreloadScripts.push({
-	            target: cdpTarget,
-	            preloadScriptId: addCdpPreloadScriptResult.identifier,
-	        });
-	        this.#targetIds.add(cdpTarget.id);
-	    }
-	    /**
-	     * Removes this script from all CDP targets.
-	     */
-	    async remove() {
-	        for (const cdpPreloadScript of this.#cdpPreloadScripts) {
-	            const cdpTarget = cdpPreloadScript.target;
-	            const cdpPreloadScriptId = cdpPreloadScript.preloadScriptId;
-	            await cdpTarget.cdpClient.sendCommand('Page.removeScriptToEvaluateOnNewDocument', {
-	                identifier: cdpPreloadScriptId,
-	            });
-	        }
-	    }
-	    /** Removes the provided cdp target from the list of cdp preload scripts. */
-	    dispose(cdpTargetId) {
-	        this.#cdpPreloadScripts = this.#cdpPreloadScripts.filter((cdpPreloadScript) => cdpPreloadScript.target?.id !== cdpTargetId);
-	        this.#targetIds.delete(cdpTargetId);
-	    }
-	}
-	PreloadScript$1.PreloadScript = PreloadScript;
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(ScriptProcessor$1, "__esModule", { value: true });
-	ScriptProcessor$1.ScriptProcessor = void 0;
-	const protocol_1 = protocol;
-	const PreloadScript_1 = PreloadScript$1;
-	class ScriptProcessor {
-	    #browsingContextStorage;
-	    #realmStorage;
-	    #preloadScriptStorage;
-	    #logger;
-	    constructor(browsingContextStorage, realmStorage, preloadScriptStorage, logger) {
-	        this.#browsingContextStorage = browsingContextStorage;
-	        this.#realmStorage = realmStorage;
-	        this.#preloadScriptStorage = preloadScriptStorage;
-	        this.#logger = logger;
-	    }
-	    async addPreloadScript(params) {
-	        const contexts = this.#browsingContextStorage.verifyContextsList(params.contexts);
-	        const preloadScript = new PreloadScript_1.PreloadScript(params, this.#logger);
-	        this.#preloadScriptStorage.add(preloadScript);
-	        const cdpTargets = contexts.size === 0
-	            ? new Set(this.#browsingContextStorage
-	                .getTopLevelContexts()
-	                .map((context) => context.cdpTarget))
-	            : new Set([...contexts.values()].map((context) => context.cdpTarget));
-	        await preloadScript.initInTargets(cdpTargets, false);
-	        return {
-	            script: preloadScript.id,
-	        };
-	    }
-	    async removePreloadScript(params) {
-	        const bidiId = params.script;
-	        const scripts = this.#preloadScriptStorage.find({
-	            id: bidiId,
-	        });
-	        if (scripts.length === 0) {
-	            throw new protocol_1.NoSuchScriptException(`No preload script with BiDi ID '${bidiId}'`);
-	        }
-	        await Promise.all(scripts.map((script) => script.remove()));
-	        this.#preloadScriptStorage.remove({
-	            id: bidiId,
-	        });
-	        return {};
-	    }
-	    async callFunction(params) {
-	        const realm = await this.#getRealm(params.target);
-	        return await realm.callFunction(params.functionDeclaration, params.this ?? {
-	            type: 'undefined',
-	        }, // `this` is `undefined` by default.
-	        params.arguments ?? [], // `arguments` is `[]` by default.
-	        params.awaitPromise, params.resultOwnership ?? "none" /* Script.ResultOwnership.None */, params.serializationOptions ?? {}, params.userActivation ?? false);
-	    }
-	    async evaluate(params) {
-	        const realm = await this.#getRealm(params.target);
-	        return await realm.evaluate(params.expression, params.awaitPromise, params.resultOwnership ?? "none" /* Script.ResultOwnership.None */, params.serializationOptions ?? {}, params.userActivation ?? false);
-	    }
-	    async disown(params) {
-	        const realm = await this.#getRealm(params.target);
-	        await Promise.all(params.handles.map(async (handle) => await realm.disown(handle)));
-	        return {};
-	    }
-	    getRealms(params) {
-	        if (params.context !== undefined) {
-	            // Make sure the context is known.
-	            this.#browsingContextStorage.getContext(params.context);
-	        }
-	        const realms = this.#realmStorage
-	            .findRealms({
-	            browsingContextId: params.context,
-	            type: params.type,
-	        })
-	            .map((realm) => realm.realmInfo);
-	        return { realms };
-	    }
-	    async #getRealm(target) {
-	        if ('context' in target) {
-	            const context = this.#browsingContextStorage.getContext(target.context);
-	            return await context.getOrCreateSandbox(target.sandbox);
-	        }
-	        return this.#realmStorage.getRealm({
-	            realmId: target.realm,
-	        });
-	    }
-	}
-	ScriptProcessor$1.ScriptProcessor = ScriptProcessor;
-
-	var SessionProcessor$1 = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(SessionProcessor$1, "__esModule", { value: true });
-	SessionProcessor$1.SessionProcessor = void 0;
-	class SessionProcessor {
-	    #eventManager;
-	    constructor(eventManager) {
-	        this.#eventManager = eventManager;
-	    }
-	    status() {
-	        return { ready: false, message: 'already connected' };
-	    }
-	    async subscribe(params, channel = null) {
-	        await this.#eventManager.subscribe(params.events, params.contexts ?? [null], channel);
-	        return {};
-	    }
-	    async unsubscribe(params, channel = null) {
-	        await this.#eventManager.unsubscribe(params.events, params.contexts ?? [null], channel);
-	        return {};
-	    }
-	}
-	SessionProcessor$1.SessionProcessor = SessionProcessor;
-
-	var StorageProcessor$1 = {};
-
-	Object.defineProperty(StorageProcessor$1, "__esModule", { value: true });
-	StorageProcessor$1.StorageProcessor = void 0;
-	const protocol_js_1$7 = protocol;
-	const assert_js_1 = assert$1;
-	const log_js_1$6 = log$1;
-	const NetworkProcessor_js_1$1 = NetworkProcessor$1;
-	const NetworkUtils_js_1 = NetworkUtils;
-	/**
-	 * Responsible for handling the `storage` domain.
-	 */
-	class StorageProcessor {
-	    #browserCdpClient;
-	    #browsingContextStorage;
-	    #logger;
-	    constructor(browserCdpClient, browsingContextStorage, logger) {
-	        this.#browsingContextStorage = browsingContextStorage;
-	        this.#browserCdpClient = browserCdpClient;
-	        this.#logger = logger;
-	    }
-	    async deleteCookies(params) {
-	        const partitionKey = this.#expandStoragePartitionSpec(params.partition);
-	        let cdpResponse;
-	        try {
-	            cdpResponse = await this.#browserCdpClient.sendCommand('Storage.getCookies', {
-	                browserContextId: this.#getCdpBrowserContextId(partitionKey),
-	            });
-	        }
-	        catch (err) {
-	            if (this.#isNoSuchUserContextError(err)) {
-	                // If the user context is not found, special error is thrown.
-	                throw new protocol_js_1$7.NoSuchUserContextException(err.message);
-	            }
-	            throw err;
-	        }
-	        const cdpCookiesToDelete = cdpResponse.cookies
-	            .filter(
-	        // CDP's partition key is the source origin. If the request specifies the
-	        // `sourceOrigin` partition key, only cookies with the requested source origin
-	        // are returned.
-	        (c) => partitionKey.sourceOrigin === undefined ||
-	            c.partitionKey === partitionKey.sourceOrigin)
-	            .filter((cdpCookie) => {
-	            const bidiCookie = (0, NetworkUtils_js_1.cdpToBiDiCookie)(cdpCookie);
-	            return this.#matchCookie(bidiCookie, params.filter);
-	        })
-	            .map((cookie) => ({
-	            ...cookie,
-	            // Set expiry to pass date to delete the cookie.
-	            expires: 1,
-	        }));
-	        await this.#browserCdpClient.sendCommand('Storage.setCookies', {
-	            cookies: cdpCookiesToDelete,
-	            browserContextId: this.#getCdpBrowserContextId(partitionKey),
-	        });
-	        return {
-	            partitionKey,
-	        };
-	    }
-	    async getCookies(params) {
-	        const partitionKey = this.#expandStoragePartitionSpec(params.partition);
-	        let cdpResponse;
-	        try {
-	            cdpResponse = await this.#browserCdpClient.sendCommand('Storage.getCookies', {
-	                browserContextId: this.#getCdpBrowserContextId(partitionKey),
-	            });
-	        }
-	        catch (err) {
-	            if (this.#isNoSuchUserContextError(err)) {
-	                // If the user context is not found, special error is thrown.
-	                throw new protocol_js_1$7.NoSuchUserContextException(err.message);
-	            }
-	            throw err;
-	        }
-	        const filteredBiDiCookies = cdpResponse.cookies
-	            .filter(
-	        // CDP's partition key is the source origin. If the request specifies the
-	        // `sourceOrigin` partition key, only cookies with the requested source origin
-	        // are returned.
-	        (c) => partitionKey.sourceOrigin === undefined ||
-	            c.partitionKey === partitionKey.sourceOrigin)
-	            .map((c) => (0, NetworkUtils_js_1.cdpToBiDiCookie)(c))
-	            .filter((c) => this.#matchCookie(c, params.filter));
-	        return {
-	            cookies: filteredBiDiCookies,
-	            partitionKey,
-	        };
-	    }
-	    async setCookie(params) {
-	        const partitionKey = this.#expandStoragePartitionSpec(params.partition);
-	        const cdpCookie = (0, NetworkUtils_js_1.bidiToCdpCookie)(params, partitionKey);
-	        try {
-	            await this.#browserCdpClient.sendCommand('Storage.setCookies', {
-	                cookies: [cdpCookie],
-	                browserContextId: this.#getCdpBrowserContextId(partitionKey),
-	            });
-	        }
-	        catch (err) {
-	            if (this.#isNoSuchUserContextError(err)) {
-	                // If the user context is not found, special error is thrown.
-	                throw new protocol_js_1$7.NoSuchUserContextException(err.message);
-	            }
-	            this.#logger?.(log_js_1$6.LogType.debugError, err);
-	            throw new protocol_js_1$7.UnableToSetCookieException(err.toString());
-	        }
-	        return {
-	            partitionKey,
-	        };
-	    }
-	    #isNoSuchUserContextError(err) {
-	        // Heuristic to detect if the user context is not found.
-	        // See https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/protocol/browser_handler.cc;drc=a56154dd81e4679712422ac6eed2c9581cb51ab0;l=314
-	        return err.message?.startsWith('Failed to find browser context for id');
-	    }
-	    #getCdpBrowserContextId(partitionKey) {
-	        return partitionKey.userContext === 'default'
-	            ? undefined
-	            : partitionKey.userContext;
-	    }
-	    #expandStoragePartitionSpecByBrowsingContext(descriptor) {
-	        const browsingContextId = descriptor.context;
-	        const browsingContext = this.#browsingContextStorage.getContext(browsingContextId);
-	        // https://w3c.github.io/webdriver-bidi/#associated-storage-partition.
-	        // Each browsing context also has an associated storage partition, which is the
-	        // storage partition it uses to persist data. In Chromium it's a `BrowserContext`
-	        // which maps to BiDi `UserContext`.
-	        return {
-	            userContext: browsingContext.userContext,
-	        };
-	    }
-	    #expandStoragePartitionSpecByStorageKey(descriptor) {
-	        const unsupportedPartitionKeys = new Map();
-	        let sourceOrigin = descriptor.sourceOrigin;
-	        if (sourceOrigin !== undefined) {
-	            const url = NetworkProcessor_js_1$1.NetworkProcessor.parseUrlString(sourceOrigin);
-	            if (url.origin === 'null') {
-	                // Origin `null` is a special case for local pages.
-	                sourceOrigin = url.origin;
-	            }
-	            else {
-	                // Port is not supported in CDP Cookie's `partitionKey`, so it should be stripped
-	                // from the requested source origin.
-	                sourceOrigin = `${url.protocol}//${url.hostname}`;
-	            }
-	        }
-	        for (const [key, value] of Object.entries(descriptor)) {
-	            if (key !== undefined &&
-	                value !== undefined &&
-	                !['type', 'sourceOrigin', 'userContext'].includes(key)) {
-	                unsupportedPartitionKeys.set(key, value);
-	            }
-	        }
-	        if (unsupportedPartitionKeys.size > 0) {
-	            this.#logger?.(log_js_1$6.LogType.debugInfo, `Unsupported partition keys: ${JSON.stringify(Object.fromEntries(unsupportedPartitionKeys))}`);
-	        }
-	        // Set `userContext` to `default` if not provided, as it's required in Chromium.
-	        const userContext = descriptor.userContext ?? 'default';
-	        return {
-	            userContext,
-	            ...(sourceOrigin === undefined ? {} : { sourceOrigin }),
-	        };
-	    }
-	    #expandStoragePartitionSpec(partitionSpec) {
-	        if (partitionSpec === undefined) {
-	            // `userContext` is required in Chromium.
-	            return { userContext: 'default' };
-	        }
-	        if (partitionSpec.type === 'context') {
-	            return this.#expandStoragePartitionSpecByBrowsingContext(partitionSpec);
-	        }
-	        (0, assert_js_1.assert)(partitionSpec.type === 'storageKey', 'Unknown partition type');
-	        // Partition spec is a storage partition.
-	        // Let partition key be partition spec.
-	        return this.#expandStoragePartitionSpecByStorageKey(partitionSpec);
-	    }
-	    #matchCookie(cookie, filter) {
-	        if (filter === undefined) {
-	            return true;
-	        }
-	        return ((filter.domain === undefined || filter.domain === cookie.domain) &&
-	            (filter.name === undefined || filter.name === cookie.name) &&
-	            // `value` contains fields `type` and `value`.
-	            (filter.value === undefined ||
-	                (0, NetworkUtils_js_1.deserializeByteValue)(filter.value) ===
-	                    (0, NetworkUtils_js_1.deserializeByteValue)(cookie.value)) &&
-	            (filter.path === undefined || filter.path === cookie.path) &&
-	            (filter.size === undefined || filter.size === cookie.size) &&
-	            (filter.httpOnly === undefined || filter.httpOnly === cookie.httpOnly) &&
-	            (filter.secure === undefined || filter.secure === cookie.secure) &&
-	            (filter.sameSite === undefined || filter.sameSite === cookie.sameSite) &&
-	            (filter.expiry === undefined || filter.expiry === cookie.expiry));
-	    }
-	}
-	StorageProcessor$1.StorageProcessor = StorageProcessor;
-
-	var OutgoingMessage$1 = {};
-
-	/**
-	 * Copyright 2021 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(OutgoingMessage$1, "__esModule", { value: true });
-	OutgoingMessage$1.OutgoingMessage = void 0;
-	class OutgoingMessage {
-	    #message;
-	    #channel;
-	    constructor(message, channel = null) {
-	        this.#message = message;
-	        this.#channel = channel;
-	    }
-	    static createFromPromise(messagePromise, channel) {
-	        return messagePromise.then((message) => {
-	            if (message.kind === 'success') {
-	                return {
-	                    kind: 'success',
-	                    value: new OutgoingMessage(message.value, channel),
-	                };
-	            }
-	            return message;
-	        });
-	    }
-	    static createResolved(message, channel) {
-	        return Promise.resolve({
-	            kind: 'success',
-	            value: new OutgoingMessage(message, channel),
-	        });
-	    }
-	    get message() {
-	        return this.#message;
-	    }
-	    get channel() {
-	        return this.#channel;
-	    }
-	}
-	OutgoingMessage$1.OutgoingMessage = OutgoingMessage;
-
-	/**
-	 * Copyright 2021 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(CommandProcessor$1, "__esModule", { value: true });
-	CommandProcessor$1.CommandProcessor = void 0;
-	const protocol_js_1$6 = protocol;
-	const EventEmitter_js_1$3 = EventEmitter$1;
-	const log_js_1$5 = log$1;
-	const BidiNoOpParser_js_1 = BidiNoOpParser$1;
-	const BrowserProcessor_js_1 = BrowserProcessor$1;
-	const CdpProcessor_js_1 = CdpProcessor$1;
-	const BrowsingContextProcessor_js_1 = BrowsingContextProcessor$1;
-	const InputProcessor_js_1 = InputProcessor$1;
-	const NetworkProcessor_js_1 = NetworkProcessor$1;
-	const NetworkStorage_js_1 = NetworkStorage$1;
-	const PermissionsProcessor_js_1 = PermissionsProcessor$1;
-	const PreloadScriptStorage_js_1 = PreloadScriptStorage$1;
-	const ScriptProcessor_js_1 = ScriptProcessor$1;
-	const SessionProcessor_js_1 = SessionProcessor$1;
-	const StorageProcessor_js_1 = StorageProcessor$1;
-	const OutgoingMessage_js_1$1 = OutgoingMessage$1;
-	class CommandProcessor extends EventEmitter_js_1$3.EventEmitter {
-	    // keep-sorted start
-	    #browserProcessor;
-	    #browsingContextProcessor;
-	    #cdpProcessor;
-	    #inputProcessor;
-	    #networkProcessor;
-	    #permissionsProcessor;
-	    #scriptProcessor;
-	    #sessionProcessor;
-	    #storageProcessor;
-	    // keep-sorted end
-	    #parser;
-	    #logger;
-	    constructor(cdpConnection, browserCdpClient, eventManager, selfTargetId, defaultUserContextId, browsingContextStorage, realmStorage, acceptInsecureCerts, sharedIdWithFrame, parser = new BidiNoOpParser_js_1.BidiNoOpParser(), logger) {
-	        super();
-	        this.#parser = parser;
-	        this.#logger = logger;
-	        const networkStorage = new NetworkStorage_js_1.NetworkStorage(eventManager, browserCdpClient, logger);
-	        const preloadScriptStorage = new PreloadScriptStorage_js_1.PreloadScriptStorage();
-	        // keep-sorted start block=yes
-	        this.#browserProcessor = new BrowserProcessor_js_1.BrowserProcessor(browserCdpClient);
-	        this.#browsingContextProcessor = new BrowsingContextProcessor_js_1.BrowsingContextProcessor(cdpConnection, browserCdpClient, selfTargetId, eventManager, browsingContextStorage, realmStorage, networkStorage, preloadScriptStorage, acceptInsecureCerts, sharedIdWithFrame, defaultUserContextId, logger);
-	        this.#cdpProcessor = new CdpProcessor_js_1.CdpProcessor(browsingContextStorage, realmStorage, cdpConnection, browserCdpClient);
-	        this.#inputProcessor = new InputProcessor_js_1.InputProcessor(browsingContextStorage, realmStorage);
-	        this.#networkProcessor = new NetworkProcessor_js_1.NetworkProcessor(browsingContextStorage, networkStorage);
-	        this.#permissionsProcessor = new PermissionsProcessor_js_1.PermissionsProcessor(browserCdpClient);
-	        this.#scriptProcessor = new ScriptProcessor_js_1.ScriptProcessor(browsingContextStorage, realmStorage, preloadScriptStorage, logger);
-	        this.#sessionProcessor = new SessionProcessor_js_1.SessionProcessor(eventManager);
-	        this.#storageProcessor = new StorageProcessor_js_1.StorageProcessor(browserCdpClient, browsingContextStorage, logger);
-	        // keep-sorted end
-	    }
-	    async #processCommand(command) {
-	        switch (command.method) {
-	            case 'session.end':
-	            case 'session.new':
-	                // TODO: Implement.
-	                break;
-	            // Browser domain
-	            // keep-sorted start block=yes
-	            case 'browser.close':
-	                return this.#browserProcessor.close();
-	            case 'browser.createUserContext':
-	                return await this.#browserProcessor.createUserContext(command.params);
-	            case 'browser.getUserContexts':
-	                return await this.#browserProcessor.getUserContexts();
-	            case 'browser.removeUserContext':
-	                return await this.#browserProcessor.removeUserContext(this.#parser.parseRemoveUserContextParams(command.params));
-	            // keep-sorted end
-	            // Browsing Context domain
-	            // keep-sorted start block=yes
-	            case 'browsingContext.activate':
-	                return await this.#browsingContextProcessor.activate(this.#parser.parseActivateParams(command.params));
-	            case 'browsingContext.captureScreenshot':
-	                return await this.#browsingContextProcessor.captureScreenshot(this.#parser.parseCaptureScreenshotParams(command.params));
-	            case 'browsingContext.close':
-	                return await this.#browsingContextProcessor.close(this.#parser.parseCloseParams(command.params));
-	            case 'browsingContext.create':
-	                return await this.#browsingContextProcessor.create(this.#parser.parseCreateParams(command.params));
-	            case 'browsingContext.getTree':
-	                return this.#browsingContextProcessor.getTree(this.#parser.parseGetTreeParams(command.params));
-	            case 'browsingContext.handleUserPrompt':
-	                return await this.#browsingContextProcessor.handleUserPrompt(this.#parser.parseHandleUserPromptParams(command.params));
-	            case 'browsingContext.locateNodes':
-	                return await this.#browsingContextProcessor.locateNodes(this.#parser.parseLocateNodesParams(command.params));
-	            case 'browsingContext.navigate':
-	                return await this.#browsingContextProcessor.navigate(this.#parser.parseNavigateParams(command.params));
-	            case 'browsingContext.print':
-	                return await this.#browsingContextProcessor.print(this.#parser.parsePrintParams(command.params));
-	            case 'browsingContext.reload':
-	                return await this.#browsingContextProcessor.reload(this.#parser.parseReloadParams(command.params));
-	            case 'browsingContext.setViewport':
-	                return await this.#browsingContextProcessor.setViewport(this.#parser.parseSetViewportParams(command.params));
-	            case 'browsingContext.traverseHistory':
-	                return await this.#browsingContextProcessor.traverseHistory(this.#parser.parseTraverseHistoryParams(command.params));
-	            // keep-sorted end
-	            // CDP domain
-	            // keep-sorted start block=yes
-	            case 'cdp.getSession':
-	                return this.#cdpProcessor.getSession(this.#parser.parseGetSessionParams(command.params));
-	            case 'cdp.resolveRealm':
-	                return this.#cdpProcessor.resolveRealm(this.#parser.parseResolveRealmParams(command.params));
-	            case 'cdp.sendCommand':
-	                return await this.#cdpProcessor.sendCommand(this.#parser.parseSendCommandParams(command.params));
-	            // keep-sorted end
-	            // Input domain
-	            // keep-sorted start block=yes
-	            case 'input.performActions':
-	                return await this.#inputProcessor.performActions(this.#parser.parsePerformActionsParams(command.params));
-	            case 'input.releaseActions':
-	                return await this.#inputProcessor.releaseActions(this.#parser.parseReleaseActionsParams(command.params));
-	            case 'input.setFiles':
-	                return await this.#inputProcessor.setFiles(this.#parser.parseSetFilesParams(command.params));
-	            // keep-sorted end
-	            // Network domain
-	            // keep-sorted start block=yes
-	            case 'network.addIntercept':
-	                return await this.#networkProcessor.addIntercept(this.#parser.parseAddInterceptParams(command.params));
-	            case 'network.continueRequest':
-	                return await this.#networkProcessor.continueRequest(this.#parser.parseContinueRequestParams(command.params));
-	            case 'network.continueResponse':
-	                return await this.#networkProcessor.continueResponse(this.#parser.parseContinueResponseParams(command.params));
-	            case 'network.continueWithAuth':
-	                return await this.#networkProcessor.continueWithAuth(this.#parser.parseContinueWithAuthParams(command.params));
-	            case 'network.failRequest':
-	                return await this.#networkProcessor.failRequest(this.#parser.parseFailRequestParams(command.params));
-	            case 'network.provideResponse':
-	                return await this.#networkProcessor.provideResponse(this.#parser.parseProvideResponseParams(command.params));
-	            case 'network.removeIntercept':
-	                return await this.#networkProcessor.removeIntercept(this.#parser.parseRemoveInterceptParams(command.params));
-	            // keep-sorted end
-	            // Permissions domain
-	            // keep-sorted start block=yes
-	            case 'permissions.setPermission':
-	                return await this.#permissionsProcessor.setPermissions(this.#parser.parseSetPermissionsParams(command.params));
-	            // keep-sorted end
-	            // Script domain
-	            // keep-sorted start block=yes
-	            case 'script.addPreloadScript':
-	                return await this.#scriptProcessor.addPreloadScript(this.#parser.parseAddPreloadScriptParams(command.params));
-	            case 'script.callFunction':
-	                return await this.#scriptProcessor.callFunction(this.#parser.parseCallFunctionParams(this.#processTargetParams(command.params)));
-	            case 'script.disown':
-	                return await this.#scriptProcessor.disown(this.#parser.parseDisownParams(this.#processTargetParams(command.params)));
-	            case 'script.evaluate':
-	                return await this.#scriptProcessor.evaluate(this.#parser.parseEvaluateParams(this.#processTargetParams(command.params)));
-	            case 'script.getRealms':
-	                return this.#scriptProcessor.getRealms(this.#parser.parseGetRealmsParams(command.params));
-	            case 'script.removePreloadScript':
-	                return await this.#scriptProcessor.removePreloadScript(this.#parser.parseRemovePreloadScriptParams(command.params));
-	            // keep-sorted end
-	            // Session domain
-	            // keep-sorted start block=yes
-	            case 'session.status':
-	                return this.#sessionProcessor.status();
-	            case 'session.subscribe':
-	                return await this.#sessionProcessor.subscribe(this.#parser.parseSubscribeParams(command.params), command.channel);
-	            case 'session.unsubscribe':
-	                return await this.#sessionProcessor.unsubscribe(this.#parser.parseSubscribeParams(command.params), command.channel);
-	            // keep-sorted end
-	            // Storage domain
-	            // keep-sorted start block=yes
-	            case 'storage.deleteCookies':
-	                return await this.#storageProcessor.deleteCookies(this.#parser.parseDeleteCookiesParams(command.params));
-	            case 'storage.getCookies':
-	                return await this.#storageProcessor.getCookies(this.#parser.parseGetCookiesParams(command.params));
-	            case 'storage.setCookie':
-	                return await this.#storageProcessor.setCookie(this.#parser.parseSetCookieParams(command.params));
-	            // keep-sorted end
-	        }
-	        // Intentionally kept outside the switch statement to ensure that
-	        // ESLint @typescript-eslint/switch-exhaustiveness-check triggers if a new
-	        // command is added.
-	        throw new protocol_js_1$6.UnknownCommandException(`Unknown command '${command.method}'.`);
-	    }
-	    // Workaround for as zod.union always take the first schema
-	    // https://github.com/w3c/webdriver-bidi/issues/635
-	    #processTargetParams(params) {
-	        if (typeof params === 'object' &&
-	            params &&
-	            'target' in params &&
-	            typeof params.target === 'object' &&
-	            params.target &&
-	            'context' in params.target) {
-	            delete params.target['realm'];
-	        }
-	        return params;
-	    }
-	    async processCommand(command) {
-	        try {
-	            const result = await this.#processCommand(command);
-	            const response = {
-	                type: 'success',
-	                id: command.id,
-	                result,
-	            };
-	            this.emit("response" /* CommandProcessorEvents.Response */, {
-	                message: OutgoingMessage_js_1$1.OutgoingMessage.createResolved(response, command.channel),
-	                event: command.method,
-	            });
-	        }
-	        catch (e) {
-	            if (e instanceof protocol_js_1$6.Exception) {
-	                this.emit("response" /* CommandProcessorEvents.Response */, {
-	                    message: OutgoingMessage_js_1$1.OutgoingMessage.createResolved(e.toErrorResponse(command.id), command.channel),
-	                    event: command.method,
-	                });
-	            }
-	            else {
-	                const error = e;
-	                this.#logger?.(log_js_1$5.LogType.bidi, error);
-	                this.emit("response" /* CommandProcessorEvents.Response */, {
-	                    message: OutgoingMessage_js_1$1.OutgoingMessage.createResolved(new protocol_js_1$6.UnknownErrorException(error.message, error.stack).toErrorResponse(command.id), command.channel),
-	                    event: command.method,
-	                });
-	            }
-	        }
-	    }
-	}
-	CommandProcessor$1.CommandProcessor = CommandProcessor;
-
-	var BrowsingContextStorage$1 = {};
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(BrowsingContextStorage$1, "__esModule", { value: true });
-	BrowsingContextStorage$1.BrowsingContextStorage = void 0;
-	const protocol_js_1$5 = protocol;
-	/** Container class for browsing contexts. */
-	class BrowsingContextStorage {
-	    /** Map from context ID to context implementation. */
-	    #contexts = new Map();
-	    /** Gets all top-level contexts, i.e. those with no parent. */
-	    getTopLevelContexts() {
-	        return this.getAllContexts().filter((context) => context.isTopLevelContext());
-	    }
-	    /** Gets all contexts. */
-	    getAllContexts() {
-	        return Array.from(this.#contexts.values());
-	    }
-	    /** Deletes the context with the given ID. */
-	    deleteContextById(id) {
-	        this.#contexts.delete(id);
-	    }
-	    /** Deletes the given context. */
-	    deleteContext(context) {
-	        this.#contexts.delete(context.id);
-	    }
-	    /** Tracks the given context. */
-	    addContext(context) {
-	        this.#contexts.set(context.id, context);
-	    }
-	    /** Returns true whether there is an existing context with the given ID. */
-	    hasContext(id) {
-	        return this.#contexts.has(id);
-	    }
-	    /** Gets the context with the given ID, if any. */
-	    findContext(id) {
-	        return this.#contexts.get(id);
-	    }
-	    /** Returns the top-level context ID of the given context, if any. */
-	    findTopLevelContextId(id) {
-	        if (id === null) {
-	            return null;
-	        }
-	        const maybeContext = this.findContext(id);
-	        const parentId = maybeContext?.parentId ?? null;
-	        if (parentId === null) {
-	            return id;
-	        }
-	        return this.findTopLevelContextId(parentId);
-	    }
-	    findContextBySession(sessionId) {
-	        for (const context of this.#contexts.values()) {
-	            if (context.cdpTarget.cdpSessionId === sessionId) {
-	                return context;
-	            }
-	        }
-	        return;
-	    }
-	    /** Gets the context with the given ID, if any, otherwise throws. */
-	    getContext(id) {
-	        const result = this.findContext(id);
-	        if (result === undefined) {
-	            throw new protocol_js_1$5.NoSuchFrameException(`Context ${id} not found`);
-	        }
-	        return result;
-	    }
-	    verifyContextsList(contexts) {
-	        const foundContexts = new Set();
-	        if (!contexts) {
-	            return foundContexts;
-	        }
-	        for (const contextId of contexts) {
-	            const context = this.getContext(contextId);
-	            if (context.isTopLevelContext()) {
-	                foundContexts.add(context);
-	            }
-	            else {
-	                throw new protocol_js_1$5.InvalidArgumentException(`Non top-level context '${contextId}' given.`);
-	            }
-	        }
-	        return foundContexts;
-	    }
-	}
-	BrowsingContextStorage$1.BrowsingContextStorage = BrowsingContextStorage;
-
-	var RealmStorage$1 = {};
-
-	Object.defineProperty(RealmStorage$1, "__esModule", { value: true });
-	RealmStorage$1.RealmStorage = void 0;
-	const protocol_js_1$4 = protocol;
-	const WindowRealm_js_1 = WindowRealm$1;
-	/** Container class for browsing realms. */
-	class RealmStorage {
-	    /** Tracks handles and their realms sent to the client. */
-	    #knownHandlesToRealmMap = new Map();
-	    /** Map from realm ID to Realm. */
-	    #realmMap = new Map();
-	    get knownHandlesToRealmMap() {
-	        return this.#knownHandlesToRealmMap;
-	    }
-	    addRealm(realm) {
-	        this.#realmMap.set(realm.realmId, realm);
-	    }
-	    /** Finds all realms that match the given filter. */
-	    findRealms(filter) {
-	        return Array.from(this.#realmMap.values()).filter((realm) => {
-	            if (filter.realmId !== undefined && filter.realmId !== realm.realmId) {
-	                return false;
-	            }
-	            if (filter.browsingContextId !== undefined &&
-	                !realm.associatedBrowsingContexts
-	                    .map((browsingContext) => browsingContext.id)
-	                    .includes(filter.browsingContextId)) {
-	                return false;
-	            }
-	            if (filter.sandbox !== undefined &&
-	                (!(realm instanceof WindowRealm_js_1.WindowRealm) || filter.sandbox !== realm.sandbox)) {
-	                return false;
-	            }
-	            if (filter.executionContextId !== undefined &&
-	                filter.executionContextId !== realm.executionContextId) {
-	                return false;
-	            }
-	            if (filter.origin !== undefined && filter.origin !== realm.origin) {
-	                return false;
-	            }
-	            if (filter.type !== undefined && filter.type !== realm.realmType) {
-	                return false;
-	            }
-	            if (filter.cdpSessionId !== undefined &&
-	                filter.cdpSessionId !== realm.cdpClient.sessionId) {
-	                return false;
-	            }
-	            return true;
-	        });
-	    }
-	    findRealm(filter) {
-	        const maybeRealms = this.findRealms(filter);
-	        if (maybeRealms.length !== 1) {
-	            return undefined;
-	        }
-	        return maybeRealms[0];
-	    }
-	    /** Gets the only realm that matches the given filter, if any, otherwise throws. */
-	    getRealm(filter) {
-	        const maybeRealm = this.findRealm(filter);
-	        if (maybeRealm === undefined) {
-	            throw new protocol_js_1$4.NoSuchFrameException(`Realm ${JSON.stringify(filter)} not found`);
-	        }
-	        return maybeRealm;
-	    }
-	    /** Deletes all realms that match the given filter. */
-	    deleteRealms(filter) {
-	        this.findRealms(filter).map((realm) => {
-	            realm.dispose();
-	            this.#realmMap.delete(realm.realmId);
-	            Array.from(this.knownHandlesToRealmMap.entries())
-	                .filter(([, r]) => r === realm.realmId)
-	                .map(([handle]) => this.knownHandlesToRealmMap.delete(handle));
-	        });
-	    }
-	}
-	RealmStorage$1.RealmStorage = RealmStorage;
-
-	var EventManager$1 = {};
-
-	var Buffer$2 = {};
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(Buffer$2, "__esModule", { value: true });
-	Buffer$2.Buffer = void 0;
-	/** Implements a FIFO buffer with a fixed size. */
-	let Buffer$1 = class Buffer {
-	    #capacity;
-	    #entries = [];
-	    #onItemRemoved;
-	    /**
-	     * @param capacity The buffer capacity.
-	     * @param onItemRemoved Delegate called for each removed element.
-	     */
-	    constructor(capacity, onItemRemoved) {
-	        this.#capacity = capacity;
-	        this.#onItemRemoved = onItemRemoved;
-	    }
-	    get() {
-	        return this.#entries;
-	    }
-	    add(value) {
-	        this.#entries.push(value);
-	        while (this.#entries.length > this.#capacity) {
-	            const item = this.#entries.shift();
-	            if (item !== undefined) {
-	                this.#onItemRemoved?.(item);
-	            }
-	        }
-	    }
-	};
-	Buffer$2.Buffer = Buffer$1;
-
-	var DefaultMap$1 = {};
-
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(DefaultMap$1, "__esModule", { value: true });
-	DefaultMap$1.DefaultMap = void 0;
-	/**
-	 * A subclass of Map whose functionality is almost the same as its parent
-	 * except for the fact that DefaultMap never returns undefined. It provides a
-	 * default value for keys that do not exist.
-	 */
-	class DefaultMap extends Map {
-	    /** The default value to return whenever a key is not present in the map. */
-	    #getDefaultValue;
-	    constructor(getDefaultValue, entries) {
-	        super(entries);
-	        this.#getDefaultValue = getDefaultValue;
-	    }
-	    get(key) {
-	        if (!this.has(key)) {
-	            this.set(key, this.#getDefaultValue(key));
-	        }
-	        return super.get(key);
-	    }
-	}
-	DefaultMap$1.DefaultMap = DefaultMap;
-
-	var IdWrapper$1 = {};
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(IdWrapper$1, "__esModule", { value: true });
-	IdWrapper$1.IdWrapper = void 0;
-	/**
-	 * Creates an object with a positive unique incrementing id.
-	 */
-	class IdWrapper {
-	    static #counter = 0;
-	    #id;
-	    constructor() {
-	        this.#id = ++IdWrapper.#counter;
-	    }
-	    get id() {
-	        return this.#id;
-	    }
-	}
-	IdWrapper$1.IdWrapper = IdWrapper;
-
-	var events = {};
-
-	Object.defineProperty(events, "__esModule", { value: true });
-	events.assertSupportedEvent = events.isCdpEvent = void 0;
-	/**
-	 * Copyright 2023 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	const protocol_js_1$3 = protocol;
-	/**
-	 * Returns true if the given event is a CDP event.
-	 * @see https://chromedevtools.github.io/devtools-protocol/
-	 */
-	function isCdpEvent(name) {
-	    return (name.split('.').at(0)?.startsWith(protocol_js_1$3.ChromiumBidi.BiDiModule.Cdp) ?? false);
-	}
-	events.isCdpEvent = isCdpEvent;
-	/**
-	 * Asserts that the given event is known to BiDi or BiDi+, or throws otherwise.
-	 */
-	function assertSupportedEvent(name) {
-	    if (!protocol_js_1$3.ChromiumBidi.EVENT_NAMES.has(name) && !isCdpEvent(name)) {
-	        throw new protocol_js_1$3.InvalidArgumentException(`Unknown event: ${name}`);
-	    }
-	}
-	events.assertSupportedEvent = assertSupportedEvent;
-
-	var SubscriptionManager$1 = {};
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(SubscriptionManager$1, "__esModule", { value: true });
-	SubscriptionManager$1.SubscriptionManager = SubscriptionManager$1.unrollEvents = SubscriptionManager$1.cartesianProduct = void 0;
-	const protocol_js_1$2 = protocol;
-	const events_js_1$1 = events;
-	/**
-	 * Returns the cartesian product of the given arrays.
-	 *
-	 * Example:
-	 *   cartesian([1, 2], ['a', 'b']); => [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
-	 */
-	function cartesianProduct(...a) {
-	    return a.reduce((a, b) => a.flatMap((d) => b.map((e) => [d, e].flat())));
-	}
-	SubscriptionManager$1.cartesianProduct = cartesianProduct;
-	/** Expands "AllEvents" events into atomic events. */
-	function unrollEvents(events) {
-	    const allEvents = new Set();
-	    function addEvents(events) {
-	        for (const event of events) {
-	            allEvents.add(event);
-	        }
-	    }
-	    for (const event of events) {
-	        switch (event) {
-	            case protocol_js_1$2.ChromiumBidi.BiDiModule.BrowsingContext:
-	                addEvents(Object.values(protocol_js_1$2.ChromiumBidi.BrowsingContext.EventNames));
-	                break;
-	            case protocol_js_1$2.ChromiumBidi.BiDiModule.Log:
-	                addEvents(Object.values(protocol_js_1$2.ChromiumBidi.Log.EventNames));
-	                break;
-	            case protocol_js_1$2.ChromiumBidi.BiDiModule.Network:
-	                addEvents(Object.values(protocol_js_1$2.ChromiumBidi.Network.EventNames));
-	                break;
-	            case protocol_js_1$2.ChromiumBidi.BiDiModule.Script:
-	                addEvents(Object.values(protocol_js_1$2.ChromiumBidi.Script.EventNames));
-	                break;
-	            default:
-	                allEvents.add(event);
-	        }
-	    }
-	    return [...allEvents.values()];
-	}
-	SubscriptionManager$1.unrollEvents = unrollEvents;
-	class SubscriptionManager {
-	    #subscriptionPriority = 0;
-	    // BrowsingContext `null` means the event has subscription across all the
-	    // browsing contexts.
-	    // Channel `null` means no `channel` should be added.
-	    #channelToContextToEventMap = new Map();
-	    #browsingContextStorage;
-	    constructor(browsingContextStorage) {
-	        this.#browsingContextStorage = browsingContextStorage;
-	    }
-	    getChannelsSubscribedToEvent(eventMethod, contextId) {
-	        const prioritiesAndChannels = Array.from(this.#channelToContextToEventMap.keys())
-	            .map((channel) => ({
-	            priority: this.#getEventSubscriptionPriorityForChannel(eventMethod, contextId, channel),
-	            channel,
-	        }))
-	            .filter(({ priority }) => priority !== null);
-	        // Sort channels by priority.
-	        return prioritiesAndChannels
-	            .sort((a, b) => a.priority - b.priority)
-	            .map(({ channel }) => channel);
-	    }
-	    #getEventSubscriptionPriorityForChannel(eventMethod, contextId, channel) {
-	        const contextToEventMap = this.#channelToContextToEventMap.get(channel);
-	        if (contextToEventMap === undefined) {
-	            return null;
-	        }
-	        const maybeTopLevelContextId = this.#browsingContextStorage.findTopLevelContextId(contextId);
-	        // `null` covers global subscription.
-	        const relevantContexts = [...new Set([null, maybeTopLevelContextId])];
-	        // Get all the subscription priorities.
-	        const priorities = relevantContexts
-	            .map((context) => {
-	            // Get the priority for exact event name
-	            const priority = contextToEventMap.get(context)?.get(eventMethod);
-	            // For CDP we can't provide specific event name when subscribing
-	            // to the module directly.
-	            // Because of that we need to see event `cdp` exits in the map.
-	            if ((0, events_js_1$1.isCdpEvent)(eventMethod)) {
-	                const cdpPriority = contextToEventMap
-	                    .get(context)
-	                    ?.get(protocol_js_1$2.ChromiumBidi.BiDiModule.Cdp);
-	                // If we subscribe to the event directly and `cdp` module as well
-	                // priority will be different we take minimal priority
-	                return priority && cdpPriority
-	                    ? Math.min(priority, cdpPriority)
-	                    : // At this point we know that we have subscribed
-	                        // to only one of the two
-	                        priority ?? cdpPriority;
-	            }
-	            return priority;
-	        })
-	            .filter((p) => p !== undefined);
-	        if (priorities.length === 0) {
-	            // Not subscribed, return null.
-	            return null;
-	        }
-	        // Return minimal priority.
-	        return Math.min(...priorities);
-	    }
-	    /**
-	     * @param module BiDi+ module
-	     * @param contextId `null` == globally subscribed
-	     *
-	     * @returns
-	     */
-	    isSubscribedTo(moduleOrEvent, contextId = null) {
-	        const topLevelContext = this.#browsingContextStorage.findTopLevelContextId(contextId);
-	        for (const browserContextToEventMap of this.#channelToContextToEventMap.values()) {
-	            for (const [id, eventMap] of browserContextToEventMap.entries()) {
-	                // Not subscribed to this context or globally
-	                if (topLevelContext !== id && id !== null) {
-	                    continue;
-	                }
-	                for (const event of eventMap.keys()) {
-	                    // This also covers the `cdp` case where
-	                    // we don't unroll the event names
-	                    if (
-	                    // Event explicitly subscribed
-	                    event === moduleOrEvent ||
-	                        // Event subscribed via module
-	                        event === moduleOrEvent.split('.').at(0) ||
-	                        // Event explicitly subscribed compared to module
-	                        event.split('.').at(0) === moduleOrEvent) {
-	                        return true;
-	                    }
-	                }
-	            }
-	        }
-	        return false;
-	    }
-	    subscribe(event, contextId, channel) {
-	        // All the subscriptions are handled on the top-level contexts.
-	        contextId = this.#browsingContextStorage.findTopLevelContextId(contextId);
-	        // Check if subscribed event is a whole module
-	        switch (event) {
-	            case protocol_js_1$2.ChromiumBidi.BiDiModule.BrowsingContext:
-	                Object.values(protocol_js_1$2.ChromiumBidi.BrowsingContext.EventNames).map((specificEvent) => this.subscribe(specificEvent, contextId, channel));
-	                return;
-	            case protocol_js_1$2.ChromiumBidi.BiDiModule.Log:
-	                Object.values(protocol_js_1$2.ChromiumBidi.Log.EventNames).map((specificEvent) => this.subscribe(specificEvent, contextId, channel));
-	                return;
-	            case protocol_js_1$2.ChromiumBidi.BiDiModule.Network:
-	                Object.values(protocol_js_1$2.ChromiumBidi.Network.EventNames).map((specificEvent) => this.subscribe(specificEvent, contextId, channel));
-	                return;
-	            case protocol_js_1$2.ChromiumBidi.BiDiModule.Script:
-	                Object.values(protocol_js_1$2.ChromiumBidi.Script.EventNames).map((specificEvent) => this.subscribe(specificEvent, contextId, channel));
-	                return;
-	            // Intentionally left empty.
-	        }
-	        if (!this.#channelToContextToEventMap.has(channel)) {
-	            this.#channelToContextToEventMap.set(channel, new Map());
-	        }
-	        const contextToEventMap = this.#channelToContextToEventMap.get(channel);
-	        if (!contextToEventMap.has(contextId)) {
-	            contextToEventMap.set(contextId, new Map());
-	        }
-	        const eventMap = contextToEventMap.get(contextId);
-	        // Do not re-subscribe to events to keep the priority.
-	        if (eventMap.has(event)) {
-	            return;
-	        }
-	        eventMap.set(event, this.#subscriptionPriority++);
-	    }
-	    /**
-	     * Unsubscribes atomically from all events in the given contexts and channel.
-	     */
-	    unsubscribeAll(events, contextIds, channel) {
-	        // Assert all contexts are known.
-	        for (const contextId of contextIds) {
-	            if (contextId !== null) {
-	                this.#browsingContextStorage.getContext(contextId);
-	            }
-	        }
-	        const eventContextPairs = cartesianProduct(unrollEvents(events), contextIds);
-	        // Assert all unsubscriptions are valid.
-	        // If any of the unsubscriptions are invalid, do not unsubscribe from anything.
-	        eventContextPairs
-	            .map(([event, contextId]) => this.#checkUnsubscribe(event, contextId, channel))
-	            .forEach((unsubscribe) => unsubscribe());
-	    }
-	    /**
-	     * Unsubscribes from the event in the given context and channel.
-	     * Syntactic sugar for "unsubscribeAll".
-	     */
-	    unsubscribe(eventName, contextId, channel) {
-	        this.unsubscribeAll([eventName], [contextId], channel);
-	    }
-	    #checkUnsubscribe(event, contextId, channel) {
-	        // All the subscriptions are handled on the top-level contexts.
-	        contextId = this.#browsingContextStorage.findTopLevelContextId(contextId);
-	        if (!this.#channelToContextToEventMap.has(channel)) {
-	            throw new protocol_js_1$2.InvalidArgumentException(`Cannot unsubscribe from ${event}, ${contextId === null ? 'null' : contextId}. No subscription found.`);
-	        }
-	        const contextToEventMap = this.#channelToContextToEventMap.get(channel);
-	        if (!contextToEventMap.has(contextId)) {
-	            throw new protocol_js_1$2.InvalidArgumentException(`Cannot unsubscribe from ${event}, ${contextId === null ? 'null' : contextId}. No subscription found.`);
-	        }
-	        const eventMap = contextToEventMap.get(contextId);
-	        if (!eventMap.has(event)) {
-	            throw new protocol_js_1$2.InvalidArgumentException(`Cannot unsubscribe from ${event}, ${contextId === null ? 'null' : contextId}. No subscription found.`);
-	        }
-	        return () => {
-	            eventMap.delete(event);
-	            // Clean up maps if empty.
-	            if (eventMap.size === 0) {
-	                contextToEventMap.delete(event);
-	            }
-	            if (contextToEventMap.size === 0) {
-	                this.#channelToContextToEventMap.delete(channel);
-	            }
-	        };
-	    }
-	}
-	SubscriptionManager$1.SubscriptionManager = SubscriptionManager;
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(EventManager$1, "__esModule", { value: true });
-	EventManager$1.EventManager = void 0;
-	const protocol_js_1$1 = protocol;
-	const Buffer_js_1 = Buffer$2;
-	const DefaultMap_js_1 = DefaultMap$1;
-	const EventEmitter_js_1$2 = EventEmitter$1;
-	const IdWrapper_js_1 = IdWrapper$1;
-	const OutgoingMessage_js_1 = OutgoingMessage$1;
-	const events_js_1 = events;
-	const SubscriptionManager_js_1 = SubscriptionManager$1;
-	class EventWrapper {
-	    #idWrapper = new IdWrapper_js_1.IdWrapper();
-	    #contextId;
-	    #event;
-	    constructor(event, contextId) {
-	        this.#event = event;
-	        this.#contextId = contextId;
-	    }
-	    get id() {
-	        return this.#idWrapper.id;
-	    }
-	    get contextId() {
-	        return this.#contextId;
-	    }
-	    get event() {
-	        return this.#event;
-	    }
-	}
-	/**
-	 * Maps event name to a desired buffer length.
-	 */
-	const eventBufferLength = new Map([[protocol_js_1$1.ChromiumBidi.Log.EventNames.LogEntryAdded, 100]]);
-	class EventManager extends EventEmitter_js_1$2.EventEmitter {
-	    /**
-	     * Maps event name to a set of contexts where this event already happened.
-	     * Needed for getting buffered events from all the contexts in case of
-	     * subscripting to all contexts.
-	     */
-	    #eventToContextsMap = new DefaultMap_js_1.DefaultMap(() => new Set());
-	    /**
-	     * Maps `eventName` + `browsingContext` to buffer. Used to get buffered events
-	     * during subscription. Channel-agnostic.
-	     */
-	    #eventBuffers = new Map();
-	    /**
-	     * Maps `eventName` + `browsingContext` + `channel` to last sent event id.
-	     * Used to avoid sending duplicated events when user
-	     * subscribes -> unsubscribes -> subscribes.
-	     */
-	    #lastMessageSent = new Map();
-	    #subscriptionManager;
-	    #browsingContextStorage;
-	    constructor(browsingContextStorage) {
-	        super();
-	        this.#browsingContextStorage = browsingContextStorage;
-	        this.#subscriptionManager = new SubscriptionManager_js_1.SubscriptionManager(browsingContextStorage);
-	    }
-	    get subscriptionManager() {
-	        return this.#subscriptionManager;
-	    }
-	    /**
-	     * Returns consistent key to be used to access value maps.
-	     */
-	    static #getMapKey(eventName, browsingContext, channel) {
-	        return JSON.stringify({ eventName, browsingContext, channel });
-	    }
-	    registerEvent(event, contextId) {
-	        this.registerPromiseEvent(Promise.resolve({
-	            kind: 'success',
-	            value: event,
-	        }), contextId, event.method);
-	    }
-	    registerPromiseEvent(event, contextId, eventName) {
-	        const eventWrapper = new EventWrapper(event, contextId);
-	        const sortedChannels = this.#subscriptionManager.getChannelsSubscribedToEvent(eventName, contextId);
-	        this.#bufferEvent(eventWrapper, eventName);
-	        // Send events to channels in the subscription priority.
-	        for (const channel of sortedChannels) {
-	            this.emit("event" /* EventManagerEvents.Event */, {
-	                message: OutgoingMessage_js_1.OutgoingMessage.createFromPromise(event, channel),
-	                event: eventName,
-	            });
-	            this.#markEventSent(eventWrapper, channel, eventName);
-	        }
-	    }
-	    async subscribe(eventNames, contextIds, channel) {
-	        for (const name of eventNames) {
-	            (0, events_js_1.assertSupportedEvent)(name);
-	        }
-	        // First check if all the contexts are known.
-	        for (const contextId of contextIds) {
-	            if (contextId !== null) {
-	                // Assert the context is known. Throw exception otherwise.
-	                this.#browsingContextStorage.getContext(contextId);
-	            }
-	        }
-	        for (const eventName of eventNames) {
-	            for (const contextId of contextIds) {
-	                this.#subscriptionManager.subscribe(eventName, contextId, channel);
-	                for (const eventWrapper of this.#getBufferedEvents(eventName, contextId, channel)) {
-	                    // The order of the events is important.
-	                    this.emit("event" /* EventManagerEvents.Event */, {
-	                        message: OutgoingMessage_js_1.OutgoingMessage.createFromPromise(eventWrapper.event, channel),
-	                        event: eventName,
-	                    });
-	                    this.#markEventSent(eventWrapper, channel, eventName);
-	                }
-	            }
-	        }
-	        await this.toggleModulesIfNeeded();
-	    }
-	    async unsubscribe(eventNames, contextIds, channel) {
-	        for (const name of eventNames) {
-	            (0, events_js_1.assertSupportedEvent)(name);
-	        }
-	        this.#subscriptionManager.unsubscribeAll(eventNames, contextIds, channel);
-	        await this.toggleModulesIfNeeded();
-	    }
-	    async toggleModulesIfNeeded() {
-	        // TODO(1): Only update changed subscribers
-	        // TODO(2): Enable for Worker Targets
-	        await Promise.all(this.#browsingContextStorage.getAllContexts().map(async (context) => {
-	            return await context.toggleModulesIfNeeded();
-	        }));
-	    }
-	    /**
-	     * If the event is buffer-able, put it in the buffer.
-	     */
-	    #bufferEvent(eventWrapper, eventName) {
-	        if (!eventBufferLength.has(eventName)) {
-	            // Do nothing if the event is no buffer-able.
-	            return;
-	        }
-	        const bufferMapKey = EventManager.#getMapKey(eventName, eventWrapper.contextId);
-	        if (!this.#eventBuffers.has(bufferMapKey)) {
-	            this.#eventBuffers.set(bufferMapKey, new Buffer_js_1.Buffer(eventBufferLength.get(eventName)));
-	        }
-	        this.#eventBuffers.get(bufferMapKey).add(eventWrapper);
-	        // Add the context to the list of contexts having `eventName` events.
-	        this.#eventToContextsMap.get(eventName).add(eventWrapper.contextId);
-	    }
-	    /**
-	     * If the event is buffer-able, mark it as sent to the given contextId and channel.
-	     */
-	    #markEventSent(eventWrapper, channel, eventName) {
-	        if (!eventBufferLength.has(eventName)) {
-	            // Do nothing if the event is no buffer-able.
-	            return;
-	        }
-	        const lastSentMapKey = EventManager.#getMapKey(eventName, eventWrapper.contextId, channel);
-	        this.#lastMessageSent.set(lastSentMapKey, Math.max(this.#lastMessageSent.get(lastSentMapKey) ?? 0, eventWrapper.id));
-	    }
-	    /**
-	     * Returns events which are buffered and not yet sent to the given channel events.
-	     */
-	    #getBufferedEvents(eventName, contextId, channel) {
-	        const bufferMapKey = EventManager.#getMapKey(eventName, contextId);
-	        const lastSentMapKey = EventManager.#getMapKey(eventName, contextId, channel);
-	        const lastSentMessageId = this.#lastMessageSent.get(lastSentMapKey) ?? -Infinity;
-	        const result = this.#eventBuffers
-	            .get(bufferMapKey)
-	            ?.get()
-	            .filter((wrapper) => wrapper.id > lastSentMessageId) ?? [];
-	        if (contextId === null) {
-	            // For global subscriptions, events buffered in each context should be sent back.
-	            Array.from(this.#eventToContextsMap.get(eventName).keys())
-	                .filter((_contextId) => 
-	            // Events without context are already in the result.
-	            _contextId !== null &&
-	                // Events from deleted contexts should not be sent.
-	                this.#browsingContextStorage.hasContext(_contextId))
-	                .map((_contextId) => this.#getBufferedEvents(eventName, _contextId, channel))
-	                .forEach((events) => result.push(...events));
-	        }
-	        return result.sort((e1, e2) => e1.id - e2.id);
-	    }
-	}
-	EventManager$1.EventManager = EventManager;
-
-	/**
-	 * Copyright 2021 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(BidiServer$1, "__esModule", { value: true });
-	BidiServer$1.BidiServer = void 0;
-	const EventEmitter_js_1$1 = EventEmitter$1;
-	const log_js_1$4 = log$1;
-	const ProcessingQueue_js_1 = ProcessingQueue$1;
-	const CommandProcessor_js_1 = CommandProcessor$1;
-	const BrowsingContextStorage_js_1 = BrowsingContextStorage$1;
-	const RealmStorage_js_1 = RealmStorage$1;
-	const EventManager_js_1 = EventManager$1;
-	class BidiServer extends EventEmitter_js_1$1.EventEmitter {
-	    #messageQueue;
-	    #transport;
-	    #commandProcessor;
-	    #eventManager;
-	    #browsingContextStorage = new BrowsingContextStorage_js_1.BrowsingContextStorage();
-	    #logger;
-	    #handleIncomingMessage = (message) => {
-	        void this.#commandProcessor.processCommand(message).catch((error) => {
-	            this.#logger?.(log_js_1$4.LogType.debugError, error);
-	        });
-	    };
-	    #processOutgoingMessage = async (messageEntry) => {
-	        const message = messageEntry.message;
-	        if (messageEntry.channel !== null) {
-	            message['channel'] = messageEntry.channel;
-	        }
-	        await this.#transport.sendMessage(message);
-	    };
-	    constructor(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, defaultUserContextId, options, parser, logger) {
-	        super();
-	        this.#logger = logger;
-	        this.#messageQueue = new ProcessingQueue_js_1.ProcessingQueue(this.#processOutgoingMessage, this.#logger);
-	        this.#transport = bidiTransport;
-	        this.#transport.setOnMessage(this.#handleIncomingMessage);
-	        this.#eventManager = new EventManager_js_1.EventManager(this.#browsingContextStorage);
-	        this.#commandProcessor = new CommandProcessor_js_1.CommandProcessor(cdpConnection, browserCdpClient, this.#eventManager, selfTargetId, defaultUserContextId, this.#browsingContextStorage, new RealmStorage_js_1.RealmStorage(), options?.acceptInsecureCerts ?? false, options?.sharedIdWithFrame ?? false, parser, this.#logger);
-	        this.#eventManager.on("event" /* EventManagerEvents.Event */, ({ message, event }) => {
-	            this.emitOutgoingMessage(message, event);
-	        });
-	        this.#commandProcessor.on("response" /* CommandProcessorEvents.Response */, ({ message, event }) => {
-	            this.emitOutgoingMessage(message, event);
-	        });
-	    }
-	    /**
-	     * Creates and starts BiDi Mapper instance.
-	     */
-	    static async createAndStart(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, options, parser, logger) {
-	        // The default context is not exposed in Target.getBrowserContexts but can
-	        // be observed via Target.getTargets. To determine the default browser
-	        // context, we check which one is mentioned in Target.getTargets and not in
-	        // Target.getBrowserContexts.
-	        const [{ browserContextIds }, { targetInfos }] = await Promise.all([
-	            browserCdpClient.sendCommand('Target.getBrowserContexts'),
-	            browserCdpClient.sendCommand('Target.getTargets'),
-	        ]);
-	        let defaultUserContextId = 'default';
-	        for (const info of targetInfos) {
-	            if (info.browserContextId &&
-	                !browserContextIds.includes(info.browserContextId)) {
-	                defaultUserContextId = info.browserContextId;
-	                break;
-	            }
-	        }
-	        const server = new BidiServer(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, defaultUserContextId, options, parser, logger);
-	        // Needed to get events about new targets.
-	        await browserCdpClient.sendCommand('Target.setDiscoverTargets', {
-	            discover: true,
-	        });
-	        // Needed to automatically attach to new targets.
-	        await browserCdpClient.sendCommand('Target.setAutoAttach', {
-	            autoAttach: true,
-	            waitForDebuggerOnStart: true,
-	            flatten: true,
-	        });
-	        await server.#topLevelContextsLoaded();
-	        return server;
-	    }
-	    /**
-	     * Sends BiDi message.
-	     */
-	    emitOutgoingMessage(messageEntry, event) {
-	        this.#messageQueue.add(messageEntry, event);
-	    }
-	    close() {
-	        this.#transport.close();
-	    }
-	    async #topLevelContextsLoaded() {
-	        await Promise.all(this.#browsingContextStorage
-	            .getTopLevelContexts()
-	            .map((c) => c.lifecycleLoaded()));
-	    }
-	}
-	BidiServer$1.BidiServer = BidiServer;
-
-	(function (exports) {
-		/**
-		 * Copyright 2022 Google LLC.
-		 * Copyright (c) Microsoft Corporation.
-		 *
-		 * 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
-		 *
-		 *     http://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.
-		 */
-		Object.defineProperty(exports, "__esModule", { value: true });
-		exports.OutgoingMessage = exports.EventEmitter = exports.BidiServer = void 0;
-		/**
-		 * @fileoverview The entry point to the BiDi Mapper namespace.
-		 * Other modules should only access exports defined in this file.
-		 * XXX: Add ESlint rule for this (https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-restricted-paths.md)
-		 */
-		var BidiServer_js_1 = BidiServer$1;
-		Object.defineProperty(exports, "BidiServer", { enumerable: true, get: function () { return BidiServer_js_1.BidiServer; } });
-		var EventEmitter_js_1 = EventEmitter$1;
-		Object.defineProperty(exports, "EventEmitter", { enumerable: true, get: function () { return EventEmitter_js_1.EventEmitter; } });
-		var OutgoingMessage_js_1 = OutgoingMessage$1;
-		Object.defineProperty(exports, "OutgoingMessage", { enumerable: true, get: function () { return OutgoingMessage_js_1.OutgoingMessage; } });
-		
-	} (BidiMapper));
-
-	var CdpConnection = {};
-
-	var CdpClient = {};
-
-	/**
-	 * Copyright 2021 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	Object.defineProperty(CdpClient, "__esModule", { value: true });
-	CdpClient.MapperCdpClient = CdpClient.CloseError = void 0;
-	const EventEmitter_js_1 = EventEmitter$1;
-	/** A error that will be thrown if/when the connection is closed. */
-	class CloseError extends Error {
-	}
-	CdpClient.CloseError = CloseError;
-	/** Represents a high-level CDP connection to the browser. */
-	class MapperCdpClient extends EventEmitter_js_1.EventEmitter {
-	    #cdpConnection;
-	    #sessionId;
-	    constructor(cdpConnection, sessionId) {
-	        super();
-	        this.#cdpConnection = cdpConnection;
-	        this.#sessionId = sessionId;
-	    }
-	    get sessionId() {
-	        return this.#sessionId;
-	    }
-	    sendCommand(method, ...params) {
-	        return this.#cdpConnection.sendCommand(method, params[0], this.#sessionId);
-	    }
-	    isCloseError(error) {
-	        return error instanceof CloseError;
-	    }
-	}
-	CdpClient.MapperCdpClient = MapperCdpClient;
-
-	Object.defineProperty(CdpConnection, "__esModule", { value: true });
-	CdpConnection.MapperCdpConnection = void 0;
-	const log_js_1$3 = log$1;
-	const CdpClient_js_1 = CdpClient;
-	/**
-	 * Represents a high-level CDP connection to the browser backend.
-	 *
-	 * Manages all CdpClients (each backed by a Session ID) instance for each active
-	 * CDP session.
-	 */
-	class MapperCdpConnection {
-	    static LOGGER_PREFIX_RECV = `${log_js_1$3.LogType.cdp}:RECV â—‚`;
-	    static LOGGER_PREFIX_SEND = `${log_js_1$3.LogType.cdp}:SEND â–¸`;
-	    #mainBrowserCdpClient;
-	    #transport;
-	    /** Map from session ID to CdpClient.
-	     * `undefined` points to the main browser session. */
-	    #sessionCdpClients = new Map();
-	    #commandCallbacks = new Map();
-	    #logger;
-	    #nextId = 0;
-	    constructor(transport, logger) {
-	        this.#transport = transport;
-	        this.#logger = logger;
-	        this.#transport.setOnMessage(this.#onMessage);
-	        // Create default Browser CDP Session.
-	        this.#mainBrowserCdpClient = this.#createCdpClient(undefined);
-	    }
-	    /** Closes the connection to the browser. */
-	    close() {
-	        this.#transport.close();
-	        for (const [, { reject, error }] of this.#commandCallbacks) {
-	            reject(error);
-	        }
-	        this.#commandCallbacks.clear();
-	        this.#sessionCdpClients.clear();
-	    }
-	    async createBrowserSession() {
-	        const { sessionId } = await this.#mainBrowserCdpClient.sendCommand('Target.attachToBrowserTarget');
-	        return this.#createCdpClient(sessionId);
-	    }
-	    /**
-	     * Gets a CdpClient instance attached to the given session ID,
-	     * or null if the session is not attached.
-	     */
-	    getCdpClient(sessionId) {
-	        const cdpClient = this.#sessionCdpClients.get(sessionId);
-	        if (!cdpClient) {
-	            throw new Error(`Unknown CDP session ID: ${sessionId}`);
-	        }
-	        return cdpClient;
-	    }
-	    sendCommand(method, params, sessionId) {
-	        return new Promise((resolve, reject) => {
-	            const id = this.#nextId++;
-	            this.#commandCallbacks.set(id, {
-	                resolve,
-	                reject,
-	                error: new CdpClient_js_1.CloseError(`${method} ${JSON.stringify(params)} ${sessionId ?? ''} call rejected because the connection has been closed.`),
-	            });
-	            const cdpMessage = { id, method, params };
-	            if (sessionId) {
-	                cdpMessage.sessionId = sessionId;
-	            }
-	            void this.#transport
-	                .sendMessage(JSON.stringify(cdpMessage))
-	                ?.catch((error) => {
-	                this.#logger?.(log_js_1$3.LogType.debugError, error);
-	                this.#transport.close();
-	            });
-	            this.#logger?.(MapperCdpConnection.LOGGER_PREFIX_SEND, cdpMessage);
-	        });
-	    }
-	    #onMessage = (json) => {
-	        const message = JSON.parse(json);
-	        this.#logger?.(MapperCdpConnection.LOGGER_PREFIX_RECV, message);
-	        // Update client map if a session is attached
-	        // Listen for these events on every session.
-	        if (message.method === 'Target.attachedToTarget') {
-	            const { sessionId } = message.params;
-	            this.#createCdpClient(sessionId);
-	        }
-	        if (message.id !== undefined) {
-	            // Handle command response.
-	            const callbacks = this.#commandCallbacks.get(message.id);
-	            this.#commandCallbacks.delete(message.id);
-	            if (callbacks) {
-	                if (message.result) {
-	                    callbacks.resolve(message.result);
-	                }
-	                else if (message.error) {
-	                    callbacks.reject(message.error);
-	                }
-	            }
-	        }
-	        else if (message.method) {
-	            const client = this.#sessionCdpClients.get(message.sessionId ?? undefined);
-	            client?.emit(message.method, message.params || {});
-	            // Update client map if a session is detached
-	            // But emit on that session
-	            if (message.method === 'Target.detachedFromTarget') {
-	                const { sessionId } = message.params;
-	                const client = this.#sessionCdpClients.get(sessionId);
-	                if (client) {
-	                    this.#sessionCdpClients.delete(sessionId);
-	                    client.removeAllListeners();
-	                }
-	            }
-	        }
-	    };
-	    /**
-	     * Creates a new CdpClient instance for the given session ID.
-	     * @param sessionId either a string, or undefined for the main browser session.
-	     * The main browser session is used only to create new browser sessions.
-	     * @private
-	     */
-	    #createCdpClient(sessionId) {
-	        const cdpClient = new CdpClient_js_1.MapperCdpClient(this, sessionId);
-	        this.#sessionCdpClients.set(sessionId, cdpClient);
-	        return cdpClient;
-	    }
-	}
-	CdpConnection.MapperCdpConnection = MapperCdpConnection;
-
-	var BidiParser$1 = {};
-
-	var protocolParser = {};
-
-	var lib = {};
-
-	var external = {};
-
-	var errors = {};
-
-	var en = {};
-
-	var util = {};
-
-	(function (exports) {
-		Object.defineProperty(exports, "__esModule", { value: true });
-		exports.getParsedType = exports.ZodParsedType = exports.objectUtil = exports.util = void 0;
-		var util;
-		(function (util) {
-		    util.assertEqual = (val) => val;
-		    function assertIs(_arg) { }
-		    util.assertIs = assertIs;
-		    function assertNever(_x) {
-		        throw new Error();
-		    }
-		    util.assertNever = assertNever;
-		    util.arrayToEnum = (items) => {
-		        const obj = {};
-		        for (const item of items) {
-		            obj[item] = item;
-		        }
-		        return obj;
-		    };
-		    util.getValidEnumValues = (obj) => {
-		        const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number");
-		        const filtered = {};
-		        for (const k of validKeys) {
-		            filtered[k] = obj[k];
-		        }
-		        return util.objectValues(filtered);
-		    };
-		    util.objectValues = (obj) => {
-		        return util.objectKeys(obj).map(function (e) {
-		            return obj[e];
-		        });
-		    };
-		    util.objectKeys = typeof Object.keys === "function" // eslint-disable-line ban/ban
-		        ? (obj) => Object.keys(obj) // eslint-disable-line ban/ban
-		        : (object) => {
-		            const keys = [];
-		            for (const key in object) {
-		                if (Object.prototype.hasOwnProperty.call(object, key)) {
-		                    keys.push(key);
-		                }
-		            }
-		            return keys;
-		        };
-		    util.find = (arr, checker) => {
-		        for (const item of arr) {
-		            if (checker(item))
-		                return item;
-		        }
-		        return undefined;
-		    };
-		    util.isInteger = typeof Number.isInteger === "function"
-		        ? (val) => Number.isInteger(val) // eslint-disable-line ban/ban
-		        : (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val;
-		    function joinValues(array, separator = " | ") {
-		        return array
-		            .map((val) => (typeof val === "string" ? `'${val}'` : val))
-		            .join(separator);
-		    }
-		    util.joinValues = joinValues;
-		    util.jsonStringifyReplacer = (_, value) => {
-		        if (typeof value === "bigint") {
-		            return value.toString();
-		        }
-		        return value;
-		    };
-		})(util = exports.util || (exports.util = {}));
-		(function (objectUtil) {
-		    objectUtil.mergeShapes = (first, second) => {
-		        return {
-		            ...first,
-		            ...second, // second overwrites first
-		        };
-		    };
-		})(exports.objectUtil || (exports.objectUtil = {}));
-		exports.ZodParsedType = util.arrayToEnum([
-		    "string",
-		    "nan",
-		    "number",
-		    "integer",
-		    "float",
-		    "boolean",
-		    "date",
-		    "bigint",
-		    "symbol",
-		    "function",
-		    "undefined",
-		    "null",
-		    "array",
-		    "object",
-		    "unknown",
-		    "promise",
-		    "void",
-		    "never",
-		    "map",
-		    "set",
-		]);
-		const getParsedType = (data) => {
-		    const t = typeof data;
-		    switch (t) {
-		        case "undefined":
-		            return exports.ZodParsedType.undefined;
-		        case "string":
-		            return exports.ZodParsedType.string;
-		        case "number":
-		            return isNaN(data) ? exports.ZodParsedType.nan : exports.ZodParsedType.number;
-		        case "boolean":
-		            return exports.ZodParsedType.boolean;
-		        case "function":
-		            return exports.ZodParsedType.function;
-		        case "bigint":
-		            return exports.ZodParsedType.bigint;
-		        case "symbol":
-		            return exports.ZodParsedType.symbol;
-		        case "object":
-		            if (Array.isArray(data)) {
-		                return exports.ZodParsedType.array;
-		            }
-		            if (data === null) {
-		                return exports.ZodParsedType.null;
-		            }
-		            if (data.then &&
-		                typeof data.then === "function" &&
-		                data.catch &&
-		                typeof data.catch === "function") {
-		                return exports.ZodParsedType.promise;
-		            }
-		            if (typeof Map !== "undefined" && data instanceof Map) {
-		                return exports.ZodParsedType.map;
-		            }
-		            if (typeof Set !== "undefined" && data instanceof Set) {
-		                return exports.ZodParsedType.set;
-		            }
-		            if (typeof Date !== "undefined" && data instanceof Date) {
-		                return exports.ZodParsedType.date;
-		            }
-		            return exports.ZodParsedType.object;
-		        default:
-		            return exports.ZodParsedType.unknown;
-		    }
-		};
-		exports.getParsedType = getParsedType; 
-	} (util));
-
-	var ZodError$1 = {};
-
-	Object.defineProperty(ZodError$1, "__esModule", { value: true });
-	ZodError$1.ZodError = ZodError$1.quotelessJson = ZodError$1.ZodIssueCode = void 0;
-	const util_1$1 = util;
-	ZodError$1.ZodIssueCode = util_1$1.util.arrayToEnum([
-	    "invalid_type",
-	    "invalid_literal",
-	    "custom",
-	    "invalid_union",
-	    "invalid_union_discriminator",
-	    "invalid_enum_value",
-	    "unrecognized_keys",
-	    "invalid_arguments",
-	    "invalid_return_type",
-	    "invalid_date",
-	    "invalid_string",
-	    "too_small",
-	    "too_big",
-	    "invalid_intersection_types",
-	    "not_multiple_of",
-	    "not_finite",
-	]);
-	const quotelessJson = (obj) => {
-	    const json = JSON.stringify(obj, null, 2);
-	    return json.replace(/"([^"]+)":/g, "$1:");
-	};
-	ZodError$1.quotelessJson = quotelessJson;
-	class ZodError extends Error {
-	    constructor(issues) {
-	        super();
-	        this.issues = [];
-	        this.addIssue = (sub) => {
-	            this.issues = [...this.issues, sub];
-	        };
-	        this.addIssues = (subs = []) => {
-	            this.issues = [...this.issues, ...subs];
-	        };
-	        const actualProto = new.target.prototype;
-	        if (Object.setPrototypeOf) {
-	            // eslint-disable-next-line ban/ban
-	            Object.setPrototypeOf(this, actualProto);
-	        }
-	        else {
-	            this.__proto__ = actualProto;
-	        }
-	        this.name = "ZodError";
-	        this.issues = issues;
-	    }
-	    get errors() {
-	        return this.issues;
-	    }
-	    format(_mapper) {
-	        const mapper = _mapper ||
-	            function (issue) {
-	                return issue.message;
-	            };
-	        const fieldErrors = { _errors: [] };
-	        const processError = (error) => {
-	            for (const issue of error.issues) {
-	                if (issue.code === "invalid_union") {
-	                    issue.unionErrors.map(processError);
-	                }
-	                else if (issue.code === "invalid_return_type") {
-	                    processError(issue.returnTypeError);
-	                }
-	                else if (issue.code === "invalid_arguments") {
-	                    processError(issue.argumentsError);
-	                }
-	                else if (issue.path.length === 0) {
-	                    fieldErrors._errors.push(mapper(issue));
-	                }
-	                else {
-	                    let curr = fieldErrors;
-	                    let i = 0;
-	                    while (i < issue.path.length) {
-	                        const el = issue.path[i];
-	                        const terminal = i === issue.path.length - 1;
-	                        if (!terminal) {
-	                            curr[el] = curr[el] || { _errors: [] };
-	                            // if (typeof el === "string") {
-	                            //   curr[el] = curr[el] || { _errors: [] };
-	                            // } else if (typeof el === "number") {
-	                            //   const errorArray: any = [];
-	                            //   errorArray._errors = [];
-	                            //   curr[el] = curr[el] || errorArray;
-	                            // }
-	                        }
-	                        else {
-	                            curr[el] = curr[el] || { _errors: [] };
-	                            curr[el]._errors.push(mapper(issue));
-	                        }
-	                        curr = curr[el];
-	                        i++;
-	                    }
-	                }
-	            }
-	        };
-	        processError(this);
-	        return fieldErrors;
-	    }
-	    toString() {
-	        return this.message;
-	    }
-	    get message() {
-	        return JSON.stringify(this.issues, util_1$1.util.jsonStringifyReplacer, 2);
-	    }
-	    get isEmpty() {
-	        return this.issues.length === 0;
-	    }
-	    flatten(mapper = (issue) => issue.message) {
-	        const fieldErrors = {};
-	        const formErrors = [];
-	        for (const sub of this.issues) {
-	            if (sub.path.length > 0) {
-	                fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];
-	                fieldErrors[sub.path[0]].push(mapper(sub));
-	            }
-	            else {
-	                formErrors.push(mapper(sub));
-	            }
-	        }
-	        return { formErrors, fieldErrors };
-	    }
-	    get formErrors() {
-	        return this.flatten();
-	    }
-	}
-	ZodError$1.ZodError = ZodError;
-	ZodError.create = (issues) => {
-	    const error = new ZodError(issues);
-	    return error;
-	};
-
-	Object.defineProperty(en, "__esModule", { value: true });
-	const util_1 = util;
-	const ZodError_1 = ZodError$1;
-	const errorMap = (issue, _ctx) => {
-	    let message;
-	    switch (issue.code) {
-	        case ZodError_1.ZodIssueCode.invalid_type:
-	            if (issue.received === util_1.ZodParsedType.undefined) {
-	                message = "Required";
-	            }
-	            else {
-	                message = `Expected ${issue.expected}, received ${issue.received}`;
-	            }
-	            break;
-	        case ZodError_1.ZodIssueCode.invalid_literal:
-	            message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util_1.util.jsonStringifyReplacer)}`;
-	            break;
-	        case ZodError_1.ZodIssueCode.unrecognized_keys:
-	            message = `Unrecognized key(s) in object: ${util_1.util.joinValues(issue.keys, ", ")}`;
-	            break;
-	        case ZodError_1.ZodIssueCode.invalid_union:
-	            message = `Invalid input`;
-	            break;
-	        case ZodError_1.ZodIssueCode.invalid_union_discriminator:
-	            message = `Invalid discriminator value. Expected ${util_1.util.joinValues(issue.options)}`;
-	            break;
-	        case ZodError_1.ZodIssueCode.invalid_enum_value:
-	            message = `Invalid enum value. Expected ${util_1.util.joinValues(issue.options)}, received '${issue.received}'`;
-	            break;
-	        case ZodError_1.ZodIssueCode.invalid_arguments:
-	            message = `Invalid function arguments`;
-	            break;
-	        case ZodError_1.ZodIssueCode.invalid_return_type:
-	            message = `Invalid function return type`;
-	            break;
-	        case ZodError_1.ZodIssueCode.invalid_date:
-	            message = `Invalid date`;
-	            break;
-	        case ZodError_1.ZodIssueCode.invalid_string:
-	            if (typeof issue.validation === "object") {
-	                if ("includes" in issue.validation) {
-	                    message = `Invalid input: must include "${issue.validation.includes}"`;
-	                    if (typeof issue.validation.position === "number") {
-	                        message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`;
-	                    }
-	                }
-	                else if ("startsWith" in issue.validation) {
-	                    message = `Invalid input: must start with "${issue.validation.startsWith}"`;
-	                }
-	                else if ("endsWith" in issue.validation) {
-	                    message = `Invalid input: must end with "${issue.validation.endsWith}"`;
-	                }
-	                else {
-	                    util_1.util.assertNever(issue.validation);
-	                }
-	            }
-	            else if (issue.validation !== "regex") {
-	                message = `Invalid ${issue.validation}`;
-	            }
-	            else {
-	                message = "Invalid";
-	            }
-	            break;
-	        case ZodError_1.ZodIssueCode.too_small:
-	            if (issue.type === "array")
-	                message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;
-	            else if (issue.type === "string")
-	                message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;
-	            else if (issue.type === "number")
-	                message = `Number must be ${issue.exact
-                    ? `exactly equal to `
-                    : issue.inclusive
-                        ? `greater than or equal to `
-                        : `greater than `}${issue.minimum}`;
-	            else if (issue.type === "date")
-	                message = `Date must be ${issue.exact
-                    ? `exactly equal to `
-                    : issue.inclusive
-                        ? `greater than or equal to `
-                        : `greater than `}${new Date(Number(issue.minimum))}`;
-	            else
-	                message = "Invalid input";
-	            break;
-	        case ZodError_1.ZodIssueCode.too_big:
-	            if (issue.type === "array")
-	                message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;
-	            else if (issue.type === "string")
-	                message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;
-	            else if (issue.type === "number")
-	                message = `Number must be ${issue.exact
-                    ? `exactly`
-                    : issue.inclusive
-                        ? `less than or equal to`
-                        : `less than`} ${issue.maximum}`;
-	            else if (issue.type === "bigint")
-	                message = `BigInt must be ${issue.exact
-                    ? `exactly`
-                    : issue.inclusive
-                        ? `less than or equal to`
-                        : `less than`} ${issue.maximum}`;
-	            else if (issue.type === "date")
-	                message = `Date must be ${issue.exact
-                    ? `exactly`
-                    : issue.inclusive
-                        ? `smaller than or equal to`
-                        : `smaller than`} ${new Date(Number(issue.maximum))}`;
-	            else
-	                message = "Invalid input";
-	            break;
-	        case ZodError_1.ZodIssueCode.custom:
-	            message = `Invalid input`;
-	            break;
-	        case ZodError_1.ZodIssueCode.invalid_intersection_types:
-	            message = `Intersection results could not be merged`;
-	            break;
-	        case ZodError_1.ZodIssueCode.not_multiple_of:
-	            message = `Number must be a multiple of ${issue.multipleOf}`;
-	            break;
-	        case ZodError_1.ZodIssueCode.not_finite:
-	            message = "Number must be finite";
-	            break;
-	        default:
-	            message = _ctx.defaultError;
-	            util_1.util.assertNever(issue);
-	    }
-	    return { message };
-	};
-	en.default = errorMap;
-
-	var __importDefault$1 = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
-	    return (mod && mod.__esModule) ? mod : { "default": mod };
-	};
-	Object.defineProperty(errors, "__esModule", { value: true });
-	errors.getErrorMap = errors.setErrorMap = errors.defaultErrorMap = void 0;
-	const en_1 = __importDefault$1(en);
-	errors.defaultErrorMap = en_1.default;
-	let overrideErrorMap = en_1.default;
-	function setErrorMap(map) {
-	    overrideErrorMap = map;
-	}
-	errors.setErrorMap = setErrorMap;
-	function getErrorMap() {
-	    return overrideErrorMap;
-	}
-	errors.getErrorMap = getErrorMap;
-
-	var parseUtil = {};
-
-	(function (exports) {
-		var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
-		    return (mod && mod.__esModule) ? mod : { "default": mod };
-		};
-		Object.defineProperty(exports, "__esModule", { value: true });
-		exports.isAsync = exports.isValid = exports.isDirty = exports.isAborted = exports.OK = exports.DIRTY = exports.INVALID = exports.ParseStatus = exports.addIssueToContext = exports.EMPTY_PATH = exports.makeIssue = void 0;
-		const errors_1 = errors;
-		const en_1 = __importDefault(en);
-		const makeIssue = (params) => {
-		    const { data, path, errorMaps, issueData } = params;
-		    const fullPath = [...path, ...(issueData.path || [])];
-		    const fullIssue = {
-		        ...issueData,
-		        path: fullPath,
-		    };
-		    let errorMessage = "";
-		    const maps = errorMaps
-		        .filter((m) => !!m)
-		        .slice()
-		        .reverse();
-		    for (const map of maps) {
-		        errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message;
-		    }
-		    return {
-		        ...issueData,
-		        path: fullPath,
-		        message: issueData.message || errorMessage,
-		    };
-		};
-		exports.makeIssue = makeIssue;
-		exports.EMPTY_PATH = [];
-		function addIssueToContext(ctx, issueData) {
-		    const issue = (0, exports.makeIssue)({
-		        issueData: issueData,
-		        data: ctx.data,
-		        path: ctx.path,
-		        errorMaps: [
-		            ctx.common.contextualErrorMap,
-		            ctx.schemaErrorMap,
-		            (0, errors_1.getErrorMap)(),
-		            en_1.default, // then global default map
-		        ].filter((x) => !!x),
-		    });
-		    ctx.common.issues.push(issue);
-		}
-		exports.addIssueToContext = addIssueToContext;
-		class ParseStatus {
-		    constructor() {
-		        this.value = "valid";
-		    }
-		    dirty() {
-		        if (this.value === "valid")
-		            this.value = "dirty";
-		    }
-		    abort() {
-		        if (this.value !== "aborted")
-		            this.value = "aborted";
-		    }
-		    static mergeArray(status, results) {
-		        const arrayValue = [];
-		        for (const s of results) {
-		            if (s.status === "aborted")
-		                return exports.INVALID;
-		            if (s.status === "dirty")
-		                status.dirty();
-		            arrayValue.push(s.value);
-		        }
-		        return { status: status.value, value: arrayValue };
-		    }
-		    static async mergeObjectAsync(status, pairs) {
-		        const syncPairs = [];
-		        for (const pair of pairs) {
-		            syncPairs.push({
-		                key: await pair.key,
-		                value: await pair.value,
-		            });
-		        }
-		        return ParseStatus.mergeObjectSync(status, syncPairs);
-		    }
-		    static mergeObjectSync(status, pairs) {
-		        const finalObject = {};
-		        for (const pair of pairs) {
-		            const { key, value } = pair;
-		            if (key.status === "aborted")
-		                return exports.INVALID;
-		            if (value.status === "aborted")
-		                return exports.INVALID;
-		            if (key.status === "dirty")
-		                status.dirty();
-		            if (value.status === "dirty")
-		                status.dirty();
-		            if (key.value !== "__proto__" &&
-		                (typeof value.value !== "undefined" || pair.alwaysSet)) {
-		                finalObject[key.value] = value.value;
-		            }
-		        }
-		        return { status: status.value, value: finalObject };
-		    }
-		}
-		exports.ParseStatus = ParseStatus;
-		exports.INVALID = Object.freeze({
-		    status: "aborted",
-		});
-		const DIRTY = (value) => ({ status: "dirty", value });
-		exports.DIRTY = DIRTY;
-		const OK = (value) => ({ status: "valid", value });
-		exports.OK = OK;
-		const isAborted = (x) => x.status === "aborted";
-		exports.isAborted = isAborted;
-		const isDirty = (x) => x.status === "dirty";
-		exports.isDirty = isDirty;
-		const isValid = (x) => x.status === "valid";
-		exports.isValid = isValid;
-		const isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
-		exports.isAsync = isAsync; 
-	} (parseUtil));
-
-	var typeAliases = {};
-
-	Object.defineProperty(typeAliases, "__esModule", { value: true });
-
-	var types = {};
-
-	var errorUtil = {};
-
-	(function (exports) {
-		Object.defineProperty(exports, "__esModule", { value: true });
-		exports.errorUtil = void 0;
-		(function (errorUtil) {
-		    errorUtil.errToObj = (message) => typeof message === "string" ? { message } : message || {};
-		    errorUtil.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message;
-		})(exports.errorUtil || (exports.errorUtil = {})); 
-	} (errorUtil));
-
-	(function (exports) {
-		Object.defineProperty(exports, "__esModule", { value: true });
-		exports.date = exports.boolean = exports.bigint = exports.array = exports.any = exports.coerce = exports.ZodFirstPartyTypeKind = exports.late = exports.ZodSchema = exports.Schema = exports.custom = exports.ZodReadonly = exports.ZodPipeline = exports.ZodBranded = exports.BRAND = exports.ZodNaN = exports.ZodCatch = exports.ZodDefault = exports.ZodNullable = exports.ZodOptional = exports.ZodTransformer = exports.ZodEffects = exports.ZodPromise = exports.ZodNativeEnum = exports.ZodEnum = exports.ZodLiteral = exports.ZodLazy = exports.ZodFunction = exports.ZodSet = exports.ZodMap = exports.ZodRecord = exports.ZodTuple = exports.ZodIntersection = exports.ZodDiscriminatedUnion = exports.ZodUnion = exports.ZodObject = exports.ZodArray = exports.ZodVoid = exports.ZodNever = exports.ZodUnknown = exports.ZodAny = exports.ZodNull = exports.ZodUndefined = exports.ZodSymbol = exports.ZodDate = exports.ZodBoolean = exports.ZodBigInt = exports.ZodNumber = exports.ZodString = exports.ZodType = void 0;
-		exports.NEVER = exports.void = exports.unknown = exports.union = exports.undefined = exports.tuple = exports.transformer = exports.symbol = exports.string = exports.strictObject = exports.set = exports.record = exports.promise = exports.preprocess = exports.pipeline = exports.ostring = exports.optional = exports.onumber = exports.oboolean = exports.object = exports.number = exports.nullable = exports.null = exports.never = exports.nativeEnum = exports.nan = exports.map = exports.literal = exports.lazy = exports.intersection = exports.instanceof = exports.function = exports.enum = exports.effect = exports.discriminatedUnion = void 0;
-		const errors_1 = errors;
-		const errorUtil_1 = errorUtil;
-		const parseUtil_1 = parseUtil;
-		const util_1 = util;
-		const ZodError_1 = ZodError$1;
-		class ParseInputLazyPath {
-		    constructor(parent, value, path, key) {
-		        this._cachedPath = [];
-		        this.parent = parent;
-		        this.data = value;
-		        this._path = path;
-		        this._key = key;
-		    }
-		    get path() {
-		        if (!this._cachedPath.length) {
-		            if (this._key instanceof Array) {
-		                this._cachedPath.push(...this._path, ...this._key);
-		            }
-		            else {
-		                this._cachedPath.push(...this._path, this._key);
-		            }
-		        }
-		        return this._cachedPath;
-		    }
-		}
-		const handleResult = (ctx, result) => {
-		    if ((0, parseUtil_1.isValid)(result)) {
-		        return { success: true, data: result.value };
-		    }
-		    else {
-		        if (!ctx.common.issues.length) {
-		            throw new Error("Validation failed but no issues detected.");
-		        }
-		        return {
-		            success: false,
-		            get error() {
-		                if (this._error)
-		                    return this._error;
-		                const error = new ZodError_1.ZodError(ctx.common.issues);
-		                this._error = error;
-		                return this._error;
-		            },
-		        };
-		    }
-		};
-		function processCreateParams(params) {
-		    if (!params)
-		        return {};
-		    const { errorMap, invalid_type_error, required_error, description } = params;
-		    if (errorMap && (invalid_type_error || required_error)) {
-		        throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);
-		    }
-		    if (errorMap)
-		        return { errorMap: errorMap, description };
-		    const customMap = (iss, ctx) => {
-		        if (iss.code !== "invalid_type")
-		            return { message: ctx.defaultError };
-		        if (typeof ctx.data === "undefined") {
-		            return { message: required_error !== null && required_error !== void 0 ? required_error : ctx.defaultError };
-		        }
-		        return { message: invalid_type_error !== null && invalid_type_error !== void 0 ? invalid_type_error : ctx.defaultError };
-		    };
-		    return { errorMap: customMap, description };
-		}
-		class ZodType {
-		    constructor(def) {
-		        /** Alias of safeParseAsync */
-		        this.spa = this.safeParseAsync;
-		        this._def = def;
-		        this.parse = this.parse.bind(this);
-		        this.safeParse = this.safeParse.bind(this);
-		        this.parseAsync = this.parseAsync.bind(this);
-		        this.safeParseAsync = this.safeParseAsync.bind(this);
-		        this.spa = this.spa.bind(this);
-		        this.refine = this.refine.bind(this);
-		        this.refinement = this.refinement.bind(this);
-		        this.superRefine = this.superRefine.bind(this);
-		        this.optional = this.optional.bind(this);
-		        this.nullable = this.nullable.bind(this);
-		        this.nullish = this.nullish.bind(this);
-		        this.array = this.array.bind(this);
-		        this.promise = this.promise.bind(this);
-		        this.or = this.or.bind(this);
-		        this.and = this.and.bind(this);
-		        this.transform = this.transform.bind(this);
-		        this.brand = this.brand.bind(this);
-		        this.default = this.default.bind(this);
-		        this.catch = this.catch.bind(this);
-		        this.describe = this.describe.bind(this);
-		        this.pipe = this.pipe.bind(this);
-		        this.readonly = this.readonly.bind(this);
-		        this.isNullable = this.isNullable.bind(this);
-		        this.isOptional = this.isOptional.bind(this);
-		    }
-		    get description() {
-		        return this._def.description;
-		    }
-		    _getType(input) {
-		        return (0, util_1.getParsedType)(input.data);
-		    }
-		    _getOrReturnCtx(input, ctx) {
-		        return (ctx || {
-		            common: input.parent.common,
-		            data: input.data,
-		            parsedType: (0, util_1.getParsedType)(input.data),
-		            schemaErrorMap: this._def.errorMap,
-		            path: input.path,
-		            parent: input.parent,
-		        });
-		    }
-		    _processInputParams(input) {
-		        return {
-		            status: new parseUtil_1.ParseStatus(),
-		            ctx: {
-		                common: input.parent.common,
-		                data: input.data,
-		                parsedType: (0, util_1.getParsedType)(input.data),
-		                schemaErrorMap: this._def.errorMap,
-		                path: input.path,
-		                parent: input.parent,
-		            },
-		        };
-		    }
-		    _parseSync(input) {
-		        const result = this._parse(input);
-		        if ((0, parseUtil_1.isAsync)(result)) {
-		            throw new Error("Synchronous parse encountered promise.");
-		        }
-		        return result;
-		    }
-		    _parseAsync(input) {
-		        const result = this._parse(input);
-		        return Promise.resolve(result);
-		    }
-		    parse(data, params) {
-		        const result = this.safeParse(data, params);
-		        if (result.success)
-		            return result.data;
-		        throw result.error;
-		    }
-		    safeParse(data, params) {
-		        var _a;
-		        const ctx = {
-		            common: {
-		                issues: [],
-		                async: (_a = params === null || params === void 0 ? void 0 : params.async) !== null && _a !== void 0 ? _a : false,
-		                contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap,
-		            },
-		            path: (params === null || params === void 0 ? void 0 : params.path) || [],
-		            schemaErrorMap: this._def.errorMap,
-		            parent: null,
-		            data,
-		            parsedType: (0, util_1.getParsedType)(data),
-		        };
-		        const result = this._parseSync({ data, path: ctx.path, parent: ctx });
-		        return handleResult(ctx, result);
-		    }
-		    async parseAsync(data, params) {
-		        const result = await this.safeParseAsync(data, params);
-		        if (result.success)
-		            return result.data;
-		        throw result.error;
-		    }
-		    async safeParseAsync(data, params) {
-		        const ctx = {
-		            common: {
-		                issues: [],
-		                contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap,
-		                async: true,
-		            },
-		            path: (params === null || params === void 0 ? void 0 : params.path) || [],
-		            schemaErrorMap: this._def.errorMap,
-		            parent: null,
-		            data,
-		            parsedType: (0, util_1.getParsedType)(data),
-		        };
-		        const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx });
-		        const result = await ((0, parseUtil_1.isAsync)(maybeAsyncResult)
-		            ? maybeAsyncResult
-		            : Promise.resolve(maybeAsyncResult));
-		        return handleResult(ctx, result);
-		    }
-		    refine(check, message) {
-		        const getIssueProperties = (val) => {
-		            if (typeof message === "string" || typeof message === "undefined") {
-		                return { message };
-		            }
-		            else if (typeof message === "function") {
-		                return message(val);
-		            }
-		            else {
-		                return message;
-		            }
-		        };
-		        return this._refinement((val, ctx) => {
-		            const result = check(val);
-		            const setError = () => ctx.addIssue({
-		                code: ZodError_1.ZodIssueCode.custom,
-		                ...getIssueProperties(val),
-		            });
-		            if (typeof Promise !== "undefined" && result instanceof Promise) {
-		                return result.then((data) => {
-		                    if (!data) {
-		                        setError();
-		                        return false;
-		                    }
-		                    else {
-		                        return true;
-		                    }
-		                });
-		            }
-		            if (!result) {
-		                setError();
-		                return false;
-		            }
-		            else {
-		                return true;
-		            }
-		        });
-		    }
-		    refinement(check, refinementData) {
-		        return this._refinement((val, ctx) => {
-		            if (!check(val)) {
-		                ctx.addIssue(typeof refinementData === "function"
-		                    ? refinementData(val, ctx)
-		                    : refinementData);
-		                return false;
-		            }
-		            else {
-		                return true;
-		            }
-		        });
-		    }
-		    _refinement(refinement) {
-		        return new ZodEffects({
-		            schema: this,
-		            typeName: ZodFirstPartyTypeKind.ZodEffects,
-		            effect: { type: "refinement", refinement },
-		        });
-		    }
-		    superRefine(refinement) {
-		        return this._refinement(refinement);
-		    }
-		    optional() {
-		        return ZodOptional.create(this, this._def);
-		    }
-		    nullable() {
-		        return ZodNullable.create(this, this._def);
-		    }
-		    nullish() {
-		        return this.nullable().optional();
-		    }
-		    array() {
-		        return ZodArray.create(this, this._def);
-		    }
-		    promise() {
-		        return ZodPromise.create(this, this._def);
-		    }
-		    or(option) {
-		        return ZodUnion.create([this, option], this._def);
-		    }
-		    and(incoming) {
-		        return ZodIntersection.create(this, incoming, this._def);
-		    }
-		    transform(transform) {
-		        return new ZodEffects({
-		            ...processCreateParams(this._def),
-		            schema: this,
-		            typeName: ZodFirstPartyTypeKind.ZodEffects,
-		            effect: { type: "transform", transform },
-		        });
-		    }
-		    default(def) {
-		        const defaultValueFunc = typeof def === "function" ? def : () => def;
-		        return new ZodDefault({
-		            ...processCreateParams(this._def),
-		            innerType: this,
-		            defaultValue: defaultValueFunc,
-		            typeName: ZodFirstPartyTypeKind.ZodDefault,
-		        });
-		    }
-		    brand() {
-		        return new ZodBranded({
-		            typeName: ZodFirstPartyTypeKind.ZodBranded,
-		            type: this,
-		            ...processCreateParams(this._def),
-		        });
-		    }
-		    catch(def) {
-		        const catchValueFunc = typeof def === "function" ? def : () => def;
-		        return new ZodCatch({
-		            ...processCreateParams(this._def),
-		            innerType: this,
-		            catchValue: catchValueFunc,
-		            typeName: ZodFirstPartyTypeKind.ZodCatch,
-		        });
-		    }
-		    describe(description) {
-		        const This = this.constructor;
-		        return new This({
-		            ...this._def,
-		            description,
-		        });
-		    }
-		    pipe(target) {
-		        return ZodPipeline.create(this, target);
-		    }
-		    readonly() {
-		        return ZodReadonly.create(this);
-		    }
-		    isOptional() {
-		        return this.safeParse(undefined).success;
-		    }
-		    isNullable() {
-		        return this.safeParse(null).success;
-		    }
-		}
-		exports.ZodType = ZodType;
-		exports.Schema = ZodType;
-		exports.ZodSchema = ZodType;
-		const cuidRegex = /^c[^\s-]{8,}$/i;
-		const cuid2Regex = /^[a-z][a-z0-9]*$/;
-		const ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/;
-		// const uuidRegex =
-		//   /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i;
-		const uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i;
-		// from https://stackoverflow.com/a/46181/1550155
-		// old version: too slow, didn't support unicode
-		// const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
-		//old email regex
-		// const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@((?!-)([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{1,})[^-<>()[\].,;:\s@"]$/i;
-		// eslint-disable-next-line
-		// const emailRegex =
-		//   /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/;
-		// const emailRegex =
-		//   /^[a-zA-Z0-9\.\!\#\$\%\&\'\*\+\/\=\?\^\_\`\{\|\}\~\-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
-		// const emailRegex =
-		//   /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i;
-		const emailRegex = /^(?!\.)(?!.*\.\.)([A-Z0-9_+-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i;
-		// const emailRegex =
-		//   /^[a-z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-z0-9-]+(?:\.[a-z0-9\-]+)*$/i;
-		// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression
-		const _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;
-		let emojiRegex;
-		const ipv4Regex = /^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
-		const ipv6Regex = /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;
-		// Adapted from https://stackoverflow.com/a/3143231
-		const datetimeRegex = (args) => {
-		    if (args.precision) {
-		        if (args.offset) {
-		            return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${args.precision}}(([+-]\\d{2}(:?\\d{2})?)|Z)$`);
-		        }
-		        else {
-		            return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${args.precision}}Z$`);
-		        }
-		    }
-		    else if (args.precision === 0) {
-		        if (args.offset) {
-		            return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(([+-]\\d{2}(:?\\d{2})?)|Z)$`);
-		        }
-		        else {
-		            return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$`);
-		        }
-		    }
-		    else {
-		        if (args.offset) {
-		            return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(([+-]\\d{2}(:?\\d{2})?)|Z)$`);
-		        }
-		        else {
-		            return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$`);
-		        }
-		    }
-		};
-		function isValidIP(ip, version) {
-		    if ((version === "v4" || !version) && ipv4Regex.test(ip)) {
-		        return true;
-		    }
-		    if ((version === "v6" || !version) && ipv6Regex.test(ip)) {
-		        return true;
-		    }
-		    return false;
-		}
-		class ZodString extends ZodType {
-		    _parse(input) {
-		        if (this._def.coerce) {
-		            input.data = String(input.data);
-		        }
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.string) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.string,
-		                received: ctx.parsedType,
-		            }
-		            //
-		            );
-		            return parseUtil_1.INVALID;
-		        }
-		        const status = new parseUtil_1.ParseStatus();
-		        let ctx = undefined;
-		        for (const check of this._def.checks) {
-		            if (check.kind === "min") {
-		                if (input.data.length < check.value) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.too_small,
-		                        minimum: check.value,
-		                        type: "string",
-		                        inclusive: true,
-		                        exact: false,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "max") {
-		                if (input.data.length > check.value) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.too_big,
-		                        maximum: check.value,
-		                        type: "string",
-		                        inclusive: true,
-		                        exact: false,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "length") {
-		                const tooBig = input.data.length > check.value;
-		                const tooSmall = input.data.length < check.value;
-		                if (tooBig || tooSmall) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    if (tooBig) {
-		                        (0, parseUtil_1.addIssueToContext)(ctx, {
-		                            code: ZodError_1.ZodIssueCode.too_big,
-		                            maximum: check.value,
-		                            type: "string",
-		                            inclusive: true,
-		                            exact: true,
-		                            message: check.message,
-		                        });
-		                    }
-		                    else if (tooSmall) {
-		                        (0, parseUtil_1.addIssueToContext)(ctx, {
-		                            code: ZodError_1.ZodIssueCode.too_small,
-		                            minimum: check.value,
-		                            type: "string",
-		                            inclusive: true,
-		                            exact: true,
-		                            message: check.message,
-		                        });
-		                    }
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "email") {
-		                if (!emailRegex.test(input.data)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        validation: "email",
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "emoji") {
-		                if (!emojiRegex) {
-		                    emojiRegex = new RegExp(_emojiRegex, "u");
-		                }
-		                if (!emojiRegex.test(input.data)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        validation: "emoji",
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "uuid") {
-		                if (!uuidRegex.test(input.data)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        validation: "uuid",
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "cuid") {
-		                if (!cuidRegex.test(input.data)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        validation: "cuid",
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "cuid2") {
-		                if (!cuid2Regex.test(input.data)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        validation: "cuid2",
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "ulid") {
-		                if (!ulidRegex.test(input.data)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        validation: "ulid",
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "url") {
-		                try {
-		                    new URL(input.data);
-		                }
-		                catch (_a) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        validation: "url",
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "regex") {
-		                check.regex.lastIndex = 0;
-		                const testResult = check.regex.test(input.data);
-		                if (!testResult) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        validation: "regex",
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "trim") {
-		                input.data = input.data.trim();
-		            }
-		            else if (check.kind === "includes") {
-		                if (!input.data.includes(check.value, check.position)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        validation: { includes: check.value, position: check.position },
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "toLowerCase") {
-		                input.data = input.data.toLowerCase();
-		            }
-		            else if (check.kind === "toUpperCase") {
-		                input.data = input.data.toUpperCase();
-		            }
-		            else if (check.kind === "startsWith") {
-		                if (!input.data.startsWith(check.value)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        validation: { startsWith: check.value },
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "endsWith") {
-		                if (!input.data.endsWith(check.value)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        validation: { endsWith: check.value },
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "datetime") {
-		                const regex = datetimeRegex(check);
-		                if (!regex.test(input.data)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        validation: "datetime",
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "ip") {
-		                if (!isValidIP(input.data, check.version)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        validation: "ip",
-		                        code: ZodError_1.ZodIssueCode.invalid_string,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else {
-		                util_1.util.assertNever(check);
-		            }
-		        }
-		        return { status: status.value, value: input.data };
-		    }
-		    _regex(regex, validation, message) {
-		        return this.refinement((data) => regex.test(data), {
-		            validation,
-		            code: ZodError_1.ZodIssueCode.invalid_string,
-		            ...errorUtil_1.errorUtil.errToObj(message),
-		        });
-		    }
-		    _addCheck(check) {
-		        return new ZodString({
-		            ...this._def,
-		            checks: [...this._def.checks, check],
-		        });
-		    }
-		    email(message) {
-		        return this._addCheck({ kind: "email", ...errorUtil_1.errorUtil.errToObj(message) });
-		    }
-		    url(message) {
-		        return this._addCheck({ kind: "url", ...errorUtil_1.errorUtil.errToObj(message) });
-		    }
-		    emoji(message) {
-		        return this._addCheck({ kind: "emoji", ...errorUtil_1.errorUtil.errToObj(message) });
-		    }
-		    uuid(message) {
-		        return this._addCheck({ kind: "uuid", ...errorUtil_1.errorUtil.errToObj(message) });
-		    }
-		    cuid(message) {
-		        return this._addCheck({ kind: "cuid", ...errorUtil_1.errorUtil.errToObj(message) });
-		    }
-		    cuid2(message) {
-		        return this._addCheck({ kind: "cuid2", ...errorUtil_1.errorUtil.errToObj(message) });
-		    }
-		    ulid(message) {
-		        return this._addCheck({ kind: "ulid", ...errorUtil_1.errorUtil.errToObj(message) });
-		    }
-		    ip(options) {
-		        return this._addCheck({ kind: "ip", ...errorUtil_1.errorUtil.errToObj(options) });
-		    }
-		    datetime(options) {
-		        var _a;
-		        if (typeof options === "string") {
-		            return this._addCheck({
-		                kind: "datetime",
-		                precision: null,
-		                offset: false,
-		                message: options,
-		            });
-		        }
-		        return this._addCheck({
-		            kind: "datetime",
-		            precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === "undefined" ? null : options === null || options === void 0 ? void 0 : options.precision,
-		            offset: (_a = options === null || options === void 0 ? void 0 : options.offset) !== null && _a !== void 0 ? _a : false,
-		            ...errorUtil_1.errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message),
-		        });
-		    }
-		    regex(regex, message) {
-		        return this._addCheck({
-		            kind: "regex",
-		            regex: regex,
-		            ...errorUtil_1.errorUtil.errToObj(message),
-		        });
-		    }
-		    includes(value, options) {
-		        return this._addCheck({
-		            kind: "includes",
-		            value: value,
-		            position: options === null || options === void 0 ? void 0 : options.position,
-		            ...errorUtil_1.errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message),
-		        });
-		    }
-		    startsWith(value, message) {
-		        return this._addCheck({
-		            kind: "startsWith",
-		            value: value,
-		            ...errorUtil_1.errorUtil.errToObj(message),
-		        });
-		    }
-		    endsWith(value, message) {
-		        return this._addCheck({
-		            kind: "endsWith",
-		            value: value,
-		            ...errorUtil_1.errorUtil.errToObj(message),
-		        });
-		    }
-		    min(minLength, message) {
-		        return this._addCheck({
-		            kind: "min",
-		            value: minLength,
-		            ...errorUtil_1.errorUtil.errToObj(message),
-		        });
-		    }
-		    max(maxLength, message) {
-		        return this._addCheck({
-		            kind: "max",
-		            value: maxLength,
-		            ...errorUtil_1.errorUtil.errToObj(message),
-		        });
-		    }
-		    length(len, message) {
-		        return this._addCheck({
-		            kind: "length",
-		            value: len,
-		            ...errorUtil_1.errorUtil.errToObj(message),
-		        });
-		    }
-		    /**
-		     * @deprecated Use z.string().min(1) instead.
-		     * @see {@link ZodString.min}
-		     */
-		    nonempty(message) {
-		        return this.min(1, errorUtil_1.errorUtil.errToObj(message));
-		    }
-		    trim() {
-		        return new ZodString({
-		            ...this._def,
-		            checks: [...this._def.checks, { kind: "trim" }],
-		        });
-		    }
-		    toLowerCase() {
-		        return new ZodString({
-		            ...this._def,
-		            checks: [...this._def.checks, { kind: "toLowerCase" }],
-		        });
-		    }
-		    toUpperCase() {
-		        return new ZodString({
-		            ...this._def,
-		            checks: [...this._def.checks, { kind: "toUpperCase" }],
-		        });
-		    }
-		    get isDatetime() {
-		        return !!this._def.checks.find((ch) => ch.kind === "datetime");
-		    }
-		    get isEmail() {
-		        return !!this._def.checks.find((ch) => ch.kind === "email");
-		    }
-		    get isURL() {
-		        return !!this._def.checks.find((ch) => ch.kind === "url");
-		    }
-		    get isEmoji() {
-		        return !!this._def.checks.find((ch) => ch.kind === "emoji");
-		    }
-		    get isUUID() {
-		        return !!this._def.checks.find((ch) => ch.kind === "uuid");
-		    }
-		    get isCUID() {
-		        return !!this._def.checks.find((ch) => ch.kind === "cuid");
-		    }
-		    get isCUID2() {
-		        return !!this._def.checks.find((ch) => ch.kind === "cuid2");
-		    }
-		    get isULID() {
-		        return !!this._def.checks.find((ch) => ch.kind === "ulid");
-		    }
-		    get isIP() {
-		        return !!this._def.checks.find((ch) => ch.kind === "ip");
-		    }
-		    get minLength() {
-		        let min = null;
-		        for (const ch of this._def.checks) {
-		            if (ch.kind === "min") {
-		                if (min === null || ch.value > min)
-		                    min = ch.value;
-		            }
-		        }
-		        return min;
-		    }
-		    get maxLength() {
-		        let max = null;
-		        for (const ch of this._def.checks) {
-		            if (ch.kind === "max") {
-		                if (max === null || ch.value < max)
-		                    max = ch.value;
-		            }
-		        }
-		        return max;
-		    }
-		}
-		exports.ZodString = ZodString;
-		ZodString.create = (params) => {
-		    var _a;
-		    return new ZodString({
-		        checks: [],
-		        typeName: ZodFirstPartyTypeKind.ZodString,
-		        coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false,
-		        ...processCreateParams(params),
-		    });
-		};
-		// https://stackoverflow.com/questions/3966484/why-does-modulus-operator-return-fractional-number-in-javascript/31711034#31711034
-		function floatSafeRemainder(val, step) {
-		    const valDecCount = (val.toString().split(".")[1] || "").length;
-		    const stepDecCount = (step.toString().split(".")[1] || "").length;
-		    const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;
-		    const valInt = parseInt(val.toFixed(decCount).replace(".", ""));
-		    const stepInt = parseInt(step.toFixed(decCount).replace(".", ""));
-		    return (valInt % stepInt) / Math.pow(10, decCount);
-		}
-		class ZodNumber extends ZodType {
-		    constructor() {
-		        super(...arguments);
-		        this.min = this.gte;
-		        this.max = this.lte;
-		        this.step = this.multipleOf;
-		    }
-		    _parse(input) {
-		        if (this._def.coerce) {
-		            input.data = Number(input.data);
-		        }
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.number) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.number,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        let ctx = undefined;
-		        const status = new parseUtil_1.ParseStatus();
-		        for (const check of this._def.checks) {
-		            if (check.kind === "int") {
-		                if (!util_1.util.isInteger(input.data)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.invalid_type,
-		                        expected: "integer",
-		                        received: "float",
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "min") {
-		                const tooSmall = check.inclusive
-		                    ? input.data < check.value
-		                    : input.data <= check.value;
-		                if (tooSmall) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.too_small,
-		                        minimum: check.value,
-		                        type: "number",
-		                        inclusive: check.inclusive,
-		                        exact: false,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "max") {
-		                const tooBig = check.inclusive
-		                    ? input.data > check.value
-		                    : input.data >= check.value;
-		                if (tooBig) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.too_big,
-		                        maximum: check.value,
-		                        type: "number",
-		                        inclusive: check.inclusive,
-		                        exact: false,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "multipleOf") {
-		                if (floatSafeRemainder(input.data, check.value) !== 0) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.not_multiple_of,
-		                        multipleOf: check.value,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "finite") {
-		                if (!Number.isFinite(input.data)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.not_finite,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else {
-		                util_1.util.assertNever(check);
-		            }
-		        }
-		        return { status: status.value, value: input.data };
-		    }
-		    gte(value, message) {
-		        return this.setLimit("min", value, true, errorUtil_1.errorUtil.toString(message));
-		    }
-		    gt(value, message) {
-		        return this.setLimit("min", value, false, errorUtil_1.errorUtil.toString(message));
-		    }
-		    lte(value, message) {
-		        return this.setLimit("max", value, true, errorUtil_1.errorUtil.toString(message));
-		    }
-		    lt(value, message) {
-		        return this.setLimit("max", value, false, errorUtil_1.errorUtil.toString(message));
-		    }
-		    setLimit(kind, value, inclusive, message) {
-		        return new ZodNumber({
-		            ...this._def,
-		            checks: [
-		                ...this._def.checks,
-		                {
-		                    kind,
-		                    value,
-		                    inclusive,
-		                    message: errorUtil_1.errorUtil.toString(message),
-		                },
-		            ],
-		        });
-		    }
-		    _addCheck(check) {
-		        return new ZodNumber({
-		            ...this._def,
-		            checks: [...this._def.checks, check],
-		        });
-		    }
-		    int(message) {
-		        return this._addCheck({
-		            kind: "int",
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    positive(message) {
-		        return this._addCheck({
-		            kind: "min",
-		            value: 0,
-		            inclusive: false,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    negative(message) {
-		        return this._addCheck({
-		            kind: "max",
-		            value: 0,
-		            inclusive: false,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    nonpositive(message) {
-		        return this._addCheck({
-		            kind: "max",
-		            value: 0,
-		            inclusive: true,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    nonnegative(message) {
-		        return this._addCheck({
-		            kind: "min",
-		            value: 0,
-		            inclusive: true,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    multipleOf(value, message) {
-		        return this._addCheck({
-		            kind: "multipleOf",
-		            value: value,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    finite(message) {
-		        return this._addCheck({
-		            kind: "finite",
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    safe(message) {
-		        return this._addCheck({
-		            kind: "min",
-		            inclusive: true,
-		            value: Number.MIN_SAFE_INTEGER,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        })._addCheck({
-		            kind: "max",
-		            inclusive: true,
-		            value: Number.MAX_SAFE_INTEGER,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    get minValue() {
-		        let min = null;
-		        for (const ch of this._def.checks) {
-		            if (ch.kind === "min") {
-		                if (min === null || ch.value > min)
-		                    min = ch.value;
-		            }
-		        }
-		        return min;
-		    }
-		    get maxValue() {
-		        let max = null;
-		        for (const ch of this._def.checks) {
-		            if (ch.kind === "max") {
-		                if (max === null || ch.value < max)
-		                    max = ch.value;
-		            }
-		        }
-		        return max;
-		    }
-		    get isInt() {
-		        return !!this._def.checks.find((ch) => ch.kind === "int" ||
-		            (ch.kind === "multipleOf" && util_1.util.isInteger(ch.value)));
-		    }
-		    get isFinite() {
-		        let max = null, min = null;
-		        for (const ch of this._def.checks) {
-		            if (ch.kind === "finite" ||
-		                ch.kind === "int" ||
-		                ch.kind === "multipleOf") {
-		                return true;
-		            }
-		            else if (ch.kind === "min") {
-		                if (min === null || ch.value > min)
-		                    min = ch.value;
-		            }
-		            else if (ch.kind === "max") {
-		                if (max === null || ch.value < max)
-		                    max = ch.value;
-		            }
-		        }
-		        return Number.isFinite(min) && Number.isFinite(max);
-		    }
-		}
-		exports.ZodNumber = ZodNumber;
-		ZodNumber.create = (params) => {
-		    return new ZodNumber({
-		        checks: [],
-		        typeName: ZodFirstPartyTypeKind.ZodNumber,
-		        coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodBigInt extends ZodType {
-		    constructor() {
-		        super(...arguments);
-		        this.min = this.gte;
-		        this.max = this.lte;
-		    }
-		    _parse(input) {
-		        if (this._def.coerce) {
-		            input.data = BigInt(input.data);
-		        }
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.bigint) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.bigint,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        let ctx = undefined;
-		        const status = new parseUtil_1.ParseStatus();
-		        for (const check of this._def.checks) {
-		            if (check.kind === "min") {
-		                const tooSmall = check.inclusive
-		                    ? input.data < check.value
-		                    : input.data <= check.value;
-		                if (tooSmall) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.too_small,
-		                        type: "bigint",
-		                        minimum: check.value,
-		                        inclusive: check.inclusive,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "max") {
-		                const tooBig = check.inclusive
-		                    ? input.data > check.value
-		                    : input.data >= check.value;
-		                if (tooBig) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.too_big,
-		                        type: "bigint",
-		                        maximum: check.value,
-		                        inclusive: check.inclusive,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "multipleOf") {
-		                if (input.data % check.value !== BigInt(0)) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.not_multiple_of,
-		                        multipleOf: check.value,
-		                        message: check.message,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else {
-		                util_1.util.assertNever(check);
-		            }
-		        }
-		        return { status: status.value, value: input.data };
-		    }
-		    gte(value, message) {
-		        return this.setLimit("min", value, true, errorUtil_1.errorUtil.toString(message));
-		    }
-		    gt(value, message) {
-		        return this.setLimit("min", value, false, errorUtil_1.errorUtil.toString(message));
-		    }
-		    lte(value, message) {
-		        return this.setLimit("max", value, true, errorUtil_1.errorUtil.toString(message));
-		    }
-		    lt(value, message) {
-		        return this.setLimit("max", value, false, errorUtil_1.errorUtil.toString(message));
-		    }
-		    setLimit(kind, value, inclusive, message) {
-		        return new ZodBigInt({
-		            ...this._def,
-		            checks: [
-		                ...this._def.checks,
-		                {
-		                    kind,
-		                    value,
-		                    inclusive,
-		                    message: errorUtil_1.errorUtil.toString(message),
-		                },
-		            ],
-		        });
-		    }
-		    _addCheck(check) {
-		        return new ZodBigInt({
-		            ...this._def,
-		            checks: [...this._def.checks, check],
-		        });
-		    }
-		    positive(message) {
-		        return this._addCheck({
-		            kind: "min",
-		            value: BigInt(0),
-		            inclusive: false,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    negative(message) {
-		        return this._addCheck({
-		            kind: "max",
-		            value: BigInt(0),
-		            inclusive: false,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    nonpositive(message) {
-		        return this._addCheck({
-		            kind: "max",
-		            value: BigInt(0),
-		            inclusive: true,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    nonnegative(message) {
-		        return this._addCheck({
-		            kind: "min",
-		            value: BigInt(0),
-		            inclusive: true,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    multipleOf(value, message) {
-		        return this._addCheck({
-		            kind: "multipleOf",
-		            value,
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    get minValue() {
-		        let min = null;
-		        for (const ch of this._def.checks) {
-		            if (ch.kind === "min") {
-		                if (min === null || ch.value > min)
-		                    min = ch.value;
-		            }
-		        }
-		        return min;
-		    }
-		    get maxValue() {
-		        let max = null;
-		        for (const ch of this._def.checks) {
-		            if (ch.kind === "max") {
-		                if (max === null || ch.value < max)
-		                    max = ch.value;
-		            }
-		        }
-		        return max;
-		    }
-		}
-		exports.ZodBigInt = ZodBigInt;
-		ZodBigInt.create = (params) => {
-		    var _a;
-		    return new ZodBigInt({
-		        checks: [],
-		        typeName: ZodFirstPartyTypeKind.ZodBigInt,
-		        coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodBoolean extends ZodType {
-		    _parse(input) {
-		        if (this._def.coerce) {
-		            input.data = Boolean(input.data);
-		        }
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.boolean) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.boolean,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        return (0, parseUtil_1.OK)(input.data);
-		    }
-		}
-		exports.ZodBoolean = ZodBoolean;
-		ZodBoolean.create = (params) => {
-		    return new ZodBoolean({
-		        typeName: ZodFirstPartyTypeKind.ZodBoolean,
-		        coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodDate extends ZodType {
-		    _parse(input) {
-		        if (this._def.coerce) {
-		            input.data = new Date(input.data);
-		        }
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.date) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.date,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        if (isNaN(input.data.getTime())) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_date,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        const status = new parseUtil_1.ParseStatus();
-		        let ctx = undefined;
-		        for (const check of this._def.checks) {
-		            if (check.kind === "min") {
-		                if (input.data.getTime() < check.value) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.too_small,
-		                        message: check.message,
-		                        inclusive: true,
-		                        exact: false,
-		                        minimum: check.value,
-		                        type: "date",
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (check.kind === "max") {
-		                if (input.data.getTime() > check.value) {
-		                    ctx = this._getOrReturnCtx(input, ctx);
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.too_big,
-		                        message: check.message,
-		                        inclusive: true,
-		                        exact: false,
-		                        maximum: check.value,
-		                        type: "date",
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else {
-		                util_1.util.assertNever(check);
-		            }
-		        }
-		        return {
-		            status: status.value,
-		            value: new Date(input.data.getTime()),
-		        };
-		    }
-		    _addCheck(check) {
-		        return new ZodDate({
-		            ...this._def,
-		            checks: [...this._def.checks, check],
-		        });
-		    }
-		    min(minDate, message) {
-		        return this._addCheck({
-		            kind: "min",
-		            value: minDate.getTime(),
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    max(maxDate, message) {
-		        return this._addCheck({
-		            kind: "max",
-		            value: maxDate.getTime(),
-		            message: errorUtil_1.errorUtil.toString(message),
-		        });
-		    }
-		    get minDate() {
-		        let min = null;
-		        for (const ch of this._def.checks) {
-		            if (ch.kind === "min") {
-		                if (min === null || ch.value > min)
-		                    min = ch.value;
-		            }
-		        }
-		        return min != null ? new Date(min) : null;
-		    }
-		    get maxDate() {
-		        let max = null;
-		        for (const ch of this._def.checks) {
-		            if (ch.kind === "max") {
-		                if (max === null || ch.value < max)
-		                    max = ch.value;
-		            }
-		        }
-		        return max != null ? new Date(max) : null;
-		    }
-		}
-		exports.ZodDate = ZodDate;
-		ZodDate.create = (params) => {
-		    return new ZodDate({
-		        checks: [],
-		        coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,
-		        typeName: ZodFirstPartyTypeKind.ZodDate,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodSymbol extends ZodType {
-		    _parse(input) {
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.symbol) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.symbol,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        return (0, parseUtil_1.OK)(input.data);
-		    }
-		}
-		exports.ZodSymbol = ZodSymbol;
-		ZodSymbol.create = (params) => {
-		    return new ZodSymbol({
-		        typeName: ZodFirstPartyTypeKind.ZodSymbol,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodUndefined extends ZodType {
-		    _parse(input) {
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.undefined) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.undefined,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        return (0, parseUtil_1.OK)(input.data);
-		    }
-		}
-		exports.ZodUndefined = ZodUndefined;
-		ZodUndefined.create = (params) => {
-		    return new ZodUndefined({
-		        typeName: ZodFirstPartyTypeKind.ZodUndefined,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodNull extends ZodType {
-		    _parse(input) {
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.null) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.null,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        return (0, parseUtil_1.OK)(input.data);
-		    }
-		}
-		exports.ZodNull = ZodNull;
-		ZodNull.create = (params) => {
-		    return new ZodNull({
-		        typeName: ZodFirstPartyTypeKind.ZodNull,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodAny extends ZodType {
-		    constructor() {
-		        super(...arguments);
-		        // to prevent instances of other classes from extending ZodAny. this causes issues with catchall in ZodObject.
-		        this._any = true;
-		    }
-		    _parse(input) {
-		        return (0, parseUtil_1.OK)(input.data);
-		    }
-		}
-		exports.ZodAny = ZodAny;
-		ZodAny.create = (params) => {
-		    return new ZodAny({
-		        typeName: ZodFirstPartyTypeKind.ZodAny,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodUnknown extends ZodType {
-		    constructor() {
-		        super(...arguments);
-		        // required
-		        this._unknown = true;
-		    }
-		    _parse(input) {
-		        return (0, parseUtil_1.OK)(input.data);
-		    }
-		}
-		exports.ZodUnknown = ZodUnknown;
-		ZodUnknown.create = (params) => {
-		    return new ZodUnknown({
-		        typeName: ZodFirstPartyTypeKind.ZodUnknown,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodNever extends ZodType {
-		    _parse(input) {
-		        const ctx = this._getOrReturnCtx(input);
-		        (0, parseUtil_1.addIssueToContext)(ctx, {
-		            code: ZodError_1.ZodIssueCode.invalid_type,
-		            expected: util_1.ZodParsedType.never,
-		            received: ctx.parsedType,
-		        });
-		        return parseUtil_1.INVALID;
-		    }
-		}
-		exports.ZodNever = ZodNever;
-		ZodNever.create = (params) => {
-		    return new ZodNever({
-		        typeName: ZodFirstPartyTypeKind.ZodNever,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodVoid extends ZodType {
-		    _parse(input) {
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.undefined) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.void,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        return (0, parseUtil_1.OK)(input.data);
-		    }
-		}
-		exports.ZodVoid = ZodVoid;
-		ZodVoid.create = (params) => {
-		    return new ZodVoid({
-		        typeName: ZodFirstPartyTypeKind.ZodVoid,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodArray extends ZodType {
-		    _parse(input) {
-		        const { ctx, status } = this._processInputParams(input);
-		        const def = this._def;
-		        if (ctx.parsedType !== util_1.ZodParsedType.array) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.array,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        if (def.exactLength !== null) {
-		            const tooBig = ctx.data.length > def.exactLength.value;
-		            const tooSmall = ctx.data.length < def.exactLength.value;
-		            if (tooBig || tooSmall) {
-		                (0, parseUtil_1.addIssueToContext)(ctx, {
-		                    code: tooBig ? ZodError_1.ZodIssueCode.too_big : ZodError_1.ZodIssueCode.too_small,
-		                    minimum: (tooSmall ? def.exactLength.value : undefined),
-		                    maximum: (tooBig ? def.exactLength.value : undefined),
-		                    type: "array",
-		                    inclusive: true,
-		                    exact: true,
-		                    message: def.exactLength.message,
-		                });
-		                status.dirty();
-		            }
-		        }
-		        if (def.minLength !== null) {
-		            if (ctx.data.length < def.minLength.value) {
-		                (0, parseUtil_1.addIssueToContext)(ctx, {
-		                    code: ZodError_1.ZodIssueCode.too_small,
-		                    minimum: def.minLength.value,
-		                    type: "array",
-		                    inclusive: true,
-		                    exact: false,
-		                    message: def.minLength.message,
-		                });
-		                status.dirty();
-		            }
-		        }
-		        if (def.maxLength !== null) {
-		            if (ctx.data.length > def.maxLength.value) {
-		                (0, parseUtil_1.addIssueToContext)(ctx, {
-		                    code: ZodError_1.ZodIssueCode.too_big,
-		                    maximum: def.maxLength.value,
-		                    type: "array",
-		                    inclusive: true,
-		                    exact: false,
-		                    message: def.maxLength.message,
-		                });
-		                status.dirty();
-		            }
-		        }
-		        if (ctx.common.async) {
-		            return Promise.all([...ctx.data].map((item, i) => {
-		                return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i));
-		            })).then((result) => {
-		                return parseUtil_1.ParseStatus.mergeArray(status, result);
-		            });
-		        }
-		        const result = [...ctx.data].map((item, i) => {
-		            return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i));
-		        });
-		        return parseUtil_1.ParseStatus.mergeArray(status, result);
-		    }
-		    get element() {
-		        return this._def.type;
-		    }
-		    min(minLength, message) {
-		        return new ZodArray({
-		            ...this._def,
-		            minLength: { value: minLength, message: errorUtil_1.errorUtil.toString(message) },
-		        });
-		    }
-		    max(maxLength, message) {
-		        return new ZodArray({
-		            ...this._def,
-		            maxLength: { value: maxLength, message: errorUtil_1.errorUtil.toString(message) },
-		        });
-		    }
-		    length(len, message) {
-		        return new ZodArray({
-		            ...this._def,
-		            exactLength: { value: len, message: errorUtil_1.errorUtil.toString(message) },
-		        });
-		    }
-		    nonempty(message) {
-		        return this.min(1, message);
-		    }
-		}
-		exports.ZodArray = ZodArray;
-		ZodArray.create = (schema, params) => {
-		    return new ZodArray({
-		        type: schema,
-		        minLength: null,
-		        maxLength: null,
-		        exactLength: null,
-		        typeName: ZodFirstPartyTypeKind.ZodArray,
-		        ...processCreateParams(params),
-		    });
-		};
-		function deepPartialify(schema) {
-		    if (schema instanceof ZodObject) {
-		        const newShape = {};
-		        for (const key in schema.shape) {
-		            const fieldSchema = schema.shape[key];
-		            newShape[key] = ZodOptional.create(deepPartialify(fieldSchema));
-		        }
-		        return new ZodObject({
-		            ...schema._def,
-		            shape: () => newShape,
-		        });
-		    }
-		    else if (schema instanceof ZodArray) {
-		        return new ZodArray({
-		            ...schema._def,
-		            type: deepPartialify(schema.element),
-		        });
-		    }
-		    else if (schema instanceof ZodOptional) {
-		        return ZodOptional.create(deepPartialify(schema.unwrap()));
-		    }
-		    else if (schema instanceof ZodNullable) {
-		        return ZodNullable.create(deepPartialify(schema.unwrap()));
-		    }
-		    else if (schema instanceof ZodTuple) {
-		        return ZodTuple.create(schema.items.map((item) => deepPartialify(item)));
-		    }
-		    else {
-		        return schema;
-		    }
-		}
-		class ZodObject extends ZodType {
-		    constructor() {
-		        super(...arguments);
-		        this._cached = null;
-		        /**
-		         * @deprecated In most cases, this is no longer needed - unknown properties are now silently stripped.
-		         * If you want to pass through unknown properties, use `.passthrough()` instead.
-		         */
-		        this.nonstrict = this.passthrough;
-		        // extend<
-		        //   Augmentation extends ZodRawShape,
-		        //   NewOutput extends util.flatten<{
-		        //     [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation
-		        //       ? Augmentation[k]["_output"]
-		        //       : k extends keyof Output
-		        //       ? Output[k]
-		        //       : never;
-		        //   }>,
-		        //   NewInput extends util.flatten<{
-		        //     [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation
-		        //       ? Augmentation[k]["_input"]
-		        //       : k extends keyof Input
-		        //       ? Input[k]
-		        //       : never;
-		        //   }>
-		        // >(
-		        //   augmentation: Augmentation
-		        // ): ZodObject<
-		        //   extendShape<T, Augmentation>,
-		        //   UnknownKeys,
-		        //   Catchall,
-		        //   NewOutput,
-		        //   NewInput
-		        // > {
-		        //   return new ZodObject({
-		        //     ...this._def,
-		        //     shape: () => ({
-		        //       ...this._def.shape(),
-		        //       ...augmentation,
-		        //     }),
-		        //   }) as any;
-		        // }
-		        /**
-		         * @deprecated Use `.extend` instead
-		         *  */
-		        this.augment = this.extend;
-		    }
-		    _getCached() {
-		        if (this._cached !== null)
-		            return this._cached;
-		        const shape = this._def.shape();
-		        const keys = util_1.util.objectKeys(shape);
-		        return (this._cached = { shape, keys });
-		    }
-		    _parse(input) {
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.object) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.object,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        const { status, ctx } = this._processInputParams(input);
-		        const { shape, keys: shapeKeys } = this._getCached();
-		        const extraKeys = [];
-		        if (!(this._def.catchall instanceof ZodNever &&
-		            this._def.unknownKeys === "strip")) {
-		            for (const key in ctx.data) {
-		                if (!shapeKeys.includes(key)) {
-		                    extraKeys.push(key);
-		                }
-		            }
-		        }
-		        const pairs = [];
-		        for (const key of shapeKeys) {
-		            const keyValidator = shape[key];
-		            const value = ctx.data[key];
-		            pairs.push({
-		                key: { status: "valid", value: key },
-		                value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)),
-		                alwaysSet: key in ctx.data,
-		            });
-		        }
-		        if (this._def.catchall instanceof ZodNever) {
-		            const unknownKeys = this._def.unknownKeys;
-		            if (unknownKeys === "passthrough") {
-		                for (const key of extraKeys) {
-		                    pairs.push({
-		                        key: { status: "valid", value: key },
-		                        value: { status: "valid", value: ctx.data[key] },
-		                    });
-		                }
-		            }
-		            else if (unknownKeys === "strict") {
-		                if (extraKeys.length > 0) {
-		                    (0, parseUtil_1.addIssueToContext)(ctx, {
-		                        code: ZodError_1.ZodIssueCode.unrecognized_keys,
-		                        keys: extraKeys,
-		                    });
-		                    status.dirty();
-		                }
-		            }
-		            else if (unknownKeys === "strip") ;
-		            else {
-		                throw new Error(`Internal ZodObject error: invalid unknownKeys value.`);
-		            }
-		        }
-		        else {
-		            // run catchall validation
-		            const catchall = this._def.catchall;
-		            for (const key of extraKeys) {
-		                const value = ctx.data[key];
-		                pairs.push({
-		                    key: { status: "valid", value: key },
-		                    value: catchall._parse(new ParseInputLazyPath(ctx, value, ctx.path, key) //, ctx.child(key), value, getParsedType(value)
-		                    ),
-		                    alwaysSet: key in ctx.data,
-		                });
-		            }
-		        }
-		        if (ctx.common.async) {
-		            return Promise.resolve()
-		                .then(async () => {
-		                const syncPairs = [];
-		                for (const pair of pairs) {
-		                    const key = await pair.key;
-		                    syncPairs.push({
-		                        key,
-		                        value: await pair.value,
-		                        alwaysSet: pair.alwaysSet,
-		                    });
-		                }
-		                return syncPairs;
-		            })
-		                .then((syncPairs) => {
-		                return parseUtil_1.ParseStatus.mergeObjectSync(status, syncPairs);
-		            });
-		        }
-		        else {
-		            return parseUtil_1.ParseStatus.mergeObjectSync(status, pairs);
-		        }
-		    }
-		    get shape() {
-		        return this._def.shape();
-		    }
-		    strict(message) {
-		        errorUtil_1.errorUtil.errToObj;
-		        return new ZodObject({
-		            ...this._def,
-		            unknownKeys: "strict",
-		            ...(message !== undefined
-		                ? {
-		                    errorMap: (issue, ctx) => {
-		                        var _a, _b, _c, _d;
-		                        const defaultError = (_c = (_b = (_a = this._def).errorMap) === null || _b === void 0 ? void 0 : _b.call(_a, issue, ctx).message) !== null && _c !== void 0 ? _c : ctx.defaultError;
-		                        if (issue.code === "unrecognized_keys")
-		                            return {
-		                                message: (_d = errorUtil_1.errorUtil.errToObj(message).message) !== null && _d !== void 0 ? _d : defaultError,
-		                            };
-		                        return {
-		                            message: defaultError,
-		                        };
-		                    },
-		                }
-		                : {}),
-		        });
-		    }
-		    strip() {
-		        return new ZodObject({
-		            ...this._def,
-		            unknownKeys: "strip",
-		        });
-		    }
-		    passthrough() {
-		        return new ZodObject({
-		            ...this._def,
-		            unknownKeys: "passthrough",
-		        });
-		    }
-		    // const AugmentFactory =
-		    //   <Def extends ZodObjectDef>(def: Def) =>
-		    //   <Augmentation extends ZodRawShape>(
-		    //     augmentation: Augmentation
-		    //   ): ZodObject<
-		    //     extendShape<ReturnType<Def["shape"]>, Augmentation>,
-		    //     Def["unknownKeys"],
-		    //     Def["catchall"]
-		    //   > => {
-		    //     return new ZodObject({
-		    //       ...def,
-		    //       shape: () => ({
-		    //         ...def.shape(),
-		    //         ...augmentation,
-		    //       }),
-		    //     }) as any;
-		    //   };
-		    extend(augmentation) {
-		        return new ZodObject({
-		            ...this._def,
-		            shape: () => ({
-		                ...this._def.shape(),
-		                ...augmentation,
-		            }),
-		        });
-		    }
-		    /**
-		     * Prior to zod@1.0.12 there was a bug in the
-		     * inferred type of merged objects. Please
-		     * upgrade if you are experiencing issues.
-		     */
-		    merge(merging) {
-		        const merged = new ZodObject({
-		            unknownKeys: merging._def.unknownKeys,
-		            catchall: merging._def.catchall,
-		            shape: () => ({
-		                ...this._def.shape(),
-		                ...merging._def.shape(),
-		            }),
-		            typeName: ZodFirstPartyTypeKind.ZodObject,
-		        });
-		        return merged;
-		    }
-		    // merge<
-		    //   Incoming extends AnyZodObject,
-		    //   Augmentation extends Incoming["shape"],
-		    //   NewOutput extends {
-		    //     [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation
-		    //       ? Augmentation[k]["_output"]
-		    //       : k extends keyof Output
-		    //       ? Output[k]
-		    //       : never;
-		    //   },
-		    //   NewInput extends {
-		    //     [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation
-		    //       ? Augmentation[k]["_input"]
-		    //       : k extends keyof Input
-		    //       ? Input[k]
-		    //       : never;
-		    //   }
-		    // >(
-		    //   merging: Incoming
-		    // ): ZodObject<
-		    //   extendShape<T, ReturnType<Incoming["_def"]["shape"]>>,
-		    //   Incoming["_def"]["unknownKeys"],
-		    //   Incoming["_def"]["catchall"],
-		    //   NewOutput,
-		    //   NewInput
-		    // > {
-		    //   const merged: any = new ZodObject({
-		    //     unknownKeys: merging._def.unknownKeys,
-		    //     catchall: merging._def.catchall,
-		    //     shape: () =>
-		    //       objectUtil.mergeShapes(this._def.shape(), merging._def.shape()),
-		    //     typeName: ZodFirstPartyTypeKind.ZodObject,
-		    //   }) as any;
-		    //   return merged;
-		    // }
-		    setKey(key, schema) {
-		        return this.augment({ [key]: schema });
-		    }
-		    // merge<Incoming extends AnyZodObject>(
-		    //   merging: Incoming
-		    // ): //ZodObject<T & Incoming["_shape"], UnknownKeys, Catchall> = (merging) => {
-		    // ZodObject<
-		    //   extendShape<T, ReturnType<Incoming["_def"]["shape"]>>,
-		    //   Incoming["_def"]["unknownKeys"],
-		    //   Incoming["_def"]["catchall"]
-		    // > {
-		    //   // const mergedShape = objectUtil.mergeShapes(
-		    //   //   this._def.shape(),
-		    //   //   merging._def.shape()
-		    //   // );
-		    //   const merged: any = new ZodObject({
-		    //     unknownKeys: merging._def.unknownKeys,
-		    //     catchall: merging._def.catchall,
-		    //     shape: () =>
-		    //       objectUtil.mergeShapes(this._def.shape(), merging._def.shape()),
-		    //     typeName: ZodFirstPartyTypeKind.ZodObject,
-		    //   }) as any;
-		    //   return merged;
-		    // }
-		    catchall(index) {
-		        return new ZodObject({
-		            ...this._def,
-		            catchall: index,
-		        });
-		    }
-		    pick(mask) {
-		        const shape = {};
-		        util_1.util.objectKeys(mask).forEach((key) => {
-		            if (mask[key] && this.shape[key]) {
-		                shape[key] = this.shape[key];
-		            }
-		        });
-		        return new ZodObject({
-		            ...this._def,
-		            shape: () => shape,
-		        });
-		    }
-		    omit(mask) {
-		        const shape = {};
-		        util_1.util.objectKeys(this.shape).forEach((key) => {
-		            if (!mask[key]) {
-		                shape[key] = this.shape[key];
-		            }
-		        });
-		        return new ZodObject({
-		            ...this._def,
-		            shape: () => shape,
-		        });
-		    }
-		    /**
-		     * @deprecated
-		     */
-		    deepPartial() {
-		        return deepPartialify(this);
-		    }
-		    partial(mask) {
-		        const newShape = {};
-		        util_1.util.objectKeys(this.shape).forEach((key) => {
-		            const fieldSchema = this.shape[key];
-		            if (mask && !mask[key]) {
-		                newShape[key] = fieldSchema;
-		            }
-		            else {
-		                newShape[key] = fieldSchema.optional();
-		            }
-		        });
-		        return new ZodObject({
-		            ...this._def,
-		            shape: () => newShape,
-		        });
-		    }
-		    required(mask) {
-		        const newShape = {};
-		        util_1.util.objectKeys(this.shape).forEach((key) => {
-		            if (mask && !mask[key]) {
-		                newShape[key] = this.shape[key];
-		            }
-		            else {
-		                const fieldSchema = this.shape[key];
-		                let newField = fieldSchema;
-		                while (newField instanceof ZodOptional) {
-		                    newField = newField._def.innerType;
-		                }
-		                newShape[key] = newField;
-		            }
-		        });
-		        return new ZodObject({
-		            ...this._def,
-		            shape: () => newShape,
-		        });
-		    }
-		    keyof() {
-		        return createZodEnum(util_1.util.objectKeys(this.shape));
-		    }
-		}
-		exports.ZodObject = ZodObject;
-		ZodObject.create = (shape, params) => {
-		    return new ZodObject({
-		        shape: () => shape,
-		        unknownKeys: "strip",
-		        catchall: ZodNever.create(),
-		        typeName: ZodFirstPartyTypeKind.ZodObject,
-		        ...processCreateParams(params),
-		    });
-		};
-		ZodObject.strictCreate = (shape, params) => {
-		    return new ZodObject({
-		        shape: () => shape,
-		        unknownKeys: "strict",
-		        catchall: ZodNever.create(),
-		        typeName: ZodFirstPartyTypeKind.ZodObject,
-		        ...processCreateParams(params),
-		    });
-		};
-		ZodObject.lazycreate = (shape, params) => {
-		    return new ZodObject({
-		        shape,
-		        unknownKeys: "strip",
-		        catchall: ZodNever.create(),
-		        typeName: ZodFirstPartyTypeKind.ZodObject,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodUnion extends ZodType {
-		    _parse(input) {
-		        const { ctx } = this._processInputParams(input);
-		        const options = this._def.options;
-		        function handleResults(results) {
-		            // return first issue-free validation if it exists
-		            for (const result of results) {
-		                if (result.result.status === "valid") {
-		                    return result.result;
-		                }
-		            }
-		            for (const result of results) {
-		                if (result.result.status === "dirty") {
-		                    // add issues from dirty option
-		                    ctx.common.issues.push(...result.ctx.common.issues);
-		                    return result.result;
-		                }
-		            }
-		            // return invalid
-		            const unionErrors = results.map((result) => new ZodError_1.ZodError(result.ctx.common.issues));
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_union,
-		                unionErrors,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        if (ctx.common.async) {
-		            return Promise.all(options.map(async (option) => {
-		                const childCtx = {
-		                    ...ctx,
-		                    common: {
-		                        ...ctx.common,
-		                        issues: [],
-		                    },
-		                    parent: null,
-		                };
-		                return {
-		                    result: await option._parseAsync({
-		                        data: ctx.data,
-		                        path: ctx.path,
-		                        parent: childCtx,
-		                    }),
-		                    ctx: childCtx,
-		                };
-		            })).then(handleResults);
-		        }
-		        else {
-		            let dirty = undefined;
-		            const issues = [];
-		            for (const option of options) {
-		                const childCtx = {
-		                    ...ctx,
-		                    common: {
-		                        ...ctx.common,
-		                        issues: [],
-		                    },
-		                    parent: null,
-		                };
-		                const result = option._parseSync({
-		                    data: ctx.data,
-		                    path: ctx.path,
-		                    parent: childCtx,
-		                });
-		                if (result.status === "valid") {
-		                    return result;
-		                }
-		                else if (result.status === "dirty" && !dirty) {
-		                    dirty = { result, ctx: childCtx };
-		                }
-		                if (childCtx.common.issues.length) {
-		                    issues.push(childCtx.common.issues);
-		                }
-		            }
-		            if (dirty) {
-		                ctx.common.issues.push(...dirty.ctx.common.issues);
-		                return dirty.result;
-		            }
-		            const unionErrors = issues.map((issues) => new ZodError_1.ZodError(issues));
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_union,
-		                unionErrors,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		    }
-		    get options() {
-		        return this._def.options;
-		    }
-		}
-		exports.ZodUnion = ZodUnion;
-		ZodUnion.create = (types, params) => {
-		    return new ZodUnion({
-		        options: types,
-		        typeName: ZodFirstPartyTypeKind.ZodUnion,
-		        ...processCreateParams(params),
-		    });
-		};
-		/////////////////////////////////////////////////////
-		/////////////////////////////////////////////////////
-		//////////                                 //////////
-		//////////      ZodDiscriminatedUnion      //////////
-		//////////                                 //////////
-		/////////////////////////////////////////////////////
-		/////////////////////////////////////////////////////
-		const getDiscriminator = (type) => {
-		    if (type instanceof ZodLazy) {
-		        return getDiscriminator(type.schema);
-		    }
-		    else if (type instanceof ZodEffects) {
-		        return getDiscriminator(type.innerType());
-		    }
-		    else if (type instanceof ZodLiteral) {
-		        return [type.value];
-		    }
-		    else if (type instanceof ZodEnum) {
-		        return type.options;
-		    }
-		    else if (type instanceof ZodNativeEnum) {
-		        // eslint-disable-next-line ban/ban
-		        return Object.keys(type.enum);
-		    }
-		    else if (type instanceof ZodDefault) {
-		        return getDiscriminator(type._def.innerType);
-		    }
-		    else if (type instanceof ZodUndefined) {
-		        return [undefined];
-		    }
-		    else if (type instanceof ZodNull) {
-		        return [null];
-		    }
-		    else {
-		        return null;
-		    }
-		};
-		class ZodDiscriminatedUnion extends ZodType {
-		    _parse(input) {
-		        const { ctx } = this._processInputParams(input);
-		        if (ctx.parsedType !== util_1.ZodParsedType.object) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.object,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        const discriminator = this.discriminator;
-		        const discriminatorValue = ctx.data[discriminator];
-		        const option = this.optionsMap.get(discriminatorValue);
-		        if (!option) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_union_discriminator,
-		                options: Array.from(this.optionsMap.keys()),
-		                path: [discriminator],
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        if (ctx.common.async) {
-		            return option._parseAsync({
-		                data: ctx.data,
-		                path: ctx.path,
-		                parent: ctx,
-		            });
-		        }
-		        else {
-		            return option._parseSync({
-		                data: ctx.data,
-		                path: ctx.path,
-		                parent: ctx,
-		            });
-		        }
-		    }
-		    get discriminator() {
-		        return this._def.discriminator;
-		    }
-		    get options() {
-		        return this._def.options;
-		    }
-		    get optionsMap() {
-		        return this._def.optionsMap;
-		    }
-		    /**
-		     * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.
-		     * However, it only allows a union of objects, all of which need to share a discriminator property. This property must
-		     * have a different value for each object in the union.
-		     * @param discriminator the name of the discriminator property
-		     * @param types an array of object schemas
-		     * @param params
-		     */
-		    static create(discriminator, options, params) {
-		        // Get all the valid discriminator values
-		        const optionsMap = new Map();
-		        // try {
-		        for (const type of options) {
-		            const discriminatorValues = getDiscriminator(type.shape[discriminator]);
-		            if (!discriminatorValues) {
-		                throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
-		            }
-		            for (const value of discriminatorValues) {
-		                if (optionsMap.has(value)) {
-		                    throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);
-		                }
-		                optionsMap.set(value, type);
-		            }
-		        }
-		        return new ZodDiscriminatedUnion({
-		            typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,
-		            discriminator,
-		            options,
-		            optionsMap,
-		            ...processCreateParams(params),
-		        });
-		    }
-		}
-		exports.ZodDiscriminatedUnion = ZodDiscriminatedUnion;
-		function mergeValues(a, b) {
-		    const aType = (0, util_1.getParsedType)(a);
-		    const bType = (0, util_1.getParsedType)(b);
-		    if (a === b) {
-		        return { valid: true, data: a };
-		    }
-		    else if (aType === util_1.ZodParsedType.object && bType === util_1.ZodParsedType.object) {
-		        const bKeys = util_1.util.objectKeys(b);
-		        const sharedKeys = util_1.util
-		            .objectKeys(a)
-		            .filter((key) => bKeys.indexOf(key) !== -1);
-		        const newObj = { ...a, ...b };
-		        for (const key of sharedKeys) {
-		            const sharedValue = mergeValues(a[key], b[key]);
-		            if (!sharedValue.valid) {
-		                return { valid: false };
-		            }
-		            newObj[key] = sharedValue.data;
-		        }
-		        return { valid: true, data: newObj };
-		    }
-		    else if (aType === util_1.ZodParsedType.array && bType === util_1.ZodParsedType.array) {
-		        if (a.length !== b.length) {
-		            return { valid: false };
-		        }
-		        const newArray = [];
-		        for (let index = 0; index < a.length; index++) {
-		            const itemA = a[index];
-		            const itemB = b[index];
-		            const sharedValue = mergeValues(itemA, itemB);
-		            if (!sharedValue.valid) {
-		                return { valid: false };
-		            }
-		            newArray.push(sharedValue.data);
-		        }
-		        return { valid: true, data: newArray };
-		    }
-		    else if (aType === util_1.ZodParsedType.date &&
-		        bType === util_1.ZodParsedType.date &&
-		        +a === +b) {
-		        return { valid: true, data: a };
-		    }
-		    else {
-		        return { valid: false };
-		    }
-		}
-		class ZodIntersection extends ZodType {
-		    _parse(input) {
-		        const { status, ctx } = this._processInputParams(input);
-		        const handleParsed = (parsedLeft, parsedRight) => {
-		            if ((0, parseUtil_1.isAborted)(parsedLeft) || (0, parseUtil_1.isAborted)(parsedRight)) {
-		                return parseUtil_1.INVALID;
-		            }
-		            const merged = mergeValues(parsedLeft.value, parsedRight.value);
-		            if (!merged.valid) {
-		                (0, parseUtil_1.addIssueToContext)(ctx, {
-		                    code: ZodError_1.ZodIssueCode.invalid_intersection_types,
-		                });
-		                return parseUtil_1.INVALID;
-		            }
-		            if ((0, parseUtil_1.isDirty)(parsedLeft) || (0, parseUtil_1.isDirty)(parsedRight)) {
-		                status.dirty();
-		            }
-		            return { status: status.value, value: merged.data };
-		        };
-		        if (ctx.common.async) {
-		            return Promise.all([
-		                this._def.left._parseAsync({
-		                    data: ctx.data,
-		                    path: ctx.path,
-		                    parent: ctx,
-		                }),
-		                this._def.right._parseAsync({
-		                    data: ctx.data,
-		                    path: ctx.path,
-		                    parent: ctx,
-		                }),
-		            ]).then(([left, right]) => handleParsed(left, right));
-		        }
-		        else {
-		            return handleParsed(this._def.left._parseSync({
-		                data: ctx.data,
-		                path: ctx.path,
-		                parent: ctx,
-		            }), this._def.right._parseSync({
-		                data: ctx.data,
-		                path: ctx.path,
-		                parent: ctx,
-		            }));
-		        }
-		    }
-		}
-		exports.ZodIntersection = ZodIntersection;
-		ZodIntersection.create = (left, right, params) => {
-		    return new ZodIntersection({
-		        left: left,
-		        right: right,
-		        typeName: ZodFirstPartyTypeKind.ZodIntersection,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodTuple extends ZodType {
-		    _parse(input) {
-		        const { status, ctx } = this._processInputParams(input);
-		        if (ctx.parsedType !== util_1.ZodParsedType.array) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.array,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        if (ctx.data.length < this._def.items.length) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.too_small,
-		                minimum: this._def.items.length,
-		                inclusive: true,
-		                exact: false,
-		                type: "array",
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        const rest = this._def.rest;
-		        if (!rest && ctx.data.length > this._def.items.length) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.too_big,
-		                maximum: this._def.items.length,
-		                inclusive: true,
-		                exact: false,
-		                type: "array",
-		            });
-		            status.dirty();
-		        }
-		        const items = [...ctx.data]
-		            .map((item, itemIndex) => {
-		            const schema = this._def.items[itemIndex] || this._def.rest;
-		            if (!schema)
-		                return null;
-		            return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex));
-		        })
-		            .filter((x) => !!x); // filter nulls
-		        if (ctx.common.async) {
-		            return Promise.all(items).then((results) => {
-		                return parseUtil_1.ParseStatus.mergeArray(status, results);
-		            });
-		        }
-		        else {
-		            return parseUtil_1.ParseStatus.mergeArray(status, items);
-		        }
-		    }
-		    get items() {
-		        return this._def.items;
-		    }
-		    rest(rest) {
-		        return new ZodTuple({
-		            ...this._def,
-		            rest,
-		        });
-		    }
-		}
-		exports.ZodTuple = ZodTuple;
-		ZodTuple.create = (schemas, params) => {
-		    if (!Array.isArray(schemas)) {
-		        throw new Error("You must pass an array of schemas to z.tuple([ ... ])");
-		    }
-		    return new ZodTuple({
-		        items: schemas,
-		        typeName: ZodFirstPartyTypeKind.ZodTuple,
-		        rest: null,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodRecord extends ZodType {
-		    get keySchema() {
-		        return this._def.keyType;
-		    }
-		    get valueSchema() {
-		        return this._def.valueType;
-		    }
-		    _parse(input) {
-		        const { status, ctx } = this._processInputParams(input);
-		        if (ctx.parsedType !== util_1.ZodParsedType.object) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.object,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        const pairs = [];
-		        const keyType = this._def.keyType;
-		        const valueType = this._def.valueType;
-		        for (const key in ctx.data) {
-		            pairs.push({
-		                key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),
-		                value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),
-		            });
-		        }
-		        if (ctx.common.async) {
-		            return parseUtil_1.ParseStatus.mergeObjectAsync(status, pairs);
-		        }
-		        else {
-		            return parseUtil_1.ParseStatus.mergeObjectSync(status, pairs);
-		        }
-		    }
-		    get element() {
-		        return this._def.valueType;
-		    }
-		    static create(first, second, third) {
-		        if (second instanceof ZodType) {
-		            return new ZodRecord({
-		                keyType: first,
-		                valueType: second,
-		                typeName: ZodFirstPartyTypeKind.ZodRecord,
-		                ...processCreateParams(third),
-		            });
-		        }
-		        return new ZodRecord({
-		            keyType: ZodString.create(),
-		            valueType: first,
-		            typeName: ZodFirstPartyTypeKind.ZodRecord,
-		            ...processCreateParams(second),
-		        });
-		    }
-		}
-		exports.ZodRecord = ZodRecord;
-		class ZodMap extends ZodType {
-		    get keySchema() {
-		        return this._def.keyType;
-		    }
-		    get valueSchema() {
-		        return this._def.valueType;
-		    }
-		    _parse(input) {
-		        const { status, ctx } = this._processInputParams(input);
-		        if (ctx.parsedType !== util_1.ZodParsedType.map) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.map,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        const keyType = this._def.keyType;
-		        const valueType = this._def.valueType;
-		        const pairs = [...ctx.data.entries()].map(([key, value], index) => {
-		            return {
-		                key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])),
-		                value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"])),
-		            };
-		        });
-		        if (ctx.common.async) {
-		            const finalMap = new Map();
-		            return Promise.resolve().then(async () => {
-		                for (const pair of pairs) {
-		                    const key = await pair.key;
-		                    const value = await pair.value;
-		                    if (key.status === "aborted" || value.status === "aborted") {
-		                        return parseUtil_1.INVALID;
-		                    }
-		                    if (key.status === "dirty" || value.status === "dirty") {
-		                        status.dirty();
-		                    }
-		                    finalMap.set(key.value, value.value);
-		                }
-		                return { status: status.value, value: finalMap };
-		            });
-		        }
-		        else {
-		            const finalMap = new Map();
-		            for (const pair of pairs) {
-		                const key = pair.key;
-		                const value = pair.value;
-		                if (key.status === "aborted" || value.status === "aborted") {
-		                    return parseUtil_1.INVALID;
-		                }
-		                if (key.status === "dirty" || value.status === "dirty") {
-		                    status.dirty();
-		                }
-		                finalMap.set(key.value, value.value);
-		            }
-		            return { status: status.value, value: finalMap };
-		        }
-		    }
-		}
-		exports.ZodMap = ZodMap;
-		ZodMap.create = (keyType, valueType, params) => {
-		    return new ZodMap({
-		        valueType,
-		        keyType,
-		        typeName: ZodFirstPartyTypeKind.ZodMap,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodSet extends ZodType {
-		    _parse(input) {
-		        const { status, ctx } = this._processInputParams(input);
-		        if (ctx.parsedType !== util_1.ZodParsedType.set) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.set,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        const def = this._def;
-		        if (def.minSize !== null) {
-		            if (ctx.data.size < def.minSize.value) {
-		                (0, parseUtil_1.addIssueToContext)(ctx, {
-		                    code: ZodError_1.ZodIssueCode.too_small,
-		                    minimum: def.minSize.value,
-		                    type: "set",
-		                    inclusive: true,
-		                    exact: false,
-		                    message: def.minSize.message,
-		                });
-		                status.dirty();
-		            }
-		        }
-		        if (def.maxSize !== null) {
-		            if (ctx.data.size > def.maxSize.value) {
-		                (0, parseUtil_1.addIssueToContext)(ctx, {
-		                    code: ZodError_1.ZodIssueCode.too_big,
-		                    maximum: def.maxSize.value,
-		                    type: "set",
-		                    inclusive: true,
-		                    exact: false,
-		                    message: def.maxSize.message,
-		                });
-		                status.dirty();
-		            }
-		        }
-		        const valueType = this._def.valueType;
-		        function finalizeSet(elements) {
-		            const parsedSet = new Set();
-		            for (const element of elements) {
-		                if (element.status === "aborted")
-		                    return parseUtil_1.INVALID;
-		                if (element.status === "dirty")
-		                    status.dirty();
-		                parsedSet.add(element.value);
-		            }
-		            return { status: status.value, value: parsedSet };
-		        }
-		        const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i)));
-		        if (ctx.common.async) {
-		            return Promise.all(elements).then((elements) => finalizeSet(elements));
-		        }
-		        else {
-		            return finalizeSet(elements);
-		        }
-		    }
-		    min(minSize, message) {
-		        return new ZodSet({
-		            ...this._def,
-		            minSize: { value: minSize, message: errorUtil_1.errorUtil.toString(message) },
-		        });
-		    }
-		    max(maxSize, message) {
-		        return new ZodSet({
-		            ...this._def,
-		            maxSize: { value: maxSize, message: errorUtil_1.errorUtil.toString(message) },
-		        });
-		    }
-		    size(size, message) {
-		        return this.min(size, message).max(size, message);
-		    }
-		    nonempty(message) {
-		        return this.min(1, message);
-		    }
-		}
-		exports.ZodSet = ZodSet;
-		ZodSet.create = (valueType, params) => {
-		    return new ZodSet({
-		        valueType,
-		        minSize: null,
-		        maxSize: null,
-		        typeName: ZodFirstPartyTypeKind.ZodSet,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodFunction extends ZodType {
-		    constructor() {
-		        super(...arguments);
-		        this.validate = this.implement;
-		    }
-		    _parse(input) {
-		        const { ctx } = this._processInputParams(input);
-		        if (ctx.parsedType !== util_1.ZodParsedType.function) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.function,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        function makeArgsIssue(args, error) {
-		            return (0, parseUtil_1.makeIssue)({
-		                data: args,
-		                path: ctx.path,
-		                errorMaps: [
-		                    ctx.common.contextualErrorMap,
-		                    ctx.schemaErrorMap,
-		                    (0, errors_1.getErrorMap)(),
-		                    errors_1.defaultErrorMap,
-		                ].filter((x) => !!x),
-		                issueData: {
-		                    code: ZodError_1.ZodIssueCode.invalid_arguments,
-		                    argumentsError: error,
-		                },
-		            });
-		        }
-		        function makeReturnsIssue(returns, error) {
-		            return (0, parseUtil_1.makeIssue)({
-		                data: returns,
-		                path: ctx.path,
-		                errorMaps: [
-		                    ctx.common.contextualErrorMap,
-		                    ctx.schemaErrorMap,
-		                    (0, errors_1.getErrorMap)(),
-		                    errors_1.defaultErrorMap,
-		                ].filter((x) => !!x),
-		                issueData: {
-		                    code: ZodError_1.ZodIssueCode.invalid_return_type,
-		                    returnTypeError: error,
-		                },
-		            });
-		        }
-		        const params = { errorMap: ctx.common.contextualErrorMap };
-		        const fn = ctx.data;
-		        if (this._def.returns instanceof ZodPromise) {
-		            // Would love a way to avoid disabling this rule, but we need
-		            // an alias (using an arrow function was what caused 2651).
-		            // eslint-disable-next-line @typescript-eslint/no-this-alias
-		            const me = this;
-		            return (0, parseUtil_1.OK)(async function (...args) {
-		                const error = new ZodError_1.ZodError([]);
-		                const parsedArgs = await me._def.args
-		                    .parseAsync(args, params)
-		                    .catch((e) => {
-		                    error.addIssue(makeArgsIssue(args, e));
-		                    throw error;
-		                });
-		                const result = await Reflect.apply(fn, this, parsedArgs);
-		                const parsedReturns = await me._def.returns._def.type
-		                    .parseAsync(result, params)
-		                    .catch((e) => {
-		                    error.addIssue(makeReturnsIssue(result, e));
-		                    throw error;
-		                });
-		                return parsedReturns;
-		            });
-		        }
-		        else {
-		            // Would love a way to avoid disabling this rule, but we need
-		            // an alias (using an arrow function was what caused 2651).
-		            // eslint-disable-next-line @typescript-eslint/no-this-alias
-		            const me = this;
-		            return (0, parseUtil_1.OK)(function (...args) {
-		                const parsedArgs = me._def.args.safeParse(args, params);
-		                if (!parsedArgs.success) {
-		                    throw new ZodError_1.ZodError([makeArgsIssue(args, parsedArgs.error)]);
-		                }
-		                const result = Reflect.apply(fn, this, parsedArgs.data);
-		                const parsedReturns = me._def.returns.safeParse(result, params);
-		                if (!parsedReturns.success) {
-		                    throw new ZodError_1.ZodError([makeReturnsIssue(result, parsedReturns.error)]);
-		                }
-		                return parsedReturns.data;
-		            });
-		        }
-		    }
-		    parameters() {
-		        return this._def.args;
-		    }
-		    returnType() {
-		        return this._def.returns;
-		    }
-		    args(...items) {
-		        return new ZodFunction({
-		            ...this._def,
-		            args: ZodTuple.create(items).rest(ZodUnknown.create()),
-		        });
-		    }
-		    returns(returnType) {
-		        return new ZodFunction({
-		            ...this._def,
-		            returns: returnType,
-		        });
-		    }
-		    implement(func) {
-		        const validatedFunc = this.parse(func);
-		        return validatedFunc;
-		    }
-		    strictImplement(func) {
-		        const validatedFunc = this.parse(func);
-		        return validatedFunc;
-		    }
-		    static create(args, returns, params) {
-		        return new ZodFunction({
-		            args: (args
-		                ? args
-		                : ZodTuple.create([]).rest(ZodUnknown.create())),
-		            returns: returns || ZodUnknown.create(),
-		            typeName: ZodFirstPartyTypeKind.ZodFunction,
-		            ...processCreateParams(params),
-		        });
-		    }
-		}
-		exports.ZodFunction = ZodFunction;
-		class ZodLazy extends ZodType {
-		    get schema() {
-		        return this._def.getter();
-		    }
-		    _parse(input) {
-		        const { ctx } = this._processInputParams(input);
-		        const lazySchema = this._def.getter();
-		        return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx });
-		    }
-		}
-		exports.ZodLazy = ZodLazy;
-		ZodLazy.create = (getter, params) => {
-		    return new ZodLazy({
-		        getter: getter,
-		        typeName: ZodFirstPartyTypeKind.ZodLazy,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodLiteral extends ZodType {
-		    _parse(input) {
-		        if (input.data !== this._def.value) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                received: ctx.data,
-		                code: ZodError_1.ZodIssueCode.invalid_literal,
-		                expected: this._def.value,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        return { status: "valid", value: input.data };
-		    }
-		    get value() {
-		        return this._def.value;
-		    }
-		}
-		exports.ZodLiteral = ZodLiteral;
-		ZodLiteral.create = (value, params) => {
-		    return new ZodLiteral({
-		        value: value,
-		        typeName: ZodFirstPartyTypeKind.ZodLiteral,
-		        ...processCreateParams(params),
-		    });
-		};
-		function createZodEnum(values, params) {
-		    return new ZodEnum({
-		        values,
-		        typeName: ZodFirstPartyTypeKind.ZodEnum,
-		        ...processCreateParams(params),
-		    });
-		}
-		class ZodEnum extends ZodType {
-		    _parse(input) {
-		        if (typeof input.data !== "string") {
-		            const ctx = this._getOrReturnCtx(input);
-		            const expectedValues = this._def.values;
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                expected: util_1.util.joinValues(expectedValues),
-		                received: ctx.parsedType,
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        if (this._def.values.indexOf(input.data) === -1) {
-		            const ctx = this._getOrReturnCtx(input);
-		            const expectedValues = this._def.values;
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                received: ctx.data,
-		                code: ZodError_1.ZodIssueCode.invalid_enum_value,
-		                options: expectedValues,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        return (0, parseUtil_1.OK)(input.data);
-		    }
-		    get options() {
-		        return this._def.values;
-		    }
-		    get enum() {
-		        const enumValues = {};
-		        for (const val of this._def.values) {
-		            enumValues[val] = val;
-		        }
-		        return enumValues;
-		    }
-		    get Values() {
-		        const enumValues = {};
-		        for (const val of this._def.values) {
-		            enumValues[val] = val;
-		        }
-		        return enumValues;
-		    }
-		    get Enum() {
-		        const enumValues = {};
-		        for (const val of this._def.values) {
-		            enumValues[val] = val;
-		        }
-		        return enumValues;
-		    }
-		    extract(values) {
-		        return ZodEnum.create(values);
-		    }
-		    exclude(values) {
-		        return ZodEnum.create(this.options.filter((opt) => !values.includes(opt)));
-		    }
-		}
-		exports.ZodEnum = ZodEnum;
-		ZodEnum.create = createZodEnum;
-		class ZodNativeEnum extends ZodType {
-		    _parse(input) {
-		        const nativeEnumValues = util_1.util.getValidEnumValues(this._def.values);
-		        const ctx = this._getOrReturnCtx(input);
-		        if (ctx.parsedType !== util_1.ZodParsedType.string &&
-		            ctx.parsedType !== util_1.ZodParsedType.number) {
-		            const expectedValues = util_1.util.objectValues(nativeEnumValues);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                expected: util_1.util.joinValues(expectedValues),
-		                received: ctx.parsedType,
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        if (nativeEnumValues.indexOf(input.data) === -1) {
-		            const expectedValues = util_1.util.objectValues(nativeEnumValues);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                received: ctx.data,
-		                code: ZodError_1.ZodIssueCode.invalid_enum_value,
-		                options: expectedValues,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        return (0, parseUtil_1.OK)(input.data);
-		    }
-		    get enum() {
-		        return this._def.values;
-		    }
-		}
-		exports.ZodNativeEnum = ZodNativeEnum;
-		ZodNativeEnum.create = (values, params) => {
-		    return new ZodNativeEnum({
-		        values: values,
-		        typeName: ZodFirstPartyTypeKind.ZodNativeEnum,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodPromise extends ZodType {
-		    unwrap() {
-		        return this._def.type;
-		    }
-		    _parse(input) {
-		        const { ctx } = this._processInputParams(input);
-		        if (ctx.parsedType !== util_1.ZodParsedType.promise &&
-		            ctx.common.async === false) {
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.promise,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        const promisified = ctx.parsedType === util_1.ZodParsedType.promise
-		            ? ctx.data
-		            : Promise.resolve(ctx.data);
-		        return (0, parseUtil_1.OK)(promisified.then((data) => {
-		            return this._def.type.parseAsync(data, {
-		                path: ctx.path,
-		                errorMap: ctx.common.contextualErrorMap,
-		            });
-		        }));
-		    }
-		}
-		exports.ZodPromise = ZodPromise;
-		ZodPromise.create = (schema, params) => {
-		    return new ZodPromise({
-		        type: schema,
-		        typeName: ZodFirstPartyTypeKind.ZodPromise,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodEffects extends ZodType {
-		    innerType() {
-		        return this._def.schema;
-		    }
-		    sourceType() {
-		        return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects
-		            ? this._def.schema.sourceType()
-		            : this._def.schema;
-		    }
-		    _parse(input) {
-		        const { status, ctx } = this._processInputParams(input);
-		        const effect = this._def.effect || null;
-		        const checkCtx = {
-		            addIssue: (arg) => {
-		                (0, parseUtil_1.addIssueToContext)(ctx, arg);
-		                if (arg.fatal) {
-		                    status.abort();
-		                }
-		                else {
-		                    status.dirty();
-		                }
-		            },
-		            get path() {
-		                return ctx.path;
-		            },
-		        };
-		        checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx);
-		        if (effect.type === "preprocess") {
-		            const processed = effect.transform(ctx.data, checkCtx);
-		            if (ctx.common.issues.length) {
-		                return {
-		                    status: "dirty",
-		                    value: ctx.data,
-		                };
-		            }
-		            if (ctx.common.async) {
-		                return Promise.resolve(processed).then((processed) => {
-		                    return this._def.schema._parseAsync({
-		                        data: processed,
-		                        path: ctx.path,
-		                        parent: ctx,
-		                    });
-		                });
-		            }
-		            else {
-		                return this._def.schema._parseSync({
-		                    data: processed,
-		                    path: ctx.path,
-		                    parent: ctx,
-		                });
-		            }
-		        }
-		        if (effect.type === "refinement") {
-		            const executeRefinement = (acc
-		            // effect: RefinementEffect<any>
-		            ) => {
-		                const result = effect.refinement(acc, checkCtx);
-		                if (ctx.common.async) {
-		                    return Promise.resolve(result);
-		                }
-		                if (result instanceof Promise) {
-		                    throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");
-		                }
-		                return acc;
-		            };
-		            if (ctx.common.async === false) {
-		                const inner = this._def.schema._parseSync({
-		                    data: ctx.data,
-		                    path: ctx.path,
-		                    parent: ctx,
-		                });
-		                if (inner.status === "aborted")
-		                    return parseUtil_1.INVALID;
-		                if (inner.status === "dirty")
-		                    status.dirty();
-		                // return value is ignored
-		                executeRefinement(inner.value);
-		                return { status: status.value, value: inner.value };
-		            }
-		            else {
-		                return this._def.schema
-		                    ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx })
-		                    .then((inner) => {
-		                    if (inner.status === "aborted")
-		                        return parseUtil_1.INVALID;
-		                    if (inner.status === "dirty")
-		                        status.dirty();
-		                    return executeRefinement(inner.value).then(() => {
-		                        return { status: status.value, value: inner.value };
-		                    });
-		                });
-		            }
-		        }
-		        if (effect.type === "transform") {
-		            if (ctx.common.async === false) {
-		                const base = this._def.schema._parseSync({
-		                    data: ctx.data,
-		                    path: ctx.path,
-		                    parent: ctx,
-		                });
-		                if (!(0, parseUtil_1.isValid)(base))
-		                    return base;
-		                const result = effect.transform(base.value, checkCtx);
-		                if (result instanceof Promise) {
-		                    throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
-		                }
-		                return { status: status.value, value: result };
-		            }
-		            else {
-		                return this._def.schema
-		                    ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx })
-		                    .then((base) => {
-		                    if (!(0, parseUtil_1.isValid)(base))
-		                        return base;
-		                    return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ status: status.value, value: result }));
-		                });
-		            }
-		        }
-		        util_1.util.assertNever(effect);
-		    }
-		}
-		exports.ZodEffects = ZodEffects;
-		exports.ZodTransformer = ZodEffects;
-		ZodEffects.create = (schema, effect, params) => {
-		    return new ZodEffects({
-		        schema,
-		        typeName: ZodFirstPartyTypeKind.ZodEffects,
-		        effect,
-		        ...processCreateParams(params),
-		    });
-		};
-		ZodEffects.createWithPreprocess = (preprocess, schema, params) => {
-		    return new ZodEffects({
-		        schema,
-		        effect: { type: "preprocess", transform: preprocess },
-		        typeName: ZodFirstPartyTypeKind.ZodEffects,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodOptional extends ZodType {
-		    _parse(input) {
-		        const parsedType = this._getType(input);
-		        if (parsedType === util_1.ZodParsedType.undefined) {
-		            return (0, parseUtil_1.OK)(undefined);
-		        }
-		        return this._def.innerType._parse(input);
-		    }
-		    unwrap() {
-		        return this._def.innerType;
-		    }
-		}
-		exports.ZodOptional = ZodOptional;
-		ZodOptional.create = (type, params) => {
-		    return new ZodOptional({
-		        innerType: type,
-		        typeName: ZodFirstPartyTypeKind.ZodOptional,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodNullable extends ZodType {
-		    _parse(input) {
-		        const parsedType = this._getType(input);
-		        if (parsedType === util_1.ZodParsedType.null) {
-		            return (0, parseUtil_1.OK)(null);
-		        }
-		        return this._def.innerType._parse(input);
-		    }
-		    unwrap() {
-		        return this._def.innerType;
-		    }
-		}
-		exports.ZodNullable = ZodNullable;
-		ZodNullable.create = (type, params) => {
-		    return new ZodNullable({
-		        innerType: type,
-		        typeName: ZodFirstPartyTypeKind.ZodNullable,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodDefault extends ZodType {
-		    _parse(input) {
-		        const { ctx } = this._processInputParams(input);
-		        let data = ctx.data;
-		        if (ctx.parsedType === util_1.ZodParsedType.undefined) {
-		            data = this._def.defaultValue();
-		        }
-		        return this._def.innerType._parse({
-		            data,
-		            path: ctx.path,
-		            parent: ctx,
-		        });
-		    }
-		    removeDefault() {
-		        return this._def.innerType;
-		    }
-		}
-		exports.ZodDefault = ZodDefault;
-		ZodDefault.create = (type, params) => {
-		    return new ZodDefault({
-		        innerType: type,
-		        typeName: ZodFirstPartyTypeKind.ZodDefault,
-		        defaultValue: typeof params.default === "function"
-		            ? params.default
-		            : () => params.default,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodCatch extends ZodType {
-		    _parse(input) {
-		        const { ctx } = this._processInputParams(input);
-		        // newCtx is used to not collect issues from inner types in ctx
-		        const newCtx = {
-		            ...ctx,
-		            common: {
-		                ...ctx.common,
-		                issues: [],
-		            },
-		        };
-		        const result = this._def.innerType._parse({
-		            data: newCtx.data,
-		            path: newCtx.path,
-		            parent: {
-		                ...newCtx,
-		            },
-		        });
-		        if ((0, parseUtil_1.isAsync)(result)) {
-		            return result.then((result) => {
-		                return {
-		                    status: "valid",
-		                    value: result.status === "valid"
-		                        ? result.value
-		                        : this._def.catchValue({
-		                            get error() {
-		                                return new ZodError_1.ZodError(newCtx.common.issues);
-		                            },
-		                            input: newCtx.data,
-		                        }),
-		                };
-		            });
-		        }
-		        else {
-		            return {
-		                status: "valid",
-		                value: result.status === "valid"
-		                    ? result.value
-		                    : this._def.catchValue({
-		                        get error() {
-		                            return new ZodError_1.ZodError(newCtx.common.issues);
-		                        },
-		                        input: newCtx.data,
-		                    }),
-		            };
-		        }
-		    }
-		    removeCatch() {
-		        return this._def.innerType;
-		    }
-		}
-		exports.ZodCatch = ZodCatch;
-		ZodCatch.create = (type, params) => {
-		    return new ZodCatch({
-		        innerType: type,
-		        typeName: ZodFirstPartyTypeKind.ZodCatch,
-		        catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
-		        ...processCreateParams(params),
-		    });
-		};
-		class ZodNaN extends ZodType {
-		    _parse(input) {
-		        const parsedType = this._getType(input);
-		        if (parsedType !== util_1.ZodParsedType.nan) {
-		            const ctx = this._getOrReturnCtx(input);
-		            (0, parseUtil_1.addIssueToContext)(ctx, {
-		                code: ZodError_1.ZodIssueCode.invalid_type,
-		                expected: util_1.ZodParsedType.nan,
-		                received: ctx.parsedType,
-		            });
-		            return parseUtil_1.INVALID;
-		        }
-		        return { status: "valid", value: input.data };
-		    }
-		}
-		exports.ZodNaN = ZodNaN;
-		ZodNaN.create = (params) => {
-		    return new ZodNaN({
-		        typeName: ZodFirstPartyTypeKind.ZodNaN,
-		        ...processCreateParams(params),
-		    });
-		};
-		exports.BRAND = Symbol("zod_brand");
-		class ZodBranded extends ZodType {
-		    _parse(input) {
-		        const { ctx } = this._processInputParams(input);
-		        const data = ctx.data;
-		        return this._def.type._parse({
-		            data,
-		            path: ctx.path,
-		            parent: ctx,
-		        });
-		    }
-		    unwrap() {
-		        return this._def.type;
-		    }
-		}
-		exports.ZodBranded = ZodBranded;
-		class ZodPipeline extends ZodType {
-		    _parse(input) {
-		        const { status, ctx } = this._processInputParams(input);
-		        if (ctx.common.async) {
-		            const handleAsync = async () => {
-		                const inResult = await this._def.in._parseAsync({
-		                    data: ctx.data,
-		                    path: ctx.path,
-		                    parent: ctx,
-		                });
-		                if (inResult.status === "aborted")
-		                    return parseUtil_1.INVALID;
-		                if (inResult.status === "dirty") {
-		                    status.dirty();
-		                    return (0, parseUtil_1.DIRTY)(inResult.value);
-		                }
-		                else {
-		                    return this._def.out._parseAsync({
-		                        data: inResult.value,
-		                        path: ctx.path,
-		                        parent: ctx,
-		                    });
-		                }
-		            };
-		            return handleAsync();
-		        }
-		        else {
-		            const inResult = this._def.in._parseSync({
-		                data: ctx.data,
-		                path: ctx.path,
-		                parent: ctx,
-		            });
-		            if (inResult.status === "aborted")
-		                return parseUtil_1.INVALID;
-		            if (inResult.status === "dirty") {
-		                status.dirty();
-		                return {
-		                    status: "dirty",
-		                    value: inResult.value,
-		                };
-		            }
-		            else {
-		                return this._def.out._parseSync({
-		                    data: inResult.value,
-		                    path: ctx.path,
-		                    parent: ctx,
-		                });
-		            }
-		        }
-		    }
-		    static create(a, b) {
-		        return new ZodPipeline({
-		            in: a,
-		            out: b,
-		            typeName: ZodFirstPartyTypeKind.ZodPipeline,
-		        });
-		    }
-		}
-		exports.ZodPipeline = ZodPipeline;
-		class ZodReadonly extends ZodType {
-		    _parse(input) {
-		        const result = this._def.innerType._parse(input);
-		        if ((0, parseUtil_1.isValid)(result)) {
-		            result.value = Object.freeze(result.value);
-		        }
-		        return result;
-		    }
-		}
-		exports.ZodReadonly = ZodReadonly;
-		ZodReadonly.create = (type, params) => {
-		    return new ZodReadonly({
-		        innerType: type,
-		        typeName: ZodFirstPartyTypeKind.ZodReadonly,
-		        ...processCreateParams(params),
-		    });
-		};
-		const custom = (check, params = {}, 
-		/**
-		 * @deprecated
-		 *
-		 * Pass `fatal` into the params object instead:
-		 *
-		 * ```ts
-		 * z.string().custom((val) => val.length > 5, { fatal: false })
-		 * ```
-		 *
-		 */
-		fatal) => {
-		    if (check)
-		        return ZodAny.create().superRefine((data, ctx) => {
-		            var _a, _b;
-		            if (!check(data)) {
-		                const p = typeof params === "function"
-		                    ? params(data)
-		                    : typeof params === "string"
-		                        ? { message: params }
-		                        : params;
-		                const _fatal = (_b = (_a = p.fatal) !== null && _a !== void 0 ? _a : fatal) !== null && _b !== void 0 ? _b : true;
-		                const p2 = typeof p === "string" ? { message: p } : p;
-		                ctx.addIssue({ code: "custom", ...p2, fatal: _fatal });
-		            }
-		        });
-		    return ZodAny.create();
-		};
-		exports.custom = custom;
-		exports.late = {
-		    object: ZodObject.lazycreate,
-		};
-		var ZodFirstPartyTypeKind;
-		(function (ZodFirstPartyTypeKind) {
-		    ZodFirstPartyTypeKind["ZodString"] = "ZodString";
-		    ZodFirstPartyTypeKind["ZodNumber"] = "ZodNumber";
-		    ZodFirstPartyTypeKind["ZodNaN"] = "ZodNaN";
-		    ZodFirstPartyTypeKind["ZodBigInt"] = "ZodBigInt";
-		    ZodFirstPartyTypeKind["ZodBoolean"] = "ZodBoolean";
-		    ZodFirstPartyTypeKind["ZodDate"] = "ZodDate";
-		    ZodFirstPartyTypeKind["ZodSymbol"] = "ZodSymbol";
-		    ZodFirstPartyTypeKind["ZodUndefined"] = "ZodUndefined";
-		    ZodFirstPartyTypeKind["ZodNull"] = "ZodNull";
-		    ZodFirstPartyTypeKind["ZodAny"] = "ZodAny";
-		    ZodFirstPartyTypeKind["ZodUnknown"] = "ZodUnknown";
-		    ZodFirstPartyTypeKind["ZodNever"] = "ZodNever";
-		    ZodFirstPartyTypeKind["ZodVoid"] = "ZodVoid";
-		    ZodFirstPartyTypeKind["ZodArray"] = "ZodArray";
-		    ZodFirstPartyTypeKind["ZodObject"] = "ZodObject";
-		    ZodFirstPartyTypeKind["ZodUnion"] = "ZodUnion";
-		    ZodFirstPartyTypeKind["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
-		    ZodFirstPartyTypeKind["ZodIntersection"] = "ZodIntersection";
-		    ZodFirstPartyTypeKind["ZodTuple"] = "ZodTuple";
-		    ZodFirstPartyTypeKind["ZodRecord"] = "ZodRecord";
-		    ZodFirstPartyTypeKind["ZodMap"] = "ZodMap";
-		    ZodFirstPartyTypeKind["ZodSet"] = "ZodSet";
-		    ZodFirstPartyTypeKind["ZodFunction"] = "ZodFunction";
-		    ZodFirstPartyTypeKind["ZodLazy"] = "ZodLazy";
-		    ZodFirstPartyTypeKind["ZodLiteral"] = "ZodLiteral";
-		    ZodFirstPartyTypeKind["ZodEnum"] = "ZodEnum";
-		    ZodFirstPartyTypeKind["ZodEffects"] = "ZodEffects";
-		    ZodFirstPartyTypeKind["ZodNativeEnum"] = "ZodNativeEnum";
-		    ZodFirstPartyTypeKind["ZodOptional"] = "ZodOptional";
-		    ZodFirstPartyTypeKind["ZodNullable"] = "ZodNullable";
-		    ZodFirstPartyTypeKind["ZodDefault"] = "ZodDefault";
-		    ZodFirstPartyTypeKind["ZodCatch"] = "ZodCatch";
-		    ZodFirstPartyTypeKind["ZodPromise"] = "ZodPromise";
-		    ZodFirstPartyTypeKind["ZodBranded"] = "ZodBranded";
-		    ZodFirstPartyTypeKind["ZodPipeline"] = "ZodPipeline";
-		    ZodFirstPartyTypeKind["ZodReadonly"] = "ZodReadonly";
-		})(ZodFirstPartyTypeKind = exports.ZodFirstPartyTypeKind || (exports.ZodFirstPartyTypeKind = {}));
-		const instanceOfType = (
-		// const instanceOfType = <T extends new (...args: any[]) => any>(
-		cls, params = {
-		    message: `Input not instance of ${cls.name}`,
-		}) => (0, exports.custom)((data) => data instanceof cls, params);
-		exports.instanceof = instanceOfType;
-		const stringType = ZodString.create;
-		exports.string = stringType;
-		const numberType = ZodNumber.create;
-		exports.number = numberType;
-		const nanType = ZodNaN.create;
-		exports.nan = nanType;
-		const bigIntType = ZodBigInt.create;
-		exports.bigint = bigIntType;
-		const booleanType = ZodBoolean.create;
-		exports.boolean = booleanType;
-		const dateType = ZodDate.create;
-		exports.date = dateType;
-		const symbolType = ZodSymbol.create;
-		exports.symbol = symbolType;
-		const undefinedType = ZodUndefined.create;
-		exports.undefined = undefinedType;
-		const nullType = ZodNull.create;
-		exports.null = nullType;
-		const anyType = ZodAny.create;
-		exports.any = anyType;
-		const unknownType = ZodUnknown.create;
-		exports.unknown = unknownType;
-		const neverType = ZodNever.create;
-		exports.never = neverType;
-		const voidType = ZodVoid.create;
-		exports.void = voidType;
-		const arrayType = ZodArray.create;
-		exports.array = arrayType;
-		const objectType = ZodObject.create;
-		exports.object = objectType;
-		const strictObjectType = ZodObject.strictCreate;
-		exports.strictObject = strictObjectType;
-		const unionType = ZodUnion.create;
-		exports.union = unionType;
-		const discriminatedUnionType = ZodDiscriminatedUnion.create;
-		exports.discriminatedUnion = discriminatedUnionType;
-		const intersectionType = ZodIntersection.create;
-		exports.intersection = intersectionType;
-		const tupleType = ZodTuple.create;
-		exports.tuple = tupleType;
-		const recordType = ZodRecord.create;
-		exports.record = recordType;
-		const mapType = ZodMap.create;
-		exports.map = mapType;
-		const setType = ZodSet.create;
-		exports.set = setType;
-		const functionType = ZodFunction.create;
-		exports.function = functionType;
-		const lazyType = ZodLazy.create;
-		exports.lazy = lazyType;
-		const literalType = ZodLiteral.create;
-		exports.literal = literalType;
-		const enumType = ZodEnum.create;
-		exports.enum = enumType;
-		const nativeEnumType = ZodNativeEnum.create;
-		exports.nativeEnum = nativeEnumType;
-		const promiseType = ZodPromise.create;
-		exports.promise = promiseType;
-		const effectsType = ZodEffects.create;
-		exports.effect = effectsType;
-		exports.transformer = effectsType;
-		const optionalType = ZodOptional.create;
-		exports.optional = optionalType;
-		const nullableType = ZodNullable.create;
-		exports.nullable = nullableType;
-		const preprocessType = ZodEffects.createWithPreprocess;
-		exports.preprocess = preprocessType;
-		const pipelineType = ZodPipeline.create;
-		exports.pipeline = pipelineType;
-		const ostring = () => stringType().optional();
-		exports.ostring = ostring;
-		const onumber = () => numberType().optional();
-		exports.onumber = onumber;
-		const oboolean = () => booleanType().optional();
-		exports.oboolean = oboolean;
-		exports.coerce = {
-		    string: ((arg) => ZodString.create({ ...arg, coerce: true })),
-		    number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),
-		    boolean: ((arg) => ZodBoolean.create({
-		        ...arg,
-		        coerce: true,
-		    })),
-		    bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),
-		    date: ((arg) => ZodDate.create({ ...arg, coerce: true })),
-		};
-		exports.NEVER = parseUtil_1.INVALID; 
-	} (types));
-
-	(function (exports) {
-		var __createBinding = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
-		    if (k2 === undefined) k2 = k;
-		    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
-		}) : (function(o, m, k, k2) {
-		    if (k2 === undefined) k2 = k;
-		    o[k2] = m[k];
-		}));
-		var __exportStar = (commonjsGlobal && commonjsGlobal.__exportStar) || function(m, exports) {
-		    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
-		};
-		Object.defineProperty(exports, "__esModule", { value: true });
-		__exportStar(errors, exports);
-		__exportStar(parseUtil, exports);
-		__exportStar(typeAliases, exports);
-		__exportStar(util, exports);
-		__exportStar(types, exports);
-		__exportStar(ZodError$1, exports); 
-	} (external));
-
-	(function (exports) {
-		var __createBinding = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
-		    if (k2 === undefined) k2 = k;
-		    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
-		}) : (function(o, m, k, k2) {
-		    if (k2 === undefined) k2 = k;
-		    o[k2] = m[k];
-		}));
-		var __setModuleDefault = (commonjsGlobal && commonjsGlobal.__setModuleDefault) || (Object.create ? (function(o, v) {
-		    Object.defineProperty(o, "default", { enumerable: true, value: v });
-		}) : function(o, v) {
-		    o["default"] = v;
-		});
-		var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
-		    if (mod && mod.__esModule) return mod;
-		    var result = {};
-		    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
-		    __setModuleDefault(result, mod);
-		    return result;
-		};
-		var __exportStar = (commonjsGlobal && commonjsGlobal.__exportStar) || function(m, exports) {
-		    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
-		};
-		Object.defineProperty(exports, "__esModule", { value: true });
-		exports.z = void 0;
-		const z = __importStar(external);
-		exports.z = z;
-		__exportStar(external, exports);
-		exports.default = z; 
-	} (lib));
-
-	var webdriverBidiPermissions = {};
-
-	/**
-	 * Copyright 2024 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
-	    return (mod && mod.__esModule) ? mod : { "default": mod };
-	};
-	Object.defineProperty(webdriverBidiPermissions, "__esModule", { value: true });
-	webdriverBidiPermissions.Permissions = webdriverBidiPermissions.PermissionsCommandSchema = void 0;
-	/**
-	 * THIS FILE IS AUTOGENERATED by cddlconv 0.1.5.
-	 * Run `node tools/generate-bidi-types.mjs` to regenerate.
-	 * @see https://github.com/w3c/webdriver-bidi/blob/master/index.bs
-	 */
-	// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-	// @ts-nocheck Some types may be circular.
-	const zod_1$1 = __importDefault(lib);
-	webdriverBidiPermissions.PermissionsCommandSchema = zod_1$1.default.lazy(() => Permissions$1.SetPermissionSchema);
-	var Permissions$1;
-	(function (Permissions) {
-	    Permissions.PermissionDescriptorSchema = zod_1$1.default.lazy(() => zod_1$1.default.object({
-	        name: zod_1$1.default.string(),
-	    }));
-	})(Permissions$1 || (webdriverBidiPermissions.Permissions = Permissions$1 = {}));
-	(function (Permissions) {
-	    Permissions.PermissionStateSchema = zod_1$1.default.lazy(() => zod_1$1.default.enum(['granted', 'denied', 'prompt']));
-	})(Permissions$1 || (webdriverBidiPermissions.Permissions = Permissions$1 = {}));
-	(function (Permissions) {
-	    Permissions.SetPermissionSchema = zod_1$1.default.lazy(() => zod_1$1.default.object({
-	        method: zod_1$1.default.literal('permissions.setPermission'),
-	        params: Permissions.SetPermissionParametersSchema,
-	    }));
-	})(Permissions$1 || (webdriverBidiPermissions.Permissions = Permissions$1 = {}));
-	(function (Permissions) {
-	    Permissions.SetPermissionParametersSchema = zod_1$1.default.lazy(() => zod_1$1.default.object({
-	        descriptor: Permissions.PermissionDescriptorSchema,
-	        state: Permissions.PermissionStateSchema,
-	        origin: zod_1$1.default.string(),
-	    }));
-	})(Permissions$1 || (webdriverBidiPermissions.Permissions = Permissions$1 = {}));
-
-	var webdriverBidi = {};
-
-	(function (exports) {
-		/**
-		 * Copyright 2024 Google LLC.
-		 * Copyright (c) Microsoft Corporation.
-		 *
-		 * 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
-		 *
-		 *     http://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.
-		 */
-		var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
-		    return (mod && mod.__esModule) ? mod : { "default": mod };
-		};
-		Object.defineProperty(exports, "__esModule", { value: true });
-		exports.Input = exports.InputCommandSchema = exports.Log = exports.LogEventSchema = exports.Storage = exports.StorageResultSchema = exports.StorageCommandSchema = exports.Script = exports.ScriptResultSchema = exports.ScriptCommandSchema = exports.ScriptEventSchema = exports.Network = exports.NetworkResultSchema = exports.NetworkEventSchema = exports.NetworkCommandSchema = exports.BrowsingContext = exports.BrowsingContextResultSchema = exports.BrowsingContextEventSchema = exports.BrowsingContextCommandSchema = exports.Browser = exports.BrowserResultSchema = exports.BrowserCommandSchema = exports.SessionResultSchema = exports.Session = exports.SessionCommandSchema = exports.ErrorCodeSchema = exports.JsUintSchema = exports.JsIntSchema = exports.ExtensibleSchema = exports.EmptyResultSchema = exports.ErrorResponseSchema = exports.MessageSchema = exports.EmptyParamsSchema = exports.ResultDataSchema = exports.CommandDataSchema = exports.EventDataSchema = exports.CommandResponseSchema = exports.CommandSchema = exports.EventSchema = void 0;
-		/**
-		 * THIS FILE IS AUTOGENERATED by cddlconv 0.1.5.
-		 * Run `node tools/generate-bidi-types.mjs` to regenerate.
-		 * @see https://github.com/w3c/webdriver-bidi/blob/master/index.bs
-		 */
-		// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-		// @ts-nocheck Some types may be circular.
-		const zod_1 = __importDefault(lib);
-		exports.EventSchema = zod_1.default.lazy(() => zod_1.default
-		    .object({
-		    type: zod_1.default.literal('event'),
-		})
-		    .and(exports.EventDataSchema)
-		    .and(exports.ExtensibleSchema));
-		exports.CommandSchema = zod_1.default.lazy(() => zod_1.default
-		    .object({
-		    id: exports.JsUintSchema,
-		})
-		    .and(exports.CommandDataSchema)
-		    .and(exports.ExtensibleSchema));
-		exports.CommandResponseSchema = zod_1.default.lazy(() => zod_1.default
-		    .object({
-		    type: zod_1.default.literal('success'),
-		    id: exports.JsUintSchema,
-		    result: exports.ResultDataSchema,
-		})
-		    .and(exports.ExtensibleSchema));
-		exports.EventDataSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    exports.BrowsingContextEventSchema,
-		    exports.LogEventSchema,
-		    exports.NetworkEventSchema,
-		    exports.ScriptEventSchema,
-		]));
-		exports.CommandDataSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    exports.BrowserCommandSchema,
-		    exports.BrowsingContextCommandSchema,
-		    exports.InputCommandSchema,
-		    exports.NetworkCommandSchema,
-		    exports.ScriptCommandSchema,
-		    exports.SessionCommandSchema,
-		    exports.StorageCommandSchema,
-		]));
-		exports.ResultDataSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    exports.BrowsingContextResultSchema,
-		    exports.EmptyResultSchema,
-		    exports.NetworkResultSchema,
-		    exports.ScriptResultSchema,
-		    exports.SessionResultSchema,
-		    exports.StorageResultSchema,
-		]));
-		exports.EmptyParamsSchema = zod_1.default.lazy(() => exports.ExtensibleSchema);
-		exports.MessageSchema = zod_1.default.lazy(() => zod_1.default.union([exports.CommandResponseSchema, exports.ErrorResponseSchema, exports.EventSchema]));
-		exports.ErrorResponseSchema = zod_1.default.lazy(() => zod_1.default
-		    .object({
-		    type: zod_1.default.literal('error'),
-		    id: zod_1.default.union([exports.JsUintSchema, zod_1.default.null()]),
-		    error: exports.ErrorCodeSchema,
-		    message: zod_1.default.string(),
-		    stacktrace: zod_1.default.string().optional(),
-		})
-		    .and(exports.ExtensibleSchema));
-		exports.EmptyResultSchema = zod_1.default.lazy(() => exports.ExtensibleSchema);
-		exports.ExtensibleSchema = zod_1.default.lazy(() => zod_1.default.record(zod_1.default.string(), zod_1.default.any()));
-		exports.JsIntSchema = zod_1.default
-		    .number()
-		    .int()
-		    .gte(-9007199254740991)
-		    .lte(9007199254740991);
-		exports.JsUintSchema = zod_1.default
-		    .number()
-		    .int()
-		    .nonnegative()
-		    .gte(0)
-		    .lte(9007199254740991);
-		exports.ErrorCodeSchema = zod_1.default.lazy(() => zod_1.default.enum([
-		    'invalid argument',
-		    'invalid selector',
-		    'invalid session id',
-		    'move target out of bounds',
-		    'no such alert',
-		    'no such element',
-		    'no such frame',
-		    'no such handle',
-		    'no such history entry',
-		    'no such intercept',
-		    'no such node',
-		    'no such request',
-		    'no such script',
-		    'no such storage partition',
-		    'no such user context',
-		    'session not created',
-		    'unable to capture screen',
-		    'unable to close browser',
-		    'unable to set cookie',
-		    'unable to set file input',
-		    'underspecified storage partition',
-		    'unknown command',
-		    'unknown error',
-		    'unsupported operation',
-		]));
-		exports.SessionCommandSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Session.EndSchema,
-		    Session.NewSchema,
-		    Session.StatusSchema,
-		    Session.SubscribeSchema,
-		    Session.UnsubscribeSchema,
-		]));
-		var Session;
-		(function (Session) {
-		    Session.ProxyConfigurationSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Session.AutodetectProxyConfigurationSchema,
-		        Session.DirectProxyConfigurationSchema,
-		        Session.ManualProxyConfigurationSchema,
-		        Session.PacProxyConfigurationSchema,
-		        Session.SystemProxyConfigurationSchema,
-		        zod_1.default.object({}),
-		    ]));
-		})(Session || (exports.Session = Session = {}));
-		exports.SessionResultSchema = zod_1.default.lazy(() => zod_1.default.union([Session.NewResultSchema, Session.StatusResultSchema]));
-		(function (Session) {
-		    Session.CapabilitiesRequestSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        alwaysMatch: Session.CapabilityRequestSchema.optional(),
-		        firstMatch: zod_1.default.array(Session.CapabilityRequestSchema).optional(),
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.CapabilityRequestSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        acceptInsecureCerts: zod_1.default.boolean().optional(),
-		        browserName: zod_1.default.string().optional(),
-		        browserVersion: zod_1.default.string().optional(),
-		        platformName: zod_1.default.string().optional(),
-		        proxy: Session.ProxyConfigurationSchema.optional(),
-		        webSocketUrl: zod_1.default.boolean().optional(),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.AutodetectProxyConfigurationSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        proxyType: zod_1.default.literal('autodetect'),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.DirectProxyConfigurationSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        proxyType: zod_1.default.literal('direct'),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.ManualProxyConfigurationSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        proxyType: zod_1.default.literal('manual'),
-		        ftpProxy: zod_1.default.string().optional(),
-		        httpProxy: zod_1.default.string().optional(),
-		        sslProxy: zod_1.default.string().optional(),
-		    })
-		        .and(Session.SocksProxyConfigurationSchema.or(zod_1.default.object({})))
-		        .and(zod_1.default.object({
-		        noProxy: zod_1.default.array(zod_1.default.string()).optional(),
-		    }))
-		        .and(exports.ExtensibleSchema));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.SocksProxyConfigurationSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        socksProxy: zod_1.default.string(),
-		        socksVersion: zod_1.default.number().int().nonnegative().gte(0).lte(255),
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.PacProxyConfigurationSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        proxyType: zod_1.default.literal('pac'),
-		        proxyAutoconfigUrl: zod_1.default.string(),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.SystemProxyConfigurationSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        proxyType: zod_1.default.literal('system'),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.SubscriptionRequestSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        events: zod_1.default.array(zod_1.default.string()).min(1),
-		        contexts: zod_1.default
-		            .array(BrowsingContext.BrowsingContextSchema)
-		            .min(1)
-		            .optional(),
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.StatusSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('session.status'),
-		        params: exports.EmptyParamsSchema,
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.StatusResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        ready: zod_1.default.boolean(),
-		        message: zod_1.default.string(),
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.NewSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('session.new'),
-		        params: Session.NewParametersSchema,
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.NewParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        capabilities: Session.CapabilitiesRequestSchema,
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.NewResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        sessionId: zod_1.default.string(),
-		        capabilities: zod_1.default
-		            .object({
-		            acceptInsecureCerts: zod_1.default.boolean(),
-		            browserName: zod_1.default.string(),
-		            browserVersion: zod_1.default.string(),
-		            platformName: zod_1.default.string(),
-		            setWindowRect: zod_1.default.boolean(),
-		            userAgent: zod_1.default.string(),
-		            proxy: Session.ProxyConfigurationSchema.optional(),
-		            webSocketUrl: zod_1.default.string().optional(),
-		        })
-		            .and(exports.ExtensibleSchema),
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.EndSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('session.end'),
-		        params: exports.EmptyParamsSchema,
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.SubscribeSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('session.subscribe'),
-		        params: Session.SubscriptionRequestSchema,
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		(function (Session) {
-		    Session.UnsubscribeSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('session.unsubscribe'),
-		        params: Session.SubscriptionRequestSchema,
-		    }));
-		})(Session || (exports.Session = Session = {}));
-		exports.BrowserCommandSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Browser.CloseSchema,
-		    Browser.CreateUserContextSchema,
-		    Browser.GetUserContextsSchema,
-		    Browser.RemoveUserContextSchema,
-		]));
-		exports.BrowserResultSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Browser.CreateUserContextResultSchema,
-		    Browser.GetUserContextsResultSchema,
-		]));
-		var Browser;
-		(function (Browser) {
-		    Browser.UserContextSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(Browser || (exports.Browser = Browser = {}));
-		(function (Browser) {
-		    Browser.UserContextInfoSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        userContext: Browser.UserContextSchema,
-		    }));
-		})(Browser || (exports.Browser = Browser = {}));
-		(function (Browser) {
-		    Browser.CloseSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browser.close'),
-		        params: exports.EmptyParamsSchema,
-		    }));
-		})(Browser || (exports.Browser = Browser = {}));
-		(function (Browser) {
-		    Browser.CreateUserContextSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browser.createUserContext'),
-		        params: exports.EmptyParamsSchema,
-		    }));
-		})(Browser || (exports.Browser = Browser = {}));
-		(function (Browser) {
-		    Browser.CreateUserContextResultSchema = zod_1.default.lazy(() => Browser.UserContextInfoSchema);
-		})(Browser || (exports.Browser = Browser = {}));
-		(function (Browser) {
-		    Browser.GetUserContextsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browser.getUserContexts'),
-		        params: exports.EmptyParamsSchema,
-		    }));
-		})(Browser || (exports.Browser = Browser = {}));
-		(function (Browser) {
-		    Browser.GetUserContextsResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        userContexts: zod_1.default.array(Browser.UserContextInfoSchema).min(1),
-		    }));
-		})(Browser || (exports.Browser = Browser = {}));
-		(function (Browser) {
-		    Browser.RemoveUserContextSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browser.removeUserContext'),
-		        params: Browser.RemoveUserContextParametersSchema,
-		    }));
-		})(Browser || (exports.Browser = Browser = {}));
-		(function (Browser) {
-		    Browser.RemoveUserContextParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        userContext: Browser.UserContextSchema,
-		    }));
-		})(Browser || (exports.Browser = Browser = {}));
-		exports.BrowsingContextCommandSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    BrowsingContext.ActivateSchema,
-		    BrowsingContext.CaptureScreenshotSchema,
-		    BrowsingContext.CloseSchema,
-		    BrowsingContext.CreateSchema,
-		    BrowsingContext.GetTreeSchema,
-		    BrowsingContext.HandleUserPromptSchema,
-		    BrowsingContext.LocateNodesSchema,
-		    BrowsingContext.NavigateSchema,
-		    BrowsingContext.PrintSchema,
-		    BrowsingContext.ReloadSchema,
-		    BrowsingContext.SetViewportSchema,
-		    BrowsingContext.TraverseHistorySchema,
-		]));
-		exports.BrowsingContextEventSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    BrowsingContext.ContextCreatedSchema,
-		    BrowsingContext.ContextDestroyedSchema,
-		    BrowsingContext.DomContentLoadedSchema,
-		    BrowsingContext.DownloadWillBeginSchema,
-		    BrowsingContext.FragmentNavigatedSchema,
-		    BrowsingContext.LoadSchema,
-		    BrowsingContext.NavigationAbortedSchema,
-		    BrowsingContext.NavigationFailedSchema,
-		    BrowsingContext.NavigationStartedSchema,
-		    BrowsingContext.UserPromptClosedSchema,
-		    BrowsingContext.UserPromptOpenedSchema,
-		]));
-		exports.BrowsingContextResultSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    BrowsingContext.CaptureScreenshotResultSchema,
-		    BrowsingContext.CreateResultSchema,
-		    BrowsingContext.GetTreeResultSchema,
-		    BrowsingContext.LocateNodesResultSchema,
-		    BrowsingContext.NavigateResultSchema,
-		    BrowsingContext.PrintResultSchema,
-		    BrowsingContext.TraverseHistoryResultSchema,
-		]));
-		var BrowsingContext;
-		(function (BrowsingContext) {
-		    BrowsingContext.BrowsingContextSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.InfoListSchema = zod_1.default.lazy(() => zod_1.default.array(BrowsingContext.InfoSchema));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.InfoSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        children: zod_1.default.union([BrowsingContext.InfoListSchema, zod_1.default.null()]),
-		        context: BrowsingContext.BrowsingContextSchema,
-		        url: zod_1.default.string(),
-		        userContext: Browser.UserContextSchema,
-		        parent: zod_1.default
-		            .union([BrowsingContext.BrowsingContextSchema, zod_1.default.null()])
-		            .optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.LocatorSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        BrowsingContext.CssLocatorSchema,
-		        BrowsingContext.InnerTextLocatorSchema,
-		        BrowsingContext.XPathLocatorSchema,
-		    ]));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CssLocatorSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('css'),
-		        value: zod_1.default.string(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.InnerTextLocatorSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('innerText'),
-		        value: zod_1.default.string(),
-		        ignoreCase: zod_1.default.boolean().optional(),
-		        matchType: zod_1.default.enum(['full', 'partial']).optional(),
-		        maxDepth: exports.JsUintSchema.optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.XPathLocatorSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('xpath'),
-		        value: zod_1.default.string(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.NavigationSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.NavigationInfoSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        navigation: zod_1.default.union([BrowsingContext.NavigationSchema, zod_1.default.null()]),
-		        timestamp: exports.JsUintSchema,
-		        url: zod_1.default.string(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ReadinessStateSchema = zod_1.default.lazy(() => zod_1.default.enum(['none', 'interactive', 'complete']));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ActivateSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.activate'),
-		        params: BrowsingContext.ActivateParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ActivateParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CaptureScreenshotParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        origin: zod_1.default.enum(['viewport', 'document']).default('viewport').optional(),
-		        format: BrowsingContext.ImageFormatSchema.optional(),
-		        clip: BrowsingContext.ClipRectangleSchema.optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CaptureScreenshotSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.captureScreenshot'),
-		        params: BrowsingContext.CaptureScreenshotParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ImageFormatSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.string(),
-		        quality: zod_1.default.number().gte(0).lte(1).optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ClipRectangleSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        BrowsingContext.BoxClipRectangleSchema,
-		        BrowsingContext.ElementClipRectangleSchema,
-		    ]));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ElementClipRectangleSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('element'),
-		        element: Script.SharedReferenceSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.BoxClipRectangleSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('box'),
-		        x: zod_1.default.number(),
-		        y: zod_1.default.number(),
-		        width: zod_1.default.number(),
-		        height: zod_1.default.number(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CaptureScreenshotResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        data: zod_1.default.string(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CloseSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.close'),
-		        params: BrowsingContext.CloseParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CloseParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        promptUnload: zod_1.default.boolean().default(false).optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CreateSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.create'),
-		        params: BrowsingContext.CreateParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CreateTypeSchema = zod_1.default.lazy(() => zod_1.default.enum(['tab', 'window']));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CreateParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: BrowsingContext.CreateTypeSchema,
-		        referenceContext: BrowsingContext.BrowsingContextSchema.optional(),
-		        background: zod_1.default.boolean().default(false).optional(),
-		        userContext: Browser.UserContextSchema.optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.CreateResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.GetTreeSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.getTree'),
-		        params: BrowsingContext.GetTreeParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.GetTreeParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        maxDepth: exports.JsUintSchema.optional(),
-		        root: BrowsingContext.BrowsingContextSchema.optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.GetTreeResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        contexts: BrowsingContext.InfoListSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.HandleUserPromptSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.handleUserPrompt'),
-		        params: BrowsingContext.HandleUserPromptParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.HandleUserPromptParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        accept: zod_1.default.boolean().optional(),
-		        userText: zod_1.default.string().optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.LocateNodesParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        locator: BrowsingContext.LocatorSchema,
-		        maxNodeCount: exports.JsUintSchema.gte(1).optional(),
-		        serializationOptions: Script.SerializationOptionsSchema.optional(),
-		        startNodes: zod_1.default.array(Script.SharedReferenceSchema).min(1).optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.LocateNodesSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.locateNodes'),
-		        params: BrowsingContext.LocateNodesParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.LocateNodesResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        nodes: zod_1.default.array(Script.NodeRemoteValueSchema),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.NavigateSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.navigate'),
-		        params: BrowsingContext.NavigateParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.NavigateParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        url: zod_1.default.string(),
-		        wait: BrowsingContext.ReadinessStateSchema.optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.NavigateResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        navigation: zod_1.default.union([BrowsingContext.NavigationSchema, zod_1.default.null()]),
-		        url: zod_1.default.string(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.PrintSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.print'),
-		        params: BrowsingContext.PrintParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.PrintParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        background: zod_1.default.boolean().default(false).optional(),
-		        margin: BrowsingContext.PrintMarginParametersSchema.optional(),
-		        orientation: zod_1.default
-		            .enum(['portrait', 'landscape'])
-		            .default('portrait')
-		            .optional(),
-		        page: BrowsingContext.PrintPageParametersSchema.optional(),
-		        pageRanges: zod_1.default.array(zod_1.default.union([exports.JsUintSchema, zod_1.default.string()])).optional(),
-		        scale: zod_1.default.number().gte(0.1).lte(2).default(1).optional(),
-		        shrinkToFit: zod_1.default.boolean().default(true).optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.PrintMarginParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        bottom: zod_1.default.number().gte(0).default(1).optional(),
-		        left: zod_1.default.number().gte(0).default(1).optional(),
-		        right: zod_1.default.number().gte(0).default(1).optional(),
-		        top: zod_1.default.number().gte(0).default(1).optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.PrintPageParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        height: zod_1.default.number().gte(0.0352).default(27.94).optional(),
-		        width: zod_1.default.number().gte(0.0352).default(21.59).optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.PrintResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        data: zod_1.default.string(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ReloadSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.reload'),
-		        params: BrowsingContext.ReloadParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ReloadParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        ignoreCache: zod_1.default.boolean().optional(),
-		        wait: BrowsingContext.ReadinessStateSchema.optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.SetViewportSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.setViewport'),
-		        params: BrowsingContext.SetViewportParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.SetViewportParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        viewport: zod_1.default.union([BrowsingContext.ViewportSchema, zod_1.default.null()]).optional(),
-		        devicePixelRatio: zod_1.default.union([zod_1.default.number().gt(0), zod_1.default.null()]).optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ViewportSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        width: exports.JsUintSchema,
-		        height: exports.JsUintSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.TraverseHistorySchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.traverseHistory'),
-		        params: BrowsingContext.TraverseHistoryParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.TraverseHistoryParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        delta: exports.JsIntSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.TraverseHistoryResultSchema = zod_1.default.lazy(() => zod_1.default.object({}));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ContextCreatedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.contextCreated'),
-		        params: BrowsingContext.InfoSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.ContextDestroyedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.contextDestroyed'),
-		        params: BrowsingContext.InfoSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.NavigationStartedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.navigationStarted'),
-		        params: BrowsingContext.NavigationInfoSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.FragmentNavigatedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.fragmentNavigated'),
-		        params: BrowsingContext.NavigationInfoSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.DomContentLoadedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.domContentLoaded'),
-		        params: BrowsingContext.NavigationInfoSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.LoadSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.load'),
-		        params: BrowsingContext.NavigationInfoSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.DownloadWillBeginSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.downloadWillBegin'),
-		        params: BrowsingContext.NavigationInfoSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.NavigationAbortedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.navigationAborted'),
-		        params: BrowsingContext.NavigationInfoSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.NavigationFailedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.navigationFailed'),
-		        params: BrowsingContext.NavigationInfoSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.UserPromptClosedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.userPromptClosed'),
-		        params: BrowsingContext.UserPromptClosedParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.UserPromptClosedParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        accepted: zod_1.default.boolean(),
-		        userText: zod_1.default.string().optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.UserPromptOpenedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('browsingContext.userPromptOpened'),
-		        params: BrowsingContext.UserPromptOpenedParametersSchema,
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		(function (BrowsingContext) {
-		    BrowsingContext.UserPromptOpenedParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        type: zod_1.default.enum(['alert', 'confirm', 'prompt', 'beforeunload']),
-		        message: zod_1.default.string(),
-		        defaultValue: zod_1.default.string().optional(),
-		    }));
-		})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
-		exports.NetworkCommandSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Network.AddInterceptSchema,
-		    Network.ContinueRequestSchema,
-		    Network.ContinueResponseSchema,
-		    Network.ContinueWithAuthSchema,
-		    Network.FailRequestSchema,
-		    Network.ProvideResponseSchema,
-		    Network.RemoveInterceptSchema,
-		]));
-		exports.NetworkEventSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Network.AuthRequiredSchema,
-		    Network.BeforeRequestSentSchema,
-		    Network.FetchErrorSchema,
-		    Network.ResponseCompletedSchema,
-		    Network.ResponseStartedSchema,
-		]));
-		exports.NetworkResultSchema = zod_1.default.lazy(() => Network.AddInterceptResultSchema);
-		var Network;
-		(function (Network) {
-		    Network.AuthChallengeSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        scheme: zod_1.default.string(),
-		        realm: zod_1.default.string(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.AuthCredentialsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('password'),
-		        username: zod_1.default.string(),
-		        password: zod_1.default.string(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.BaseParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: zod_1.default.union([BrowsingContext.BrowsingContextSchema, zod_1.default.null()]),
-		        isBlocked: zod_1.default.boolean(),
-		        navigation: zod_1.default.union([BrowsingContext.NavigationSchema, zod_1.default.null()]),
-		        redirectCount: exports.JsUintSchema,
-		        request: Network.RequestDataSchema,
-		        timestamp: exports.JsUintSchema,
-		        intercepts: zod_1.default.array(Network.InterceptSchema).min(1).optional(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.BytesValueSchema = zod_1.default.lazy(() => zod_1.default.union([Network.StringValueSchema, Network.Base64ValueSchema]));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.StringValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('string'),
-		        value: zod_1.default.string(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.Base64ValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('base64'),
-		        value: zod_1.default.string(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.SameSiteSchema = zod_1.default.lazy(() => zod_1.default.enum(['strict', 'lax', 'none']));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.CookieSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        name: zod_1.default.string(),
-		        value: Network.BytesValueSchema,
-		        domain: zod_1.default.string(),
-		        path: zod_1.default.string(),
-		        size: exports.JsUintSchema,
-		        httpOnly: zod_1.default.boolean(),
-		        secure: zod_1.default.boolean(),
-		        sameSite: Network.SameSiteSchema,
-		        expiry: exports.JsUintSchema.optional(),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.CookieHeaderSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        name: zod_1.default.string(),
-		        value: Network.BytesValueSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.FetchTimingInfoSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        timeOrigin: zod_1.default.number(),
-		        requestTime: zod_1.default.number(),
-		        redirectStart: zod_1.default.number(),
-		        redirectEnd: zod_1.default.number(),
-		        fetchStart: zod_1.default.number(),
-		        dnsStart: zod_1.default.number(),
-		        dnsEnd: zod_1.default.number(),
-		        connectStart: zod_1.default.number(),
-		        connectEnd: zod_1.default.number(),
-		        tlsStart: zod_1.default.number(),
-		        requestStart: zod_1.default.number(),
-		        responseStart: zod_1.default.number(),
-		        responseEnd: zod_1.default.number(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.HeaderSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        name: zod_1.default.string(),
-		        value: Network.BytesValueSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.InitiatorSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.enum(['parser', 'script', 'preflight', 'other']),
-		        columnNumber: exports.JsUintSchema.optional(),
-		        lineNumber: exports.JsUintSchema.optional(),
-		        stackTrace: Script.StackTraceSchema.optional(),
-		        request: Network.RequestSchema.optional(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.InterceptSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.RequestSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.RequestDataSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        request: Network.RequestSchema,
-		        url: zod_1.default.string(),
-		        method: zod_1.default.string(),
-		        headers: zod_1.default.array(Network.HeaderSchema),
-		        cookies: zod_1.default.array(Network.CookieSchema),
-		        headersSize: exports.JsUintSchema,
-		        bodySize: zod_1.default.union([exports.JsUintSchema, zod_1.default.null()]),
-		        timings: Network.FetchTimingInfoSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ResponseContentSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        size: exports.JsUintSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ResponseDataSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        url: zod_1.default.string(),
-		        protocol: zod_1.default.string(),
-		        status: exports.JsUintSchema,
-		        statusText: zod_1.default.string(),
-		        fromCache: zod_1.default.boolean(),
-		        headers: zod_1.default.array(Network.HeaderSchema),
-		        mimeType: zod_1.default.string(),
-		        bytesReceived: exports.JsUintSchema,
-		        headersSize: zod_1.default.union([exports.JsUintSchema, zod_1.default.null()]),
-		        bodySize: zod_1.default.union([exports.JsUintSchema, zod_1.default.null()]),
-		        content: Network.ResponseContentSchema,
-		        authChallenges: zod_1.default.array(Network.AuthChallengeSchema).optional(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.SetCookieHeaderSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        name: zod_1.default.string(),
-		        value: Network.BytesValueSchema,
-		        domain: zod_1.default.string().optional(),
-		        httpOnly: zod_1.default.boolean().optional(),
-		        expiry: zod_1.default.string().optional(),
-		        maxAge: exports.JsIntSchema.optional(),
-		        path: zod_1.default.string().optional(),
-		        sameSite: Network.SameSiteSchema.optional(),
-		        secure: zod_1.default.boolean().optional(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.UrlPatternSchema = zod_1.default.lazy(() => zod_1.default.union([Network.UrlPatternPatternSchema, Network.UrlPatternStringSchema]));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.UrlPatternPatternSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('pattern'),
-		        protocol: zod_1.default.string().optional(),
-		        hostname: zod_1.default.string().optional(),
-		        port: zod_1.default.string().optional(),
-		        pathname: zod_1.default.string().optional(),
-		        search: zod_1.default.string().optional(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.UrlPatternStringSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('string'),
-		        pattern: zod_1.default.string(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.AddInterceptParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        phases: zod_1.default.array(Network.InterceptPhaseSchema).min(1),
-		        contexts: zod_1.default
-		            .array(BrowsingContext.BrowsingContextSchema)
-		            .min(1)
-		            .optional(),
-		        urlPatterns: zod_1.default.array(Network.UrlPatternSchema).optional(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.AddInterceptSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.addIntercept'),
-		        params: Network.AddInterceptParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.InterceptPhaseSchema = zod_1.default.lazy(() => zod_1.default.enum(['beforeRequestSent', 'responseStarted', 'authRequired']));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.AddInterceptResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        intercept: Network.InterceptSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ContinueRequestSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.continueRequest'),
-		        params: Network.ContinueRequestParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ContinueRequestParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        request: Network.RequestSchema,
-		        body: Network.BytesValueSchema.optional(),
-		        cookies: zod_1.default.array(Network.CookieHeaderSchema).optional(),
-		        headers: zod_1.default.array(Network.HeaderSchema).optional(),
-		        method: zod_1.default.string().optional(),
-		        url: zod_1.default.string().optional(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ContinueResponseSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.continueResponse'),
-		        params: Network.ContinueResponseParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ContinueResponseParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        request: Network.RequestSchema,
-		        cookies: zod_1.default.array(Network.SetCookieHeaderSchema).optional(),
-		        credentials: Network.AuthCredentialsSchema.optional(),
-		        headers: zod_1.default.array(Network.HeaderSchema).optional(),
-		        reasonPhrase: zod_1.default.string().optional(),
-		        statusCode: exports.JsUintSchema.optional(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ContinueWithAuthSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.continueWithAuth'),
-		        params: Network.ContinueWithAuthParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ContinueWithAuthParametersSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        request: Network.RequestSchema,
-		    })
-		        .and(zod_1.default.union([
-		        Network.ContinueWithAuthCredentialsSchema,
-		        Network.ContinueWithAuthNoCredentialsSchema,
-		    ])));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ContinueWithAuthCredentialsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        action: zod_1.default.literal('provideCredentials'),
-		        credentials: Network.AuthCredentialsSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ContinueWithAuthNoCredentialsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        action: zod_1.default.enum(['default', 'cancel']),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.FailRequestSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.failRequest'),
-		        params: Network.FailRequestParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.FailRequestParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        request: Network.RequestSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ProvideResponseSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.provideResponse'),
-		        params: Network.ProvideResponseParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ProvideResponseParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        request: Network.RequestSchema,
-		        body: Network.BytesValueSchema.optional(),
-		        cookies: zod_1.default.array(Network.SetCookieHeaderSchema).optional(),
-		        headers: zod_1.default.array(Network.HeaderSchema).optional(),
-		        reasonPhrase: zod_1.default.string().optional(),
-		        statusCode: exports.JsUintSchema.optional(),
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.RemoveInterceptSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.removeIntercept'),
-		        params: Network.RemoveInterceptParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.RemoveInterceptParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        intercept: Network.InterceptSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		exports.ScriptEventSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Script.MessageSchema,
-		    Script.RealmCreatedSchema,
-		    Script.RealmDestroyedSchema,
-		]));
-		(function (Network) {
-		    Network.AuthRequiredParametersSchema = zod_1.default.lazy(() => Network.BaseParametersSchema.and(zod_1.default.object({
-		        response: Network.ResponseDataSchema,
-		    })));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.BeforeRequestSentParametersSchema = zod_1.default.lazy(() => Network.BaseParametersSchema.and(zod_1.default.object({
-		        initiator: Network.InitiatorSchema,
-		    })));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.FetchErrorParametersSchema = zod_1.default.lazy(() => Network.BaseParametersSchema.and(zod_1.default.object({
-		        errorText: zod_1.default.string(),
-		    })));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ResponseCompletedParametersSchema = zod_1.default.lazy(() => Network.BaseParametersSchema.and(zod_1.default.object({
-		        response: Network.ResponseDataSchema,
-		    })));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ResponseStartedParametersSchema = zod_1.default.lazy(() => Network.BaseParametersSchema.and(zod_1.default.object({
-		        response: Network.ResponseDataSchema,
-		    })));
-		})(Network || (exports.Network = Network = {}));
-		exports.ScriptCommandSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Script.AddPreloadScriptSchema,
-		    Script.CallFunctionSchema,
-		    Script.DisownSchema,
-		    Script.EvaluateSchema,
-		    Script.GetRealmsSchema,
-		    Script.RemovePreloadScriptSchema,
-		]));
-		exports.ScriptResultSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Script.AddPreloadScriptResultSchema,
-		    Script.EvaluateResultSchema,
-		    Script.GetRealmsResultSchema,
-		]));
-		(function (Network) {
-		    Network.AuthRequiredSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.authRequired'),
-		        params: Network.AuthRequiredParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.BeforeRequestSentSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.beforeRequestSent'),
-		        params: Network.BeforeRequestSentParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.FetchErrorSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.fetchError'),
-		        params: Network.FetchErrorParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ResponseCompletedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.responseCompleted'),
-		        params: Network.ResponseCompletedParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		(function (Network) {
-		    Network.ResponseStartedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('network.responseStarted'),
-		        params: Network.ResponseStartedParametersSchema,
-		    }));
-		})(Network || (exports.Network = Network = {}));
-		var Script;
-		(function (Script) {
-		    Script.ChannelSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.EvaluateResultSuccessSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('success'),
-		        result: Script.RemoteValueSchema,
-		        realm: Script.RealmSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ExceptionDetailsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        columnNumber: exports.JsUintSchema,
-		        exception: Script.RemoteValueSchema,
-		        lineNumber: exports.JsUintSchema,
-		        stackTrace: Script.StackTraceSchema,
-		        text: zod_1.default.string(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ChannelValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('channel'),
-		        value: Script.ChannelPropertiesSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ChannelPropertiesSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        channel: Script.ChannelSchema,
-		        serializationOptions: Script.SerializationOptionsSchema.optional(),
-		        ownership: Script.ResultOwnershipSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.EvaluateResultSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Script.EvaluateResultSuccessSchema,
-		        Script.EvaluateResultExceptionSchema,
-		    ]));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.EvaluateResultExceptionSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('exception'),
-		        exceptionDetails: Script.ExceptionDetailsSchema,
-		        realm: Script.RealmSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.HandleSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.InternalIdSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ListLocalValueSchema = zod_1.default.lazy(() => zod_1.default.array(Script.LocalValueSchema));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.LocalValueSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Script.RemoteReferenceSchema,
-		        Script.PrimitiveProtocolValueSchema,
-		        Script.ChannelValueSchema,
-		        Script.ArrayLocalValueSchema,
-		        Script.DateLocalValueSchema,
-		        Script.MapLocalValueSchema,
-		        Script.ObjectLocalValueSchema,
-		        Script.RegExpLocalValueSchema,
-		        Script.SetLocalValueSchema,
-		    ]));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ArrayLocalValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('array'),
-		        value: Script.ListLocalValueSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.DateLocalValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('date'),
-		        value: zod_1.default.string(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.MappingLocalValueSchema = zod_1.default.lazy(() => zod_1.default.array(zod_1.default.tuple([
-		        zod_1.default.union([Script.LocalValueSchema, zod_1.default.string()]),
-		        Script.LocalValueSchema,
-		    ])));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.MapLocalValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('map'),
-		        value: Script.MappingLocalValueSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ObjectLocalValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('object'),
-		        value: Script.MappingLocalValueSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RegExpValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        pattern: zod_1.default.string(),
-		        flags: zod_1.default.string().optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RegExpLocalValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('regexp'),
-		        value: Script.RegExpValueSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.SetLocalValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('set'),
-		        value: Script.ListLocalValueSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.PreloadScriptSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RealmSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.PrimitiveProtocolValueSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Script.UndefinedValueSchema,
-		        Script.NullValueSchema,
-		        Script.StringValueSchema,
-		        Script.NumberValueSchema,
-		        Script.BooleanValueSchema,
-		        Script.BigIntValueSchema,
-		    ]));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.UndefinedValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('undefined'),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.NullValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('null'),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.StringValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('string'),
-		        value: zod_1.default.string(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.SpecialNumberSchema = zod_1.default.lazy(() => zod_1.default.enum(['NaN', '-0', 'Infinity', '-Infinity']));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.NumberValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('number'),
-		        value: zod_1.default.union([zod_1.default.number(), Script.SpecialNumberSchema]),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.BooleanValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('boolean'),
-		        value: zod_1.default.boolean(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.BigIntValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('bigint'),
-		        value: zod_1.default.string(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RealmInfoSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Script.WindowRealmInfoSchema,
-		        Script.DedicatedWorkerRealmInfoSchema,
-		        Script.SharedWorkerRealmInfoSchema,
-		        Script.ServiceWorkerRealmInfoSchema,
-		        Script.WorkerRealmInfoSchema,
-		        Script.PaintWorkletRealmInfoSchema,
-		        Script.AudioWorkletRealmInfoSchema,
-		        Script.WorkletRealmInfoSchema,
-		    ]));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.BaseRealmInfoSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        realm: Script.RealmSchema,
-		        origin: zod_1.default.string(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.WindowRealmInfoSchema = zod_1.default.lazy(() => Script.BaseRealmInfoSchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('window'),
-		        context: BrowsingContext.BrowsingContextSchema,
-		        sandbox: zod_1.default.string().optional(),
-		    })));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.DedicatedWorkerRealmInfoSchema = zod_1.default.lazy(() => Script.BaseRealmInfoSchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('dedicated-worker'),
-		        owners: zod_1.default.tuple([Script.RealmSchema]),
-		    })));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.SharedWorkerRealmInfoSchema = zod_1.default.lazy(() => Script.BaseRealmInfoSchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('shared-worker'),
-		    })));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ServiceWorkerRealmInfoSchema = zod_1.default.lazy(() => Script.BaseRealmInfoSchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('service-worker'),
-		    })));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.WorkerRealmInfoSchema = zod_1.default.lazy(() => Script.BaseRealmInfoSchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('worker'),
-		    })));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.PaintWorkletRealmInfoSchema = zod_1.default.lazy(() => Script.BaseRealmInfoSchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('paint-worklet'),
-		    })));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.AudioWorkletRealmInfoSchema = zod_1.default.lazy(() => Script.BaseRealmInfoSchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('audio-worklet'),
-		    })));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.WorkletRealmInfoSchema = zod_1.default.lazy(() => Script.BaseRealmInfoSchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('worklet'),
-		    })));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RealmTypeSchema = zod_1.default.lazy(() => zod_1.default.enum([
-		        'window',
-		        'dedicated-worker',
-		        'shared-worker',
-		        'service-worker',
-		        'worker',
-		        'paint-worklet',
-		        'audio-worklet',
-		        'worklet',
-		    ]));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ListRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.array(Script.RemoteValueSchema));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.MappingRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.array(zod_1.default.tuple([
-		        zod_1.default.union([Script.RemoteValueSchema, zod_1.default.string()]),
-		        Script.RemoteValueSchema,
-		    ])));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RemoteValueSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Script.PrimitiveProtocolValueSchema,
-		        Script.SymbolRemoteValueSchema,
-		        Script.ArrayRemoteValueSchema,
-		        Script.ObjectRemoteValueSchema,
-		        Script.FunctionRemoteValueSchema,
-		        Script.RegExpRemoteValueSchema,
-		        Script.DateRemoteValueSchema,
-		        Script.MapRemoteValueSchema,
-		        Script.SetRemoteValueSchema,
-		        Script.WeakMapRemoteValueSchema,
-		        Script.WeakSetRemoteValueSchema,
-		        Script.IteratorRemoteValueSchema,
-		        Script.GeneratorRemoteValueSchema,
-		        Script.ErrorRemoteValueSchema,
-		        Script.ProxyRemoteValueSchema,
-		        Script.PromiseRemoteValueSchema,
-		        Script.TypedArrayRemoteValueSchema,
-		        Script.ArrayBufferRemoteValueSchema,
-		        Script.NodeListRemoteValueSchema,
-		        Script.HtmlCollectionRemoteValueSchema,
-		        Script.NodeRemoteValueSchema,
-		        Script.WindowProxyRemoteValueSchema,
-		    ]));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RemoteReferenceSchema = zod_1.default.lazy(() => zod_1.default.union([Script.SharedReferenceSchema, Script.RemoteObjectReferenceSchema]));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.SharedReferenceSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        sharedId: Script.SharedIdSchema,
-		        handle: Script.HandleSchema.optional(),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RemoteObjectReferenceSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        handle: Script.HandleSchema,
-		        sharedId: Script.SharedIdSchema.optional(),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.SymbolRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('symbol'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ArrayRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('array'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		        value: Script.ListRemoteValueSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ObjectRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('object'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		        value: Script.MappingRemoteValueSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.FunctionRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('function'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RegExpRemoteValueSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    })
-		        .and(Script.RegExpLocalValueSchema));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.DateRemoteValueSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    })
-		        .and(Script.DateLocalValueSchema));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.MapRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('map'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		        value: Script.MappingRemoteValueSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.SetRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('set'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		        value: Script.ListRemoteValueSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.WeakMapRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('weakmap'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.WeakSetRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('weakset'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.IteratorRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('iterator'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.GeneratorRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('generator'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ErrorRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('error'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ProxyRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('proxy'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.PromiseRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('promise'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.TypedArrayRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('typedarray'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ArrayBufferRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('arraybuffer'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.NodeListRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('nodelist'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		        value: Script.ListRemoteValueSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.HtmlCollectionRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('htmlcollection'),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		        value: Script.ListRemoteValueSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.NodeRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('node'),
-		        sharedId: Script.SharedIdSchema.optional(),
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		        value: Script.NodePropertiesSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.NodePropertiesSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        nodeType: exports.JsUintSchema,
-		        childNodeCount: exports.JsUintSchema,
-		        attributes: zod_1.default.record(zod_1.default.string(), zod_1.default.string()).optional(),
-		        children: zod_1.default.array(Script.NodeRemoteValueSchema).optional(),
-		        localName: zod_1.default.string().optional(),
-		        mode: zod_1.default.enum(['open', 'closed']).optional(),
-		        namespaceURI: zod_1.default.string().optional(),
-		        nodeValue: zod_1.default.string().optional(),
-		        shadowRoot: zod_1.default.union([Script.NodeRemoteValueSchema, zod_1.default.null()]).optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.WindowProxyRemoteValueSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('window'),
-		        value: Script.WindowProxyPropertiesSchema,
-		        handle: Script.HandleSchema.optional(),
-		        internalId: Script.InternalIdSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.WindowProxyPropertiesSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ResultOwnershipSchema = zod_1.default.lazy(() => zod_1.default.enum(['root', 'none']));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.SerializationOptionsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        maxDomDepth: zod_1.default.union([exports.JsUintSchema, zod_1.default.null()]).default(0).optional(),
-		        maxObjectDepth: zod_1.default
-		            .union([exports.JsUintSchema, zod_1.default.null()])
-		            .default(null)
-		            .optional(),
-		        includeShadowTree: zod_1.default
-		            .enum(['none', 'open', 'all'])
-		            .default('none')
-		            .optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.SharedIdSchema = zod_1.default.lazy(() => zod_1.default.string());
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.StackFrameSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        columnNumber: exports.JsUintSchema,
-		        functionName: zod_1.default.string(),
-		        lineNumber: exports.JsUintSchema,
-		        url: zod_1.default.string(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.StackTraceSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        callFrames: zod_1.default.array(Script.StackFrameSchema),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.SourceSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        realm: Script.RealmSchema,
-		        context: BrowsingContext.BrowsingContextSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RealmTargetSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        realm: Script.RealmSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.ContextTargetSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        sandbox: zod_1.default.string().optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.TargetSchema = zod_1.default.lazy(() => zod_1.default.union([Script.RealmTargetSchema, Script.ContextTargetSchema]));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.AddPreloadScriptSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('script.addPreloadScript'),
-		        params: Script.AddPreloadScriptParametersSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.AddPreloadScriptParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        functionDeclaration: zod_1.default.string(),
-		        arguments: zod_1.default.array(Script.ChannelValueSchema).optional(),
-		        contexts: zod_1.default
-		            .array(BrowsingContext.BrowsingContextSchema)
-		            .min(1)
-		            .optional(),
-		        sandbox: zod_1.default.string().optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.AddPreloadScriptResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        script: Script.PreloadScriptSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.DisownSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('script.disown'),
-		        params: Script.DisownParametersSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.DisownParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        handles: zod_1.default.array(Script.HandleSchema),
-		        target: Script.TargetSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.CallFunctionParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        functionDeclaration: zod_1.default.string(),
-		        awaitPromise: zod_1.default.boolean(),
-		        target: Script.TargetSchema,
-		        arguments: zod_1.default.array(Script.LocalValueSchema).optional(),
-		        resultOwnership: Script.ResultOwnershipSchema.optional(),
-		        serializationOptions: Script.SerializationOptionsSchema.optional(),
-		        this: Script.LocalValueSchema.optional(),
-		        userActivation: zod_1.default.boolean().default(false).optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.CallFunctionSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('script.callFunction'),
-		        params: Script.CallFunctionParametersSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.EvaluateSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('script.evaluate'),
-		        params: Script.EvaluateParametersSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.EvaluateParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        expression: zod_1.default.string(),
-		        target: Script.TargetSchema,
-		        awaitPromise: zod_1.default.boolean(),
-		        resultOwnership: Script.ResultOwnershipSchema.optional(),
-		        serializationOptions: Script.SerializationOptionsSchema.optional(),
-		        userActivation: zod_1.default.boolean().default(false).optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.GetRealmsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('script.getRealms'),
-		        params: Script.GetRealmsParametersSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.GetRealmsParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema.optional(),
-		        type: Script.RealmTypeSchema.optional(),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.GetRealmsResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        realms: zod_1.default.array(Script.RealmInfoSchema),
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RemovePreloadScriptSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('script.removePreloadScript'),
-		        params: Script.RemovePreloadScriptParametersSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RemovePreloadScriptParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        script: Script.PreloadScriptSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.MessageParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        channel: Script.ChannelSchema,
-		        data: Script.RemoteValueSchema,
-		        source: Script.SourceSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RealmCreatedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('script.realmCreated'),
-		        params: Script.RealmInfoSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.MessageSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('script.message'),
-		        params: Script.MessageParametersSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RealmDestroyedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('script.realmDestroyed'),
-		        params: Script.RealmDestroyedParametersSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		(function (Script) {
-		    Script.RealmDestroyedParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        realm: Script.RealmSchema,
-		    }));
-		})(Script || (exports.Script = Script = {}));
-		exports.StorageCommandSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Storage.DeleteCookiesSchema,
-		    Storage.GetCookiesSchema,
-		    Storage.SetCookieSchema,
-		]));
-		exports.StorageResultSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Storage.DeleteCookiesResultSchema,
-		    Storage.GetCookiesResultSchema,
-		    Storage.SetCookieResultSchema,
-		]));
-		var Storage;
-		(function (Storage) {
-		    Storage.PartitionKeySchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        userContext: zod_1.default.string().optional(),
-		        sourceOrigin: zod_1.default.string().optional(),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.GetCookiesSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('storage.getCookies'),
-		        params: Storage.GetCookiesParametersSchema,
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.CookieFilterSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        name: zod_1.default.string().optional(),
-		        value: Network.BytesValueSchema.optional(),
-		        domain: zod_1.default.string().optional(),
-		        path: zod_1.default.string().optional(),
-		        size: exports.JsUintSchema.optional(),
-		        httpOnly: zod_1.default.boolean().optional(),
-		        secure: zod_1.default.boolean().optional(),
-		        sameSite: Network.SameSiteSchema.optional(),
-		        expiry: exports.JsUintSchema.optional(),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.BrowsingContextPartitionDescriptorSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('context'),
-		        context: BrowsingContext.BrowsingContextSchema,
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.StorageKeyPartitionDescriptorSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        type: zod_1.default.literal('storageKey'),
-		        userContext: zod_1.default.string().optional(),
-		        sourceOrigin: zod_1.default.string().optional(),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.PartitionDescriptorSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Storage.BrowsingContextPartitionDescriptorSchema,
-		        Storage.StorageKeyPartitionDescriptorSchema,
-		    ]));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.GetCookiesParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        filter: Storage.CookieFilterSchema.optional(),
-		        partition: Storage.PartitionDescriptorSchema.optional(),
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.GetCookiesResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        cookies: zod_1.default.array(Network.CookieSchema),
-		        partitionKey: Storage.PartitionKeySchema,
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.SetCookieSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('storage.setCookie'),
-		        params: Storage.SetCookieParametersSchema,
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.PartialCookieSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        name: zod_1.default.string(),
-		        value: Network.BytesValueSchema,
-		        domain: zod_1.default.string(),
-		        path: zod_1.default.string().optional(),
-		        httpOnly: zod_1.default.boolean().optional(),
-		        secure: zod_1.default.boolean().optional(),
-		        sameSite: Network.SameSiteSchema.optional(),
-		        expiry: exports.JsUintSchema.optional(),
-		    })
-		        .and(exports.ExtensibleSchema));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.SetCookieParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        cookie: Storage.PartialCookieSchema,
-		        partition: Storage.PartitionDescriptorSchema.optional(),
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.SetCookieResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        partitionKey: Storage.PartitionKeySchema,
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.DeleteCookiesSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('storage.deleteCookies'),
-		        params: Storage.DeleteCookiesParametersSchema,
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.DeleteCookiesParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        filter: Storage.CookieFilterSchema.optional(),
-		        partition: Storage.PartitionDescriptorSchema.optional(),
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		(function (Storage) {
-		    Storage.DeleteCookiesResultSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        partitionKey: Storage.PartitionKeySchema,
-		    }));
-		})(Storage || (exports.Storage = Storage = {}));
-		exports.LogEventSchema = zod_1.default.lazy(() => Log.EntryAddedSchema);
-		var Log;
-		(function (Log) {
-		    Log.LevelSchema = zod_1.default.lazy(() => zod_1.default.enum(['debug', 'info', 'warn', 'error']));
-		})(Log || (exports.Log = Log = {}));
-		(function (Log) {
-		    Log.EntrySchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Log.GenericLogEntrySchema,
-		        Log.ConsoleLogEntrySchema,
-		        Log.JavascriptLogEntrySchema,
-		    ]));
-		})(Log || (exports.Log = Log = {}));
-		(function (Log) {
-		    Log.BaseLogEntrySchema = zod_1.default.lazy(() => zod_1.default.object({
-		        level: Log.LevelSchema,
-		        source: Script.SourceSchema,
-		        text: zod_1.default.union([zod_1.default.string(), zod_1.default.null()]),
-		        timestamp: exports.JsUintSchema,
-		        stackTrace: Script.StackTraceSchema.optional(),
-		    }));
-		})(Log || (exports.Log = Log = {}));
-		(function (Log) {
-		    Log.GenericLogEntrySchema = zod_1.default.lazy(() => Log.BaseLogEntrySchema.and(zod_1.default.object({
-		        type: zod_1.default.string(),
-		    })));
-		})(Log || (exports.Log = Log = {}));
-		(function (Log) {
-		    Log.ConsoleLogEntrySchema = zod_1.default.lazy(() => Log.BaseLogEntrySchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('console'),
-		        method: zod_1.default.string(),
-		        args: zod_1.default.array(Script.RemoteValueSchema),
-		    })));
-		})(Log || (exports.Log = Log = {}));
-		(function (Log) {
-		    Log.JavascriptLogEntrySchema = zod_1.default.lazy(() => Log.BaseLogEntrySchema.and(zod_1.default.object({
-		        type: zod_1.default.literal('javascript'),
-		    })));
-		})(Log || (exports.Log = Log = {}));
-		(function (Log) {
-		    Log.EntryAddedSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('log.entryAdded'),
-		        params: Log.EntrySchema,
-		    }));
-		})(Log || (exports.Log = Log = {}));
-		exports.InputCommandSchema = zod_1.default.lazy(() => zod_1.default.union([
-		    Input.PerformActionsSchema,
-		    Input.ReleaseActionsSchema,
-		    Input.SetFilesSchema,
-		]));
-		var Input;
-		(function (Input) {
-		    Input.ElementOriginSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('element'),
-		        element: Script.SharedReferenceSchema,
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PerformActionsParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        actions: zod_1.default.array(Input.SourceActionsSchema),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.NoneSourceActionsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('none'),
-		        id: zod_1.default.string(),
-		        actions: zod_1.default.array(Input.NoneSourceActionSchema),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.KeySourceActionsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('key'),
-		        id: zod_1.default.string(),
-		        actions: zod_1.default.array(Input.KeySourceActionSchema),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PointerSourceActionsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('pointer'),
-		        id: zod_1.default.string(),
-		        parameters: Input.PointerParametersSchema.optional(),
-		        actions: zod_1.default.array(Input.PointerSourceActionSchema),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PerformActionsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('input.performActions'),
-		        params: Input.PerformActionsParametersSchema,
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.SourceActionsSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Input.NoneSourceActionsSchema,
-		        Input.KeySourceActionsSchema,
-		        Input.PointerSourceActionsSchema,
-		        Input.WheelSourceActionsSchema,
-		    ]));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.NoneSourceActionSchema = zod_1.default.lazy(() => Input.PauseActionSchema);
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.KeySourceActionSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Input.PauseActionSchema,
-		        Input.KeyDownActionSchema,
-		        Input.KeyUpActionSchema,
-		    ]));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PointerTypeSchema = zod_1.default.lazy(() => zod_1.default.enum(['mouse', 'pen', 'touch']));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PointerParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        pointerType: Input.PointerTypeSchema.default('mouse').optional(),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.WheelSourceActionsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('wheel'),
-		        id: zod_1.default.string(),
-		        actions: zod_1.default.array(Input.WheelSourceActionSchema),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PointerSourceActionSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        Input.PauseActionSchema,
-		        Input.PointerDownActionSchema,
-		        Input.PointerUpActionSchema,
-		        Input.PointerMoveActionSchema,
-		    ]));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.WheelSourceActionSchema = zod_1.default.lazy(() => zod_1.default.union([Input.PauseActionSchema, Input.WheelScrollActionSchema]));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PauseActionSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('pause'),
-		        duration: exports.JsUintSchema.optional(),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.KeyDownActionSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('keyDown'),
-		        value: zod_1.default.string(),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.KeyUpActionSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('keyUp'),
-		        value: zod_1.default.string(),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PointerUpActionSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('pointerUp'),
-		        button: exports.JsUintSchema,
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PointerDownActionSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        type: zod_1.default.literal('pointerDown'),
-		        button: exports.JsUintSchema,
-		    })
-		        .and(Input.PointerCommonPropertiesSchema));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PointerMoveActionSchema = zod_1.default.lazy(() => zod_1.default
-		        .object({
-		        type: zod_1.default.literal('pointerMove'),
-		        x: exports.JsIntSchema,
-		        y: exports.JsIntSchema,
-		        duration: exports.JsUintSchema.optional(),
-		        origin: Input.OriginSchema.optional(),
-		    })
-		        .and(Input.PointerCommonPropertiesSchema));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.WheelScrollActionSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        type: zod_1.default.literal('scroll'),
-		        x: exports.JsIntSchema,
-		        y: exports.JsIntSchema,
-		        deltaX: exports.JsIntSchema,
-		        deltaY: exports.JsIntSchema,
-		        duration: exports.JsUintSchema.optional(),
-		        origin: Input.OriginSchema.default('viewport').optional(),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.PointerCommonPropertiesSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        width: exports.JsUintSchema.default(1).optional(),
-		        height: exports.JsUintSchema.default(1).optional(),
-		        pressure: zod_1.default.number().default(0).optional(),
-		        tangentialPressure: zod_1.default.number().default(0).optional(),
-		        twist: zod_1.default
-		            .number()
-		            .int()
-		            .nonnegative()
-		            .gte(0)
-		            .lte(359)
-		            .default(0)
-		            .optional(),
-		        altitudeAngle: zod_1.default
-		            .number()
-		            .gte(0)
-		            .lte(1.5707963267948966)
-		            .default(0)
-		            .optional(),
-		        azimuthAngle: zod_1.default
-		            .number()
-		            .gte(0)
-		            .lte(6.283185307179586)
-		            .default(0)
-		            .optional(),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.OriginSchema = zod_1.default.lazy(() => zod_1.default.union([
-		        zod_1.default.literal('viewport'),
-		        zod_1.default.literal('pointer'),
-		        Input.ElementOriginSchema,
-		    ]));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.ReleaseActionsSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('input.releaseActions'),
-		        params: Input.ReleaseActionsParametersSchema,
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.ReleaseActionsParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.SetFilesSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        method: zod_1.default.literal('input.setFiles'),
-		        params: Input.SetFilesParametersSchema,
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		(function (Input) {
-		    Input.SetFilesParametersSchema = zod_1.default.lazy(() => zod_1.default.object({
-		        context: BrowsingContext.BrowsingContextSchema,
-		        element: Script.SharedReferenceSchema,
-		        files: zod_1.default.array(zod_1.default.string()),
-		    }));
-		})(Input || (exports.Input = Input = {}));
-		
-	} (webdriverBidi));
-
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	var __createBinding$1 = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
-	    if (k2 === undefined) k2 = k;
-	    var desc = Object.getOwnPropertyDescriptor(m, k);
-	    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
-	      desc = { enumerable: true, get: function() { return m[k]; } };
-	    }
-	    Object.defineProperty(o, k2, desc);
-	}) : (function(o, m, k, k2) {
-	    if (k2 === undefined) k2 = k;
-	    o[k2] = m[k];
-	}));
-	var __setModuleDefault$1 = (commonjsGlobal && commonjsGlobal.__setModuleDefault) || (Object.create ? (function(o, v) {
-	    Object.defineProperty(o, "default", { enumerable: true, value: v });
-	}) : function(o, v) {
-	    o["default"] = v;
-	});
-	var __importStar$1 = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
-	    if (mod && mod.__esModule) return mod;
-	    var result = {};
-	    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding$1(result, mod, k);
-	    __setModuleDefault$1(result, mod);
-	    return result;
-	};
-	Object.defineProperty(protocolParser, "__esModule", { value: true });
-	protocolParser.Permissions = protocolParser.Cdp = protocolParser.Storage = protocolParser.Input = protocolParser.Session = protocolParser.BrowsingContext = protocolParser.Script = protocolParser.Network = protocolParser.Browser = protocolParser.parseObject = void 0;
-	/**
-	 * @fileoverview Provides parsing and validator for WebDriver BiDi protocol.
-	 * Parser types should match the `../protocol` types.
-	 */
-	const zod_1 = lib;
-	const protocol_js_1 = protocol;
-	const WebDriverBidiPermissions = __importStar$1(webdriverBidiPermissions);
-	const WebDriverBidi = __importStar$1(webdriverBidi);
-	function parseObject(obj, schema) {
-	    const parseResult = schema.safeParse(obj);
-	    if (parseResult.success) {
-	        return parseResult.data;
-	    }
-	    const errorMessage = parseResult.error.errors
-	        .map((e) => `${e.message} in ` +
-	        `${e.path.map((p) => JSON.stringify(p)).join('/')}.`)
-	        .join(' ');
-	    throw new protocol_js_1.InvalidArgumentException(errorMessage);
-	}
-	protocolParser.parseObject = parseObject;
-	/** @see https://w3c.github.io/webdriver-bidi/#module-browser */
-	var Browser;
-	(function (Browser) {
-	    function parseRemoveUserContextParams(params) {
-	        return parseObject(params, WebDriverBidi.Browser.RemoveUserContextParametersSchema);
-	    }
-	    Browser.parseRemoveUserContextParams = parseRemoveUserContextParams;
-	})(Browser || (protocolParser.Browser = Browser = {}));
-	/** @see https://w3c.github.io/webdriver-bidi/#module-network */
-	var Network;
-	(function (Network) {
-	    function parseAddInterceptParameters(params) {
-	        // Work around of `cddlconv` https://github.com/google/cddlconv/issues/19.
-	        return parseObject(params, WebDriverBidi.Network.AddInterceptParametersSchema);
-	    }
-	    Network.parseAddInterceptParameters = parseAddInterceptParameters;
-	    function parseContinueRequestParameters(params) {
-	        return parseObject(params, WebDriverBidi.Network.ContinueRequestParametersSchema);
-	    }
-	    Network.parseContinueRequestParameters = parseContinueRequestParameters;
-	    function parseContinueResponseParameters(params) {
-	        // TODO: remove cast after https://github.com/google/cddlconv/issues/19 is fixed.
-	        return parseObject(params, WebDriverBidi.Network.ContinueResponseParametersSchema);
-	    }
-	    Network.parseContinueResponseParameters = parseContinueResponseParameters;
-	    function parseContinueWithAuthParameters(params) {
-	        return parseObject(params, WebDriverBidi.Network.ContinueWithAuthParametersSchema);
-	    }
-	    Network.parseContinueWithAuthParameters = parseContinueWithAuthParameters;
-	    function parseFailRequestParameters(params) {
-	        return parseObject(params, WebDriverBidi.Network.FailRequestParametersSchema);
-	    }
-	    Network.parseFailRequestParameters = parseFailRequestParameters;
-	    function parseProvideResponseParameters(params) {
-	        // TODO: remove cast after https://github.com/google/cddlconv/issues/19 is fixed.
-	        return parseObject(params, WebDriverBidi.Network.ProvideResponseParametersSchema);
-	    }
-	    Network.parseProvideResponseParameters = parseProvideResponseParameters;
-	    function parseRemoveInterceptParameters(params) {
-	        return parseObject(params, WebDriverBidi.Network.RemoveInterceptParametersSchema);
-	    }
-	    Network.parseRemoveInterceptParameters = parseRemoveInterceptParameters;
-	})(Network || (protocolParser.Network = Network = {}));
-	/** @see https://w3c.github.io/webdriver-bidi/#module-script */
-	var Script;
-	(function (Script) {
-	    function parseGetRealmsParams(params) {
-	        return parseObject(params, WebDriverBidi.Script.GetRealmsParametersSchema);
-	    }
-	    Script.parseGetRealmsParams = parseGetRealmsParams;
-	    function parseEvaluateParams(params) {
-	        return parseObject(params, WebDriverBidi.Script.EvaluateParametersSchema);
-	    }
-	    Script.parseEvaluateParams = parseEvaluateParams;
-	    function parseDisownParams(params) {
-	        return parseObject(params, WebDriverBidi.Script.DisownParametersSchema);
-	    }
-	    Script.parseDisownParams = parseDisownParams;
-	    function parseAddPreloadScriptParams(params) {
-	        return parseObject(params, WebDriverBidi.Script.AddPreloadScriptParametersSchema);
-	    }
-	    Script.parseAddPreloadScriptParams = parseAddPreloadScriptParams;
-	    function parseRemovePreloadScriptParams(params) {
-	        return parseObject(params, WebDriverBidi.Script.RemovePreloadScriptParametersSchema);
-	    }
-	    Script.parseRemovePreloadScriptParams = parseRemovePreloadScriptParams;
-	    function parseCallFunctionParams(params) {
-	        return parseObject(params, WebDriverBidi.Script.CallFunctionParametersSchema);
-	    }
-	    Script.parseCallFunctionParams = parseCallFunctionParams;
-	})(Script || (protocolParser.Script = Script = {}));
-	/** @see https://w3c.github.io/webdriver-bidi/#module-browsingContext */
-	var BrowsingContext;
-	(function (BrowsingContext) {
-	    function parseActivateParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.ActivateParametersSchema);
-	    }
-	    BrowsingContext.parseActivateParams = parseActivateParams;
-	    function parseGetTreeParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.GetTreeParametersSchema);
-	    }
-	    BrowsingContext.parseGetTreeParams = parseGetTreeParams;
-	    function parseNavigateParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.NavigateParametersSchema);
-	    }
-	    BrowsingContext.parseNavigateParams = parseNavigateParams;
-	    function parseReloadParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.ReloadParametersSchema);
-	    }
-	    BrowsingContext.parseReloadParams = parseReloadParams;
-	    function parseCreateParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.CreateParametersSchema);
-	    }
-	    BrowsingContext.parseCreateParams = parseCreateParams;
-	    function parseCloseParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.CloseParametersSchema);
-	    }
-	    BrowsingContext.parseCloseParams = parseCloseParams;
-	    function parseCaptureScreenshotParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.CaptureScreenshotParametersSchema);
-	    }
-	    BrowsingContext.parseCaptureScreenshotParams = parseCaptureScreenshotParams;
-	    function parsePrintParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.PrintParametersSchema);
-	    }
-	    BrowsingContext.parsePrintParams = parsePrintParams;
-	    function parseSetViewportParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.SetViewportParametersSchema);
-	    }
-	    BrowsingContext.parseSetViewportParams = parseSetViewportParams;
-	    function parseTraverseHistoryParams(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.TraverseHistoryParametersSchema);
-	    }
-	    BrowsingContext.parseTraverseHistoryParams = parseTraverseHistoryParams;
-	    function parseHandleUserPromptParameters(params) {
-	        return parseObject(params, WebDriverBidi.BrowsingContext.HandleUserPromptParametersSchema);
-	    }
-	    BrowsingContext.parseHandleUserPromptParameters = parseHandleUserPromptParameters;
-	    function parseLocateNodesParams(params) {
-	        // TODO: remove cast after https://github.com/google/cddlconv/issues/19 is fixed.
-	        return parseObject(params, WebDriverBidi.BrowsingContext.LocateNodesParametersSchema);
-	    }
-	    BrowsingContext.parseLocateNodesParams = parseLocateNodesParams;
-	})(BrowsingContext || (protocolParser.BrowsingContext = BrowsingContext = {}));
-	/** @see https://w3c.github.io/webdriver-bidi/#module-session */
-	var Session;
-	(function (Session) {
-	    function parseSubscribeParams(params) {
-	        return parseObject(params, WebDriverBidi.Session.SubscriptionRequestSchema);
-	    }
-	    Session.parseSubscribeParams = parseSubscribeParams;
-	})(Session || (protocolParser.Session = Session = {}));
-	var Input;
-	(function (Input) {
-	    function parsePerformActionsParams(params) {
-	        return parseObject(params, WebDriverBidi.Input.PerformActionsParametersSchema);
-	    }
-	    Input.parsePerformActionsParams = parsePerformActionsParams;
-	    function parseReleaseActionsParams(params) {
-	        return parseObject(params, WebDriverBidi.Input.ReleaseActionsParametersSchema);
-	    }
-	    Input.parseReleaseActionsParams = parseReleaseActionsParams;
-	    function parseSetFilesParams(params) {
-	        return parseObject(params, WebDriverBidi.Input.SetFilesParametersSchema);
-	    }
-	    Input.parseSetFilesParams = parseSetFilesParams;
-	})(Input || (protocolParser.Input = Input = {}));
-	var Storage;
-	(function (Storage) {
-	    function parseGetCookiesParams(params) {
-	        // Work around of `cddlconv` https://github.com/google/cddlconv/issues/19.
-	        // The generated schema `SameSiteSchema` in `src/protocol-parser/webdriver-bidi.ts` is
-	        // of type `"none" | "strict" | "lax"` which is not assignable to generated enum
-	        // `SameSite` in `src/protocol/webdriver-bidi.ts`.
-	        // TODO: remove cast after https://github.com/google/cddlconv/issues/19 is fixed.
-	        return parseObject(params, WebDriverBidi.Storage.GetCookiesParametersSchema);
-	    }
-	    Storage.parseGetCookiesParams = parseGetCookiesParams;
-	    function parseSetCookieParams(params) {
-	        // Work around of `cddlconv` https://github.com/google/cddlconv/issues/19.
-	        // The generated schema `SameSiteSchema` in `src/protocol-parser/webdriver-bidi.ts` is
-	        // of type `"none" | "strict" | "lax"` which is not assignable to generated enum
-	        // `SameSite` in `src/protocol/webdriver-bidi.ts`.
-	        // TODO: remove cast after https://github.com/google/cddlconv/issues/19 is fixed.
-	        return parseObject(params, WebDriverBidi.Storage.SetCookieParametersSchema);
-	    }
-	    Storage.parseSetCookieParams = parseSetCookieParams;
-	    function parseDeleteCookiesParams(params) {
-	        // Work around of `cddlconv` https://github.com/google/cddlconv/issues/19.
-	        // The generated schema `SameSiteSchema` in `src/protocol-parser/webdriver-bidi.ts` is
-	        // of type `"none" | "strict" | "lax"` which is not assignable to generated enum
-	        // `SameSite` in `src/protocol/webdriver-bidi.ts`.
-	        // TODO: remove cast after https://github.com/google/cddlconv/issues/19 is fixed.
-	        return parseObject(params, WebDriverBidi.Storage.DeleteCookiesParametersSchema);
-	    }
-	    Storage.parseDeleteCookiesParams = parseDeleteCookiesParams;
-	})(Storage || (protocolParser.Storage = Storage = {}));
-	var Cdp;
-	(function (Cdp) {
-	    const SendCommandRequestSchema = zod_1.z.object({
-	        // Allowing any cdpMethod, and casting to proper type later on.
-	        method: zod_1.z.string(),
-	        // `passthrough` allows object to have any fields.
-	        // https://github.com/colinhacks/zod#passthrough
-	        params: zod_1.z.object({}).passthrough().optional(),
-	        session: zod_1.z.string().optional(),
-	    });
-	    const GetSessionRequestSchema = zod_1.z.object({
-	        context: WebDriverBidi.BrowsingContext.BrowsingContextSchema,
-	    });
-	    const ResolveRealmRequestSchema = zod_1.z.object({
-	        realm: WebDriverBidi.Script.RealmSchema,
-	    });
-	    function parseSendCommandRequest(params) {
-	        return parseObject(params, SendCommandRequestSchema);
-	    }
-	    Cdp.parseSendCommandRequest = parseSendCommandRequest;
-	    function parseGetSessionRequest(params) {
-	        return parseObject(params, GetSessionRequestSchema);
-	    }
-	    Cdp.parseGetSessionRequest = parseGetSessionRequest;
-	    function parseResolveRealmRequest(params) {
-	        return parseObject(params, ResolveRealmRequestSchema);
-	    }
-	    Cdp.parseResolveRealmRequest = parseResolveRealmRequest;
-	})(Cdp || (protocolParser.Cdp = Cdp = {}));
-	var Permissions;
-	(function (Permissions) {
-	    function parseSetPermissionsParams(params) {
-	        return {
-	            // TODO: remove once "goog:" attributes are not needed.
-	            ...params,
-	            ...parseObject(params, WebDriverBidiPermissions.Permissions.SetPermissionParametersSchema),
-	        };
-	    }
-	    Permissions.parseSetPermissionsParams = parseSetPermissionsParams;
-	})(Permissions || (protocolParser.Permissions = Permissions = {}));
-
-	var __createBinding = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
-	    if (k2 === undefined) k2 = k;
-	    var desc = Object.getOwnPropertyDescriptor(m, k);
-	    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
-	      desc = { enumerable: true, get: function() { return m[k]; } };
-	    }
-	    Object.defineProperty(o, k2, desc);
-	}) : (function(o, m, k, k2) {
-	    if (k2 === undefined) k2 = k;
-	    o[k2] = m[k];
-	}));
-	var __setModuleDefault = (commonjsGlobal && commonjsGlobal.__setModuleDefault) || (Object.create ? (function(o, v) {
-	    Object.defineProperty(o, "default", { enumerable: true, value: v });
-	}) : function(o, v) {
-	    o["default"] = v;
-	});
-	var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
-	    if (mod && mod.__esModule) return mod;
-	    var result = {};
-	    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
-	    __setModuleDefault(result, mod);
-	    return result;
-	};
-	Object.defineProperty(BidiParser$1, "__esModule", { value: true });
-	BidiParser$1.BidiParser = void 0;
-	const Parser = __importStar(protocolParser);
-	class BidiParser {
-	    // Browser domain
-	    // keep-sorted start block=yes
-	    parseRemoveUserContextParams(params) {
-	        return Parser.Browser.parseRemoveUserContextParams(params);
-	    }
-	    // keep-sorted end
-	    // Browsing Context domain
-	    // keep-sorted start block=yes
-	    parseActivateParams(params) {
-	        return Parser.BrowsingContext.parseActivateParams(params);
-	    }
-	    parseCaptureScreenshotParams(params) {
-	        return Parser.BrowsingContext.parseCaptureScreenshotParams(params);
-	    }
-	    parseCloseParams(params) {
-	        return Parser.BrowsingContext.parseCloseParams(params);
-	    }
-	    parseCreateParams(params) {
-	        return Parser.BrowsingContext.parseCreateParams(params);
-	    }
-	    parseGetTreeParams(params) {
-	        return Parser.BrowsingContext.parseGetTreeParams(params);
-	    }
-	    parseHandleUserPromptParams(params) {
-	        return Parser.BrowsingContext.parseHandleUserPromptParameters(params);
-	    }
-	    parseLocateNodesParams(params) {
-	        return Parser.BrowsingContext.parseLocateNodesParams(params);
-	    }
-	    parseNavigateParams(params) {
-	        return Parser.BrowsingContext.parseNavigateParams(params);
-	    }
-	    parsePrintParams(params) {
-	        return Parser.BrowsingContext.parsePrintParams(params);
-	    }
-	    parseReloadParams(params) {
-	        return Parser.BrowsingContext.parseReloadParams(params);
-	    }
-	    parseSetViewportParams(params) {
-	        return Parser.BrowsingContext.parseSetViewportParams(params);
-	    }
-	    parseTraverseHistoryParams(params) {
-	        return Parser.BrowsingContext.parseTraverseHistoryParams(params);
-	    }
-	    // keep-sorted end
-	    // CDP domain
-	    // keep-sorted start block=yes
-	    parseGetSessionParams(params) {
-	        return Parser.Cdp.parseGetSessionRequest(params);
-	    }
-	    parseResolveRealmParams(params) {
-	        return Parser.Cdp.parseResolveRealmRequest(params);
-	    }
-	    parseSendCommandParams(params) {
-	        return Parser.Cdp.parseSendCommandRequest(params);
-	    }
-	    // keep-sorted end
-	    // Input domain
-	    // keep-sorted start block=yes
-	    parsePerformActionsParams(params) {
-	        return Parser.Input.parsePerformActionsParams(params);
-	    }
-	    parseReleaseActionsParams(params) {
-	        return Parser.Input.parseReleaseActionsParams(params);
-	    }
-	    parseSetFilesParams(params) {
-	        return Parser.Input.parseSetFilesParams(params);
-	    }
-	    // keep-sorted end
-	    // Network domain
-	    // keep-sorted start block=yes
-	    parseAddInterceptParams(params) {
-	        return Parser.Network.parseAddInterceptParameters(params);
-	    }
-	    parseContinueRequestParams(params) {
-	        return Parser.Network.parseContinueRequestParameters(params);
-	    }
-	    parseContinueResponseParams(params) {
-	        return Parser.Network.parseContinueResponseParameters(params);
-	    }
-	    parseContinueWithAuthParams(params) {
-	        return Parser.Network.parseContinueWithAuthParameters(params);
-	    }
-	    parseFailRequestParams(params) {
-	        return Parser.Network.parseFailRequestParameters(params);
-	    }
-	    parseProvideResponseParams(params) {
-	        return Parser.Network.parseProvideResponseParameters(params);
-	    }
-	    parseRemoveInterceptParams(params) {
-	        return Parser.Network.parseRemoveInterceptParameters(params);
-	    }
-	    // keep-sorted end
-	    // Permissions domain
-	    // keep-sorted start block=yes
-	    parseSetPermissionsParams(params) {
-	        return Parser.Permissions.parseSetPermissionsParams(params);
-	    }
-	    // keep-sorted end
-	    // Script domain
-	    // keep-sorted start block=yes
-	    parseAddPreloadScriptParams(params) {
-	        return Parser.Script.parseAddPreloadScriptParams(params);
-	    }
-	    parseCallFunctionParams(params) {
-	        return Parser.Script.parseCallFunctionParams(params);
-	    }
-	    parseDisownParams(params) {
-	        return Parser.Script.parseDisownParams(params);
-	    }
-	    parseEvaluateParams(params) {
-	        return Parser.Script.parseEvaluateParams(params);
-	    }
-	    parseGetRealmsParams(params) {
-	        return Parser.Script.parseGetRealmsParams(params);
-	    }
-	    parseRemovePreloadScriptParams(params) {
-	        return Parser.Script.parseRemovePreloadScriptParams(params);
-	    }
-	    // keep-sorted end
-	    // Session domain
-	    // keep-sorted start block=yes
-	    parseSubscribeParams(params) {
-	        return Parser.Session.parseSubscribeParams(params);
-	    }
-	    // keep-sorted end
-	    // Storage domain
-	    // keep-sorted start block=yes
-	    parseDeleteCookiesParams(params) {
-	        return Parser.Storage.parseDeleteCookiesParams(params);
-	    }
-	    parseGetCookiesParams(params) {
-	        return Parser.Storage.parseGetCookiesParams(params);
-	    }
-	    parseSetCookieParams(params) {
-	        return Parser.Storage.parseSetCookieParams(params);
-	    }
-	}
-	BidiParser$1.BidiParser = BidiParser;
-
-	var mapperTabPage = {};
-
-	Object.defineProperty(mapperTabPage, "__esModule", { value: true });
-	mapperTabPage.log = mapperTabPage.generatePage = void 0;
-	/**
-	 * Copyright 2022 Google LLC.
-	 * Copyright (c) Microsoft Corporation.
-	 *
-	 * 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
-	 *
-	 *     http://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.
-	 */
-	const log_js_1$2 = log$1;
-	/** HTML source code for the user-facing Mapper tab. */
-	const mapperPageSource = '<!DOCTYPE html><title>BiDi-CDP Mapper</title><style>body{font-family: Roboto, serif; font-size: 13px; color: #202124;}.log{padding: 12px; font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; font-size: 11px; line-height: 180%; background: #f1f3f4; border-radius: 4px;}.pre{overflow-wrap: break-word; padding: 10px;}.card{margin: 60px auto; padding: 2px 0; max-width: 900px; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15), 0 1px 6px rgba(0, 0, 0, 0.2); border-radius: 8px;}.divider{height: 1px; background: #f0f0f0;}.item{padding: 16px 20px;}</style><div class="card"><div class="item"><h1>BiDi-CDP Mapper is controlling this tab</h1><p>Closing or reloading it will stop the BiDi process. <a target="_blank" title="BiDi-CDP Mapper GitHub Repository" href="https://github.com/GoogleChromeLabs/chromium-bidi">Details.</a></p></div><div class="divider"></div><details id="details"><summary class="item">Debug information</summary></details></div>';
-	/**
-	 * The following piece of HTML should be added to the `debug` element:
-	 *
-	 * <div class="divider"></div>
-	 * <div class="item">
-	 * <h3>${name}</h3>
-	 * <div id="${name}_log" class="log">
-	 */
-	function findOrCreateTypeLogContainer(logPrefix) {
-	    const logType = logPrefix.split(':')[0];
-	    const containerId = `${logType}_log`;
-	    const existingContainer = document.getElementById(containerId);
-	    if (existingContainer) {
-	        return existingContainer;
-	    }
-	    const debugElement = document.getElementById('details');
-	    const divider = document.createElement('div');
-	    divider.className = 'divider';
-	    debugElement.appendChild(divider);
-	    const htmlItem = document.createElement('div');
-	    htmlItem.className = 'item';
-	    htmlItem.innerHTML = `<h3>${logType}</h3><div id="${containerId}" class="log"></div>`;
-	    debugElement.appendChild(htmlItem);
-	    return document.getElementById(containerId);
-	}
-	function generatePage() {
-	    // If run not in browser (e.g. unit test), do nothing.
-	    if (!globalThis.document.documentElement) {
-	        return;
-	    }
-	    globalThis.document.documentElement.innerHTML = mapperPageSource;
-	    // Create main log containers in proper order.
-	    findOrCreateTypeLogContainer(log_js_1$2.LogType.debugInfo);
-	    findOrCreateTypeLogContainer(log_js_1$2.LogType.bidi);
-	    findOrCreateTypeLogContainer(log_js_1$2.LogType.cdp);
-	}
-	mapperTabPage.generatePage = generatePage;
-	function stringify(message) {
-	    if (typeof message === 'object') {
-	        return JSON.stringify(message, null, 2);
-	    }
-	    return message;
-	}
-	function log(logPrefix, ...messages) {
-	    // If run not in browser (e.g. unit test), do nothing.
-	    if (!globalThis.document.documentElement) {
-	        return;
-	    }
-	    // Skip sending BiDi logs as they are logged once by `bidi:server:*`
-	    if (!logPrefix.startsWith(log_js_1$2.LogType.bidi)) {
-	        // If `sendDebugMessage` is defined, send the log message there.
-	        globalThis.window?.sendDebugMessage?.(JSON.stringify({ logType: logPrefix, messages }));
-	    }
-	    const typeLogContainer = findOrCreateTypeLogContainer(logPrefix);
-	    // This piece of HTML should be added:
-	    // <div class="pre">...log message...</div>
-	    const lineElement = document.createElement('div');
-	    lineElement.className = 'pre';
-	    lineElement.textContent = [logPrefix, ...messages].map(stringify).join(' ');
-	    typeLogContainer.appendChild(lineElement);
-	    if (typeLogContainer.childNodes.length > 200) {
-	        typeLogContainer.removeChild(typeLogContainer.childNodes[0]);
-	    }
-	}
-	mapperTabPage.log = log;
-
-	var Transport = {};
-
-	Object.defineProperty(Transport, "__esModule", { value: true });
-	Transport.WindowCdpTransport = Transport.WindowBidiTransport = void 0;
-	const log_js_1$1 = log$1;
-	const mapperTabPage_js_1$1 = mapperTabPage;
-	class WindowBidiTransport {
-	    static LOGGER_PREFIX_RECV = `${log_js_1$1.LogType.bidi}:RECV â—‚`;
-	    static LOGGER_PREFIX_SEND = `${log_js_1$1.LogType.bidi}:SEND â–¸`;
-	    #onMessage = null;
-	    constructor() {
-	        window.onBidiMessage = (message) => {
-	            (0, mapperTabPage_js_1$1.log)(WindowBidiTransport.LOGGER_PREFIX_RECV, message);
-	            try {
-	                const command = WindowBidiTransport.#parseBidiMessage(message);
-	                this.#onMessage?.call(null, command);
-	            }
-	            catch (e) {
-	                const error = e instanceof Error ? e : new Error(e);
-	                // Transport-level error does not provide channel.
-	                this.#respondWithError(message, "invalid argument" /* ErrorCode.InvalidArgument */, error, null);
-	            }
-	        };
-	    }
-	    setOnMessage(onMessage) {
-	        this.#onMessage = onMessage;
-	    }
-	    sendMessage(message) {
-	        (0, mapperTabPage_js_1$1.log)(WindowBidiTransport.LOGGER_PREFIX_SEND, message);
-	        const json = JSON.stringify(message);
-	        window.sendBidiResponse(json);
-	    }
-	    close() {
-	        this.#onMessage = null;
-	        window.onBidiMessage = null;
-	    }
-	    #respondWithError(plainCommandData, errorCode, error, channel) {
-	        const errorResponse = WindowBidiTransport.#getErrorResponse(plainCommandData, errorCode, error);
-	        if (channel) {
-	            this.sendMessage({
-	                ...errorResponse,
-	                channel,
-	            });
-	        }
-	        else {
-	            this.sendMessage(errorResponse);
-	        }
-	    }
-	    static #getJsonType(value) {
-	        if (value === null) {
-	            return 'null';
-	        }
-	        if (Array.isArray(value)) {
-	            return 'array';
-	        }
-	        return typeof value;
-	    }
-	    static #getErrorResponse(message, errorCode, error) {
-	        // XXX: this is bizarre per spec. We reparse the payload and
-	        // extract the ID, regardless of what kind of value it was.
-	        let messageId;
-	        try {
-	            const command = JSON.parse(message);
-	            if (WindowBidiTransport.#getJsonType(command) === 'object' &&
-	                'id' in command) {
-	                messageId = command.id;
-	            }
-	        }
-	        catch { }
-	        return {
-	            type: 'error',
-	            id: messageId,
-	            error: errorCode,
-	            message: error.message,
-	        };
-	    }
-	    static #parseBidiMessage(message) {
-	        let command;
-	        try {
-	            command = JSON.parse(message);
-	        }
-	        catch {
-	            throw new Error('Cannot parse data as JSON');
-	        }
-	        const type = WindowBidiTransport.#getJsonType(command);
-	        if (type !== 'object') {
-	            throw new Error(`Expected JSON object but got ${type}`);
-	        }
-	        // Extract and validate id, method and params.
-	        const { id, method, params } = command;
-	        const idType = WindowBidiTransport.#getJsonType(id);
-	        if (idType !== 'number' || !Number.isInteger(id) || id < 0) {
-	            // TODO: should uint64_t be the upper limit?
-	            // https://tools.ietf.org/html/rfc7049#section-2.1
-	            throw new Error(`Expected unsigned integer but got ${idType}`);
-	        }
-	        const methodType = WindowBidiTransport.#getJsonType(method);
-	        if (methodType !== 'string') {
-	            throw new Error(`Expected string method but got ${methodType}`);
-	        }
-	        const paramsType = WindowBidiTransport.#getJsonType(params);
-	        if (paramsType !== 'object') {
-	            throw new Error(`Expected object params but got ${paramsType}`);
-	        }
-	        let channel = command.channel;
-	        if (channel !== undefined) {
-	            const channelType = WindowBidiTransport.#getJsonType(channel);
-	            if (channelType !== 'string') {
-	                throw new Error(`Expected string channel but got ${channelType}`);
-	            }
-	            // Empty string channel is considered as no channel provided.
-	            if (channel === '') {
-	                channel = undefined;
-	            }
-	        }
-	        return { id, method, params, channel };
-	    }
-	}
-	Transport.WindowBidiTransport = WindowBidiTransport;
-	class WindowCdpTransport {
-	    #onMessage = null;
-	    constructor() {
-	        window.cdp.onmessage = (message) => {
-	            this.#onMessage?.call(null, message);
-	        };
-	    }
-	    setOnMessage(onMessage) {
-	        this.#onMessage = onMessage;
-	    }
-	    sendMessage(message) {
-	        window.cdp.send(message);
-	    }
-	    close() {
-	        this.#onMessage = null;
-	        window.cdp.onmessage = null;
-	    }
-	}
-	Transport.WindowCdpTransport = WindowCdpTransport;
-
-	/**
+var mapperTab=function(){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function t(e){if(e.__esModule)return e;var t=e.default;if("function"==typeof t){var a=function e(){return this instanceof e?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};a.prototype=t.prototype}else a={};return Object.defineProperty(a,"__esModule",{value:!0}),Object.keys(e).forEach((function(t){var r=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(a,t,r.get?r:{enumerable:!0,get:function(){return e[t]}})})),a}var a={},r={},n={},s={};var o=Object.freeze({__proto__:null,default:function(e){return{all:e=e||new Map,on:function(t,a){var r=e.get(t);r?r.push(a):e.set(t,[a])},off:function(t,a){var r=e.get(t);r&&(a?r.splice(r.indexOf(a)>>>0,1):e.set(t,[]))},emit:function(t,a){var r=e.get(t);r&&r.slice().map((function(e){e(a)})),(r=e.get("*"))&&r.slice().map((function(e){e(t,a)}))}}}}),i=t(o),c=e&&e.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(s,"__esModule",{value:!0}),s.EventEmitter=void 0;const d=c(i);s.EventEmitter=class{#e=(0,d.default)();on(e,t){return this.#e.on(e,t),this}once(e,t){const a=r=>{t(r),this.off(e,a)};return this.on(e,a)}off(e,t){return this.#e.off(e,t),this}emit(e,t){this.#e.emit(e,t)}removeAllListeners(e){return e?this.#e.all.delete(e):this.#e.all.clear(),this}};var u,l={};Object.defineProperty(l,"__esModule",{value:!0}),l.LogType=void 0,function(e){e.bidi="bidi",e.cdp="cdp",e.debug="debug",e.debugError="debug:error",e.debugInfo="debug:info"}(u||(l.LogType=u={}));var h={};Object.defineProperty(h,"__esModule",{value:!0}),h.ProcessingQueue=void 0;const p=l;class m{static LOGGER_PREFIX=`${p.LogType.debug}:queue`;#t;#a;#r=[];#n=!1;constructor(e,t){this.#a=e,this.#t=t}add(e,t){this.#r.push([e,t]),this.#s()}async#s(){if(!this.#n){for(this.#n=!0;this.#r.length>0;){const e=this.#r.shift();if(!e)continue;const[t,a]=e;this.#t?.(m.LOGGER_PREFIX,"Processing event:",a),await t.then((e=>{if("error"!==e.kind)return this.#a(e.value);this.#t?.(p.LogType.debugError,"Event threw before sending:",e.error.message,e.error.stack)})).catch((e=>{this.#t?.(p.LogType.debugError,"Event was not processed:",e?.message)}))}this.#n=!1}}}h.ProcessingQueue=m;var f={},g={},y={};Object.defineProperty(y,"__esModule",{value:!0});var S,v,w,C,b,x={};Object.defineProperty(x,"__esModule",{value:!0}),x.EVENT_NAMES=x.Network=x.BrowsingContext=x.Log=x.Script=x.BiDiModule=void 0,function(e){e.Browser="browser",e.BrowsingContext="browsingContext",e.Cdp="cdp",e.Input="input",e.Log="log",e.Network="network",e.Script="script",e.Session="session"}(S||(x.BiDiModule=S={})),function(e){var t;(t=e.EventNames||(e.EventNames={})).Message="script.message",t.RealmCreated="script.realmCreated",t.RealmDestroyed="script.realmDestroyed"}(v||(x.Script=v={})),function(e){(e.EventNames||(e.EventNames={})).LogEntryAdded="log.entryAdded"}(w||(x.Log=w={})),function(e){var t;(t=e.EventNames||(e.EventNames={})).ContextCreated="browsingContext.contextCreated",t.ContextDestroyed="browsingContext.contextDestroyed",t.DomContentLoaded="browsingContext.domContentLoaded",t.DownloadWillBegin="browsingContext.downloadWillBegin",t.FragmentNavigated="browsingContext.fragmentNavigated",t.Load="browsingContext.load",t.NavigationAborted="browsingContext.navigationAborted",t.NavigationFailed="browsingContext.navigationFailed",t.NavigationStarted="browsingContext.navigationStarted",t.UserPromptClosed="browsingContext.userPromptClosed",t.UserPromptOpened="browsingContext.userPromptOpened"}(C||(x.BrowsingContext=C={})),function(e){var t;(t=e.EventNames||(e.EventNames={})).AuthRequired="network.authRequired",t.BeforeRequestSent="network.beforeRequestSent",t.FetchError="network.fetchError",t.ResponseCompleted="network.responseCompleted",t.ResponseStarted="network.responseStarted"}(b||(x.Network=b={})),x.EVENT_NAMES=new Set([...Object.values(S),...Object.values(C.EventNames),...Object.values(w.EventNames),...Object.values(b.EventNames),...Object.values(v.EventNames)]);var P={};Object.defineProperty(P,"__esModule",{value:!0});var I={};Object.defineProperty(I,"__esModule",{value:!0}),I.UnderspecifiedStoragePartitionException=I.UnableToSetFileInputException=I.UnableToSetCookieException=I.NoSuchStoragePartitionException=I.UnsupportedOperationException=I.UnableToCloseBrowserException=I.UnableToCaptureScreenException=I.UnknownErrorException=I.UnknownCommandException=I.SessionNotCreatedException=I.NoSuchUserContextException=I.NoSuchScriptException=I.NoSuchRequestException=I.NoSuchNodeException=I.NoSuchInterceptException=I.NoSuchHistoryEntryException=I.NoSuchHandleException=I.NoSuchFrameException=I.NoSuchElementException=I.NoSuchAlertException=I.MoveTargetOutOfBoundsException=I.InvalidSessionIdException=I.InvalidArgumentException=I.Exception=void 0;class k{error;message;stacktrace;constructor(e,t,a){this.error=e,this.message=t,this.stacktrace=a}toErrorResponse(e){return{type:"error",id:e,error:this.error,message:this.message,stacktrace:this.stacktrace}}}I.Exception=k;I.InvalidArgumentException=class extends k{constructor(e,t){super("invalid argument",e,t)}};I.InvalidSessionIdException=class extends k{constructor(e,t){super("invalid session id",e,t)}};I.MoveTargetOutOfBoundsException=class extends k{constructor(e,t){super("move target out of bounds",e,t)}};I.NoSuchAlertException=class extends k{constructor(e,t){super("no such alert",e,t)}};I.NoSuchElementException=class extends k{constructor(e,t){super("no such element",e,t)}};I.NoSuchFrameException=class extends k{constructor(e,t){super("no such frame",e,t)}};I.NoSuchHandleException=class extends k{constructor(e,t){super("no such handle",e,t)}};I.NoSuchHistoryEntryException=class extends k{constructor(e,t){super("no such history entry",e,t)}};I.NoSuchInterceptException=class extends k{constructor(e,t){super("no such intercept",e,t)}};I.NoSuchNodeException=class extends k{constructor(e,t){super("no such node",e,t)}};I.NoSuchRequestException=class extends k{constructor(e,t){super("no such request",e,t)}};I.NoSuchScriptException=class extends k{constructor(e,t){super("no such script",e,t)}};I.NoSuchUserContextException=class extends k{constructor(e,t){super("no such user context",e,t)}};I.SessionNotCreatedException=class extends k{constructor(e,t){super("session not created",e,t)}};I.UnknownCommandException=class extends k{constructor(e,t){super("unknown command",e,t)}};I.UnknownErrorException=class extends k{constructor(e,t=(new Error).stack){super("unknown error",e,t)}};I.UnableToCaptureScreenException=class extends k{constructor(e,t){super("unable to capture screen",e,t)}};I.UnableToCloseBrowserException=class extends k{constructor(e,t){super("unable to close browser",e,t)}};I.UnsupportedOperationException=class extends k{constructor(e,t){super("unsupported operation",e,t)}};I.NoSuchStoragePartitionException=class extends k{constructor(e,t){super("no such storage partition",e,t)}};I.UnableToSetCookieException=class extends k{constructor(e,t){super("unable to set cookie",e,t)}};I.UnableToSetFileInputException=class extends k{constructor(e,t){super("unable to set file input",e,t)}};I.UnderspecifiedStoragePartitionException=class extends k{constructor(e,t){super("underspecified storage partition",e,t)}};var R={};Object.defineProperty(R,"__esModule",{value:!0}),function(t){var a=e&&e.__createBinding||(Object.create?function(e,t,a,r){void 0===r&&(r=a);var n=Object.getOwnPropertyDescriptor(t,a);n&&!("get"in n?!t.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return t[a]}}),Object.defineProperty(e,r,n)}:function(e,t,a,r){void 0===r&&(r=a),e[r]=t[a]}),r=e&&e.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=e&&e.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&a(t,e,n);return r(t,e),t},s=e&&e.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||a(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),t.ChromiumBidi=t.Cdp=void 0,t.Cdp=n(y),t.ChromiumBidi=n(x),s(P,t),s(I,t),s(R,t)}(g);var E={};Object.defineProperty(E,"__esModule",{value:!0}),E.BidiNoOpParser=void 0;E.BidiNoOpParser=class{parseActivateParams(e){return e}parseCaptureScreenshotParams(e){return e}parseCloseParams(e){return e}parseCreateParams(e){return e}parseGetTreeParams(e){return e}parseHandleUserPromptParams(e){return e}parseNavigateParams(e){return e}parsePrintParams(e){return e}parseReloadParams(e){return e}parseSetViewportParams(e){return e}parseTraverseHistoryParams(e){return e}parseGetSessionParams(e){return e}parseSendCommandParams(e){return e}parseAddPreloadScriptParams(e){return e}parseCallFunctionParams(e){return e}parseDisownParams(e){return e}parseEvaluateParams(e){return e}parseGetRealmsParams(e){return e}parseRemovePreloadScriptParams(e){return e}parsePerformActionsParams(e){return e}parseReleaseActionsParams(e){return e}parseSetFilesParams(e){return e}parseAddInterceptParams(e){return e}parseContinueRequestParams(e){return e}parseContinueResponseParams(e){return e}parseContinueWithAuthParams(e){return e}parseFailRequestParams(e){return e}parseProvideResponseParams(e){return e}parseRemoveInterceptParams(e){return e}parseSetPermissionsParams(e){return e}parseSubscribeParams(e){return e}parseDeleteCookiesParams(e){return e}parseGetCookiesParams(e){return e}parseSetCookieParams(e){return e}};var _={};Object.defineProperty(_,"__esModule",{value:!0}),_.BrowserProcessor=void 0;const T=g;_.BrowserProcessor=class{#o;constructor(e){this.#o=e}close(){return setTimeout((()=>this.#o.sendCommand("Browser.close")),0),{}}async createUserContext(){return{userContext:(await this.#o.sendCommand("Target.createBrowserContext")).browserContextId}}async removeUserContext(e){if("default"===e)throw new T.InvalidArgumentException("`default` user context cannot be removed");try{await this.#o.sendCommand("Target.disposeBrowserContext",{browserContextId:e})}catch(e){if(e.message.startsWith("Failed to find context with id"))throw new T.NoSuchUserContextException(e.message);throw e}return{}}async getUserContexts(){return{userContexts:[{userContext:"default"},...(await this.#o.sendCommand("Target.getBrowserContexts")).browserContextIds.map((e=>({userContext:e})))]}}};var N={};Object.defineProperty(N,"__esModule",{value:!0}),N.CdpProcessor=void 0;N.CdpProcessor=class{#i;#c;#o;constructor(e,t,a){this.#i=e,this.#c=t,this.#o=a}getSession(e){const t=e.context,a=this.#i.getContext(t).cdpTarget.cdpSessionId;return void 0===a?{}:{session:a}}async sendCommand(e){const t=e.session?this.#c.getCdpClient(e.session):this.#o;return{result:await t.sendCommand(e.method,e.params),session:e.session}}};var j={},O={},M={},A={};Object.defineProperty(A,"__esModule",{value:!0}),A.uuidv4=void 0,A.uuidv4=function(){if("crypto"in globalThis&&"randomUUID"in globalThis.crypto)return globalThis.crypto.randomUUID();const e=new Uint8Array(16);"crypto"in globalThis&&"getRandomValues"in globalThis.crypto?globalThis.crypto.getRandomValues(e):require("crypto").webcrypto.getRandomValues(e),e[6]=15&e[6]|64,e[8]=63&e[8]|128;const t=e=>e.reduce(((e,t)=>e+t.toString(16).padStart(2,"0")),"");return[t(e.subarray(0,4)),t(e.subarray(4,6)),t(e.subarray(6,8)),t(e.subarray(8,10)),t(e.subarray(10,16))].join("-")};var B={};Object.defineProperty(B,"__esModule",{value:!0}),B.ChannelProxy=void 0;const z=g,D=l,L=A;class Z{#d;#u=(0,L.uuidv4)();#t;constructor(e,t){this.#d=e,this.#t=t}async init(e,t){const a=await Z.#l(e),r=await Z.#h(e,a);return this.#p(e,a,t),r}async startListenerFromWindow(e,t){try{const a=await this.#m(e);this.#p(e,a,t)}catch(e){this.#t?.(D.LogType.debugError,e)}}static#f(){return`(${String((()=>{const e=[];let t=null;return{async getMessage(){const a=e.length>0?Promise.resolve():new Promise((e=>{t=e}));return await a,e.shift()},sendMessage(a){e.push(a),null!==t&&(t(),t=null)}}}))})()`}static async#l(e){const t=await e.cdpClient.sendCommand("Runtime.evaluate",{expression:this.#f(),contextId:e.executionContextId,serializationOptions:{serialization:"idOnly"}});if(t.exceptionDetails||void 0===t.result.objectId)throw new Error("Cannot create channel");return t.result.objectId}static async#h(e,t){return(await e.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((e=>e.sendMessage)),arguments:[{objectId:t}],executionContextId:e.executionContextId,serializationOptions:{serialization:"idOnly"}})).result.objectId}async#p(e,t,a){for(;;)try{const r=await e.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((async e=>await e.getMessage())),arguments:[{objectId:t}],awaitPromise:!0,executionContextId:e.executionContextId,serializationOptions:{serialization:"deep",maxDepth:this.#d.serializationOptions?.maxObjectDepth??void 0}});if(r.exceptionDetails)throw r.exceptionDetails;for(const t of e.associatedBrowsingContexts)a.registerEvent({type:"event",method:z.ChromiumBidi.Script.EventNames.Message,params:{channel:this.#d.channel,data:e.cdpToBidiValue(r,this.#d.ownership??"none"),source:e.source}},t.id)}catch(e){this.#t?.(D.LogType.debugError,e);break}}async#m(e){const t=await e.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((e=>{const t=window;if(void 0===t[e])return new Promise((a=>t[e]=a));const a=t[e];return delete t[e],a})),arguments:[{value:this.#u}],executionContextId:e.executionContextId,awaitPromise:!0,serializationOptions:{serialization:"idOnly"}});if(void 0!==t.exceptionDetails||void 0===t.result.objectId)throw new Error(`ChannelHandle not found in window["${this.#u}"]`);return t.result.objectId}getEvalInWindowStr(){const e=String(((e,t)=>{const a=window;return void 0===a[e]?a[e]=t:(a[e](t),delete a[e]),t.sendMessage})),t=Z.#f();return`(${e})('${this.#u}',${t})`}}B.ChannelProxy=Z,Object.defineProperty(M,"__esModule",{value:!0}),M.Realm=void 0;const U=g,F=l,q=A,V=B;class ${#g;#y;#S;#t;#v;#w;#C;constructor(e,t,a,r,n,s,o){this.#g=e,this.#y=t,this.#S=a,this.#t=r,this.#v=n,this.#w=s,this.#C=o,this.#C.addRealm(this)}cdpToBidiValue(e,t){const a=this.serializeForBiDi(e.result.deepSerializedValue,new Map);if(e.result.objectId){const r=e.result.objectId;"root"===t?(a.handle=r,this.#C.knownHandlesToRealmMap.set(r,this.realmId)):this.#b(r).catch((e=>this.#t?.(F.LogType.debugError,e)))}if("object"===e.result.type)switch(e.result.subtype){case"generator":case"iterator":a.type=e.result.subtype,delete a.value}return a}serializeForBiDi(e,t){if(Object.hasOwn(e,"weakLocalObjectReference")){const a=e.weakLocalObjectReference;t.has(a)||t.set(a,(0,q.uuidv4)()),e.internalId=t.get(a),delete e.weakLocalObjectReference}if("platformobject"===e.type)return{type:"object"};const a=e.value;if(void 0===a)return e;if(["array","set","htmlcollection","nodelist"].includes(e.type))for(const e in a)a[e]=this.serializeForBiDi(a[e],t);if(["object","map"].includes(e.type))for(const e in a)a[e]=[this.serializeForBiDi(a[e][0],t),this.serializeForBiDi(a[e][1],t)];return e}get realmId(){return this.#w}get executionContextId(){return this.#S}get origin(){return this.#v}get source(){return{realm:this.realmId}}get cdpClient(){return this.#g}get baseInfo(){return{realm:this.realmId,origin:this.origin}}async evaluate(e,t,a,r,n=!1){const s=await this.cdpClient.sendCommand("Runtime.evaluate",{contextId:this.executionContextId,expression:e,awaitPromise:t,serializationOptions:$.#x("deep",r),userGesture:n});return s.exceptionDetails?await this.#P(s.exceptionDetails,0,a):{realm:this.realmId,result:this.cdpToBidiValue(s,a),type:"success"}}initialize(){for(const e of this.associatedBrowsingContexts)this.#y.registerEvent({type:"event",method:U.ChromiumBidi.Script.EventNames.RealmCreated,params:this.realmInfo},e.id)}async serializeCdpObject(e,t){const a=$.#I(e),r=await this.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((e=>e)),awaitPromise:!1,arguments:[a],serializationOptions:{serialization:"deep"},executionContextId:this.executionContextId});return this.cdpToBidiValue(r,t)}static#I(e){return void 0!==e.objectId?{objectId:e.objectId}:void 0!==e.unserializableValue?{unserializableValue:e.unserializableValue}:{value:e.value}}async stringifyObject(e){const{result:t}=await this.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((e=>String(e))),awaitPromise:!1,arguments:[e],returnByValue:!0,executionContextId:this.executionContextId});return t.value}async#k(e){const t=[];for(const[a,r]of e){let e;e="string"==typeof a?{value:a}:await this.deserializeForCdp(a);const n=await this.deserializeForCdp(r);t.push(e),t.push(n)}return t}async#R(e){return await Promise.all(e.map((e=>this.deserializeForCdp(e))))}async#E(e,t,a){const r=e.stackTrace?.callFrames.map((e=>({url:e.url,functionName:e.functionName,lineNumber:e.lineNumber-t,columnNumber:e.columnNumber})))??[],n=e.exception;return{exception:await this.serializeCdpObject(n,a),columnNumber:e.columnNumber,lineNumber:e.lineNumber-t,stackTrace:{callFrames:r},text:await this.stringifyObject(n)||e.text}}async callFunction(e,t,a,r,n,s,o=!1){const i=`(...args) => {\n      function callFunction(f, args) {\n        const deserializedThis = args.shift();\n        const deserializedArgs = args;\n        return f.apply(deserializedThis, deserializedArgs);\n      }\n      return callFunction((\n        ${e}\n      ), args);\n    }`,c=[await this.deserializeForCdp(t),...await Promise.all(a.map((async e=>await this.deserializeForCdp(e))))];let d;try{d=await this.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:i,awaitPromise:r,arguments:c,serializationOptions:$.#x("deep",s),executionContextId:this.executionContextId,userGesture:o})}catch(e){if(-32e3===e.code&&["Could not find object with given id","Argument should belong to the same JavaScript world as target object","Invalid remote object id"].includes(e.message))throw new U.NoSuchHandleException("Handle was not found.");throw e}return d.exceptionDetails?await this.#P(d.exceptionDetails,1,n):{type:"success",result:this.cdpToBidiValue(d,n),realm:this.realmId}}async deserializeForCdp(e){if("handle"in e&&e.handle)return{objectId:e.handle};if("handle"in e||"sharedId"in e)throw new U.NoSuchHandleException("Handle was not found.");switch(e.type){case"undefined":return{unserializableValue:"undefined"};case"null":return{unserializableValue:"null"};case"string":return{value:e.value};case"number":return"NaN"===e.value?{unserializableValue:"NaN"}:"-0"===e.value?{unserializableValue:"-0"}:"Infinity"===e.value?{unserializableValue:"Infinity"}:"-Infinity"===e.value?{unserializableValue:"-Infinity"}:{value:e.value};case"boolean":return{value:Boolean(e.value)};case"bigint":return{unserializableValue:`BigInt(${JSON.stringify(e.value)})`};case"date":return{unserializableValue:`new Date(Date.parse(${JSON.stringify(e.value)}))`};case"regexp":return{unserializableValue:`new RegExp(${JSON.stringify(e.value.pattern)}, ${JSON.stringify(e.value.flags)})`};case"map":{const t=await this.#k(e.value),{result:a}=await this.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String(((...e)=>{const t=new Map;for(let a=0;a<e.length;a+=2)t.set(e[a],e[a+1]);return t})),awaitPromise:!1,arguments:t,returnByValue:!1,executionContextId:this.executionContextId});return{objectId:a.objectId}}case"object":{const t=await this.#k(e.value),{result:a}=await this.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String(((...e)=>{const t={};for(let a=0;a<e.length;a+=2){t[e[a]]=e[a+1]}return t})),awaitPromise:!1,arguments:t,returnByValue:!1,executionContextId:this.executionContextId});return{objectId:a.objectId}}case"array":{const t=await this.#R(e.value),{result:a}=await this.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String(((...e)=>e)),awaitPromise:!1,arguments:t,returnByValue:!1,executionContextId:this.executionContextId});return{objectId:a.objectId}}case"set":{const t=await this.#R(e.value),{result:a}=await this.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String(((...e)=>new Set(e))),awaitPromise:!1,arguments:t,returnByValue:!1,executionContextId:this.executionContextId});return{objectId:a.objectId}}case"channel":{const t=new V.ChannelProxy(e.value,this.#t);return{objectId:await t.init(this,this.#y)}}}throw new Error(`Value ${JSON.stringify(e)} is not deserializable.`)}async#P(e,t,a){return{exceptionDetails:await this.#E(e,t,a),realm:this.realmId,type:"exception"}}static#x(e,t){return{serialization:e,additionalParameters:$.#_(t),...$.#T(t)}}static#_(e){const t={};return void 0!==e.maxDomDepth&&(t.maxNodeDepth=null===e.maxDomDepth?1e3:e.maxDomDepth),void 0!==e.includeShadowTree&&(t.includeShadowTree=e.includeShadowTree),t}static#T(e){return void 0===e.maxObjectDepth||null===e.maxObjectDepth?{}:{maxDepth:e.maxObjectDepth}}async#b(e){try{await this.cdpClient.sendCommand("Runtime.releaseObject",{objectId:e})}catch(e){if(-32e3!==e.code||"Invalid remote object id"!==e.message)throw e}}async disown(e){this.#C.knownHandlesToRealmMap.get(e)===this.realmId&&(await this.#b(e),this.#C.knownHandlesToRealmMap.delete(e))}dispose(){for(const e of this.associatedBrowsingContexts)this.#y.registerEvent({type:"event",method:U.ChromiumBidi.Script.EventNames.RealmDestroyed,params:{realm:this.realmId}},e.id)}}M.Realm=$,Object.defineProperty(O,"__esModule",{value:!0}),O.DedicatedWorkerRealm=void 0;const K=M;class H extends K.Realm{#N;constructor(e,t,a,r,n,s,o,i){super(e,t,a,r,n,o,i),this.#N=s,this.initialize()}get associatedBrowsingContexts(){return this.#N.associatedBrowsingContexts}get realmType(){return"dedicated-worker"}get source(){return{realm:this.realmId,context:this.associatedBrowsingContexts[0]?.id}}get realmInfo(){return{...this.baseInfo,type:this.realmType,owners:[this.#N.realmId]}}}O.DedicatedWorkerRealm=H;var W={},J={};Object.defineProperty(J,"__esModule",{value:!0}),J.assert=void 0,J.assert=function(e,t){if(!e)throw new Error(t??"Internal assertion failed.")};var G={};Object.defineProperty(G,"__esModule",{value:!0}),G.Deferred=void 0;class X{#j=!1;#O;#M;#A;get isFinished(){return this.#j}constructor(){this.#O=new Promise(((e,t)=>{this.#M=e,this.#A=t})),this.#O.catch((e=>{}))}then(e,t){return this.#O.then(e,t)}catch(e){return this.#O.catch(e)}resolve(e){this.#j||(this.#j=!0,this.#M(e))}reject(e){this.#j||(this.#j=!0,this.#A(e))}finally(e){return this.#O.finally(e)}[Symbol.toStringTag]="Promise"}G.Deferred=X;var Y={};Object.defineProperty(Y,"__esModule",{value:!0}),Y.inchesFromCm=void 0,Y.inchesFromCm=function(e){return e/2.54};var Q={},ee={};Object.defineProperty(ee,"__esModule",{value:!0}),ee.parseSharedId=ee.getSharedId=void 0;const te="_element_";ee.getSharedId=function(e,t,a,r){return r?`f.${e}.d.${t}.e.${a}`:`${t}${te}${a}`},ee.parseSharedId=function(e){const t=function(e){const t=e.match(new RegExp(`(.*)${te}(.*)`));if(!t)return null;const a=t[1],r=t[2];if(void 0===a||void 0===r)return null;const n=parseInt(r??"");return isNaN(n)?null:{documentId:a,backendNodeId:n}}(e);if(null!==t)return{...t,frameId:void 0};const a=e.match(/f\.(.*)\.d\.(.*)\.e\.([0-9]*)/);if(!a)return null;const r=a[1],n=a[2],s=a[3];if(void 0===r||void 0===n||void 0===s)return null;const o=parseInt(s??"");return isNaN(o)?null:{frameId:r,documentId:n,backendNodeId:o}},Object.defineProperty(Q,"__esModule",{value:!0}),Q.WindowRealm=void 0;const ae=g,re=M,ne=ee;class se extends re.Realm{#B;#i;#z;sandbox;constructor(e,t,a,r,n,s,o,i,c,d,u){super(a,r,n,s,o,i,c),this.#B=e,this.#i=t,this.#z=u,this.sandbox=d,this.initialize()}#D(e){const t=this.#i.getAllContexts().find((t=>t.navigableId===e));return t?.id??"UNKNOWN"}get browsingContext(){return this.#i.getContext(this.#B)}get associatedBrowsingContexts(){return[this.browsingContext]}get realmType(){return"window"}get realmInfo(){return{...this.baseInfo,type:this.realmType,context:this.#B,sandbox:this.sandbox}}get source(){return{realm:this.realmId,context:this.browsingContext.id}}serializeForBiDi(e,t){const a=e.value;if("node"===e.type){if(Object.hasOwn(a,"backendNodeId")){let t=this.browsingContext.navigableId??"UNKNOWN";Object.hasOwn(a,"loaderId")&&(t=a.loaderId,delete a.loaderId),e.sharedId=(0,ne.getSharedId)(this.#D(t),t,a.backendNodeId,this.#z),delete a.backendNodeId}if(Object.hasOwn(a,"children"))for(const e in a.children)a.children[e]=this.serializeForBiDi(a.children[e],t);Object.hasOwn(a,"shadowRoot")&&null!==a.shadowRoot&&(a.shadowRoot=this.serializeForBiDi(a.shadowRoot,t)),""===a.namespaceURI&&(a.namespaceURI=null)}return super.serializeForBiDi(e,t)}async deserializeForCdp(e){if("sharedId"in e&&e.sharedId){const t=(0,ne.parseSharedId)(e.sharedId);if(null===t)throw new ae.NoSuchNodeException(`SharedId "${e.sharedId}" was not found.`);const{documentId:a,backendNodeId:r}=t;if(this.browsingContext.navigableId!==a)throw new ae.NoSuchNodeException(`SharedId "${e.sharedId}" belongs to different document. Current document is ${this.browsingContext.navigableId}.`);try{const{object:e}=await this.cdpClient.sendCommand("DOM.resolveNode",{backendNodeId:r,executionContextId:this.executionContextId});return{objectId:e.objectId}}catch(t){if(-32e3===t.code&&"No node with given id found"===t.message)throw new ae.NoSuchNodeException(`SharedId "${e.sharedId}" was not found.`);throw new ae.UnknownErrorException(t.message,t.stack)}}return await super.deserializeForCdp(e)}async evaluate(e,t,a,r,n){return await this.#i.getContext(this.#B).targetUnblockedOrThrow(),await super.evaluate(e,t,a,r,n)}async callFunction(e,t,a,r,n,s,o){return await this.#i.getContext(this.#B).targetUnblockedOrThrow(),await super.callFunction(e,t,a,r,n,s,o)}}Q.WindowRealm=se,Object.defineProperty(W,"__esModule",{value:!0}),W.serializeOrigin=W.BrowsingContextImpl=void 0;const oe=g,ie=J,ce=G,de=l,ue=Y,le=Q;class he{static LOGGER_PREFIX=`${de.LogType.debug}:browsingContext`;#u;userContext;#L;#Z=new Set;#i;#U={DOMContentLoaded:new ce.Deferred,load:new ce.Deferred};#F={withinDocument:new ce.Deferred};#q="about:blank";#y;#C;#V;#$;#K;#z;#t;constructor(e,t,a,r,n,s,o,i,c){this.#$=e,this.#C=t,this.#u=a,this.#L=r,this.userContext=n,this.#y=s,this.#i=o,this.#z=i,this.#t=c}static create(e,t,a,r,n,s,o,i,c){const d=new he(e,t,a,r,n,s,o,i,c);return d.#H(),o.addContext(d),d.isTopLevelContext()||d.parent.addChild(d.id),s.registerEvent({type:"event",method:oe.ChromiumBidi.BrowsingContext.EventNames.ContextCreated,params:d.serializeToBidiValue()},d.id),d}static getTimestamp(){return(new Date).getTime()}get navigableId(){return this.#V}dispose(){this.#W(),this.#C.deleteRealms({browsingContextId:this.id}),this.isTopLevelContext()||this.parent.#Z.delete(this.id),this.#J(),this.#y.registerEvent({type:"event",method:oe.ChromiumBidi.BrowsingContext.EventNames.ContextDestroyed,params:this.serializeToBidiValue()},this.id),this.#i.deleteContextById(this.id)}get id(){return this.#u}get parentId(){return this.#L}get parent(){return null===this.parentId?null:this.#i.getContext(this.parentId)}get directChildren(){return[...this.#Z].map((e=>this.#i.getContext(e)))}get allChildren(){const e=this.directChildren;return e.concat(...e.map((e=>e.allChildren)))}isTopLevelContext(){return null===this.#L}get top(){let e=this,t=e.parent;for(;t;)e=t,t=e.parent;return e}addChild(e){this.#Z.add(e)}#W(){this.directChildren.map((e=>e.dispose()))}get#G(){return(0,ie.assert)(this.#K,`No default realm for browsing context ${this.#u}`),this.#K}get cdpTarget(){return this.#$}updateCdpTarget(e){this.#$=e,this.#H()}get url(){return this.#q}async lifecycleLoaded(){await this.#U.load}async targetUnblockedOrThrow(){const e=await this.#$.unblocked;if("error"===e.kind)throw e.error}async getOrCreateSandbox(e){if(void 0===e||""===e)return this.#G;let t=this.#C.findRealms({browsingContextId:this.id,sandbox:e});return 0===t.length&&(await this.#$.cdpClient.sendCommand("Page.createIsolatedWorld",{frameId:this.id,worldName:e}),t=this.#C.findRealms({browsingContextId:this.id,sandbox:e}),(0,ie.assert)(0!==t.length)),t[0]}serializeToBidiValue(e=0,t=!0){return{context:this.#u,url:this.url,userContext:this.userContext,children:e>0?this.directChildren.map((t=>t.serializeToBidiValue(e-1,!1))):null,...t?{parent:this.#L}:{}}}onTargetInfoChanged(e){this.#q=e.targetInfo.url}#H(){this.#$.cdpClient.on("Page.frameNavigated",(e=>{this.id===e.frame.id&&(this.#q=e.frame.url+(e.frame.urlFragment??""),this.#W())})),this.#$.cdpClient.on("Page.navigatedWithinDocument",(e=>{if(this.id!==e.frameId)return;const t=he.getTimestamp();this.#q=e.url,this.#F.withinDocument.resolve(e),this.#y.registerEvent({type:"event",method:oe.ChromiumBidi.BrowsingContext.EventNames.FragmentNavigated,params:{context:this.id,navigation:null,timestamp:t,url:this.#q}},this.id)})),this.#$.cdpClient.on("Page.frameStartedLoading",(e=>{this.id===e.frameId&&this.#y.registerEvent({type:"event",method:oe.ChromiumBidi.BrowsingContext.EventNames.NavigationStarted,params:{context:this.id,navigation:null,timestamp:he.getTimestamp(),url:""}},this.id)})),this.#$.cdpClient.on("Page.lifecycleEvent",(e=>{if(this.id!==e.frameId)return;if("init"===e.name)return void this.#X(e.loaderId);if("commit"===e.name)return void(this.#V=e.loaderId);if(e.loaderId!==this.#V)return;const t=he.getTimestamp();switch(e.name){case"DOMContentLoaded":this.#y.registerEvent({type:"event",method:oe.ChromiumBidi.BrowsingContext.EventNames.DomContentLoaded,params:{context:this.id,navigation:this.#V??null,timestamp:t,url:this.#q}},this.id),this.#U.DOMContentLoaded.resolve(e);break;case"load":this.#y.registerEvent({type:"event",method:oe.ChromiumBidi.BrowsingContext.EventNames.Load,params:{context:this.id,navigation:this.#V??null,timestamp:t,url:this.#q}},this.id),this.#U.load.resolve(e)}})),this.#$.cdpClient.on("Runtime.executionContextCreated",(e=>{const{auxData:t,name:a,uniqueId:r,id:n}=e.context;if(!t||t.frameId!==this.id)return;let s,o;switch(t.type){case"isolated":o=a,s=this.#G.origin;break;case"default":s=pe(e.context.origin);break;default:return}const i=new le.WindowRealm(this.id,this.#i,this.#$.cdpClient,this.#y,n,this.#t,s,r,this.#C,o,this.#z);t.isDefault&&(this.#K=i,Promise.all(this.#$.getChannels().map((e=>e.startListenerFromWindow(i,this.#y)))))})),this.#$.cdpClient.on("Runtime.executionContextDestroyed",(e=>{this.#C.deleteRealms({cdpSessionId:this.#$.cdpSessionId,executionContextId:e.executionContextId})})),this.#$.cdpClient.on("Runtime.executionContextsCleared",(()=>{this.#C.deleteRealms({cdpSessionId:this.#$.cdpSessionId})})),this.#$.cdpClient.on("Page.javascriptDialogClosed",(e=>{const t=e.result;this.#y.registerEvent({type:"event",method:oe.ChromiumBidi.BrowsingContext.EventNames.UserPromptClosed,params:{context:this.id,accepted:t,userText:t&&e.userInput?e.userInput:void 0}},this.id)})),this.#$.cdpClient.on("Page.javascriptDialogOpening",(e=>{this.#y.registerEvent({type:"event",method:oe.ChromiumBidi.BrowsingContext.EventNames.UserPromptOpened,params:{context:this.id,type:e.type,message:e.message,defaultValue:e.defaultPrompt||void 0}},this.id)}))}#X(e){void 0!==e&&this.#V!==e?(this.#Y(),this.#V=e):this.#F.withinDocument.isFinished?this.#F.withinDocument=new ce.Deferred:this.#t?.(he.LOGGER_PREFIX,"Document changed (navigatedWithinDocument)")}#Y(){this.#U.DOMContentLoaded.isFinished?this.#U.DOMContentLoaded=new ce.Deferred:this.#t?.(he.LOGGER_PREFIX,"Document changed (DOMContentLoaded)"),this.#U.load.isFinished?this.#U.load=new ce.Deferred:this.#t?.(he.LOGGER_PREFIX,"Document changed (load)")}#J(){this.#U.DOMContentLoaded.isFinished||this.#U.DOMContentLoaded.reject(new oe.UnknownErrorException("navigation canceled")),this.#U.load.isFinished||this.#U.load.reject(new oe.UnknownErrorException("navigation canceled"))}async navigate(e,t){try{new URL(e)}catch{throw new oe.InvalidArgumentException(`Invalid URL: ${e}`)}await this.targetUnblockedOrThrow();const a=await this.#$.cdpClient.sendCommand("Page.navigate",{url:e,frameId:this.id});if(a.errorText)throw new oe.UnknownErrorException(a.errorText);switch(this.#X(a.loaderId),t){case"none":break;case"interactive":void 0===a.loaderId?await this.#F.withinDocument:await this.#U.DOMContentLoaded;break;case"complete":void 0===a.loaderId?await this.#F.withinDocument:await this.#U.load}return{navigation:a.loaderId??null,url:"none"===t?e:this.#q}}async reload(e,t){switch(await this.targetUnblockedOrThrow(),await this.#$.cdpClient.sendCommand("Page.reload",{ignoreCache:e}),this.#Y(),t){case"none":break;case"interactive":await this.#U.DOMContentLoaded;break;case"complete":await this.#U.load}return{navigation:"none"===t?null:this.navigableId??null,url:this.url}}async setViewport(e,t){if(null===e&&null===t)await this.#$.cdpClient.sendCommand("Emulation.clearDeviceMetricsOverride");else try{await this.#$.cdpClient.sendCommand("Emulation.setDeviceMetricsOverride",{width:e?e.width:0,height:e?e.height:0,deviceScaleFactor:t||0,mobile:!1,dontSetVisibleSize:!0})}catch(e){if(e.message.startsWith("Width and height values must be positive"))throw new oe.UnsupportedOperationException("Provided viewport dimensions are not supported");throw e}}async handleUserPrompt(e){await this.#$.cdpClient.sendCommand("Page.handleJavaScriptDialog",{accept:e.accept??!0,promptText:e.userText})}async activate(){await this.#$.cdpClient.sendCommand("Page.bringToFront")}async captureScreenshot(e){if(!this.isTopLevelContext())throw new oe.UnsupportedOperationException(`Non-top-level 'context' (${e.context}) is currently not supported`);const t=function(e){const{quality:t,type:a}=e.format??{type:"image/png"};switch(a){case"image/png":return{format:"png"};case"image/jpeg":return{format:"jpeg",...void 0===t?{}:{quality:Math.round(100*t)}};case"image/webp":return{format:"webp",...void 0===t?{}:{quality:Math.round(100*t)}}}throw new oe.InvalidArgumentException(`Image format '${a}' is not a supported format`)}(e);await this.#$.cdpClient.sendCommand("Page.bringToFront");let a,r=!1;switch(e.origin??="viewport",e.origin){case"document":a=String((()=>{const e=document.documentElement;return{x:0,y:0,width:e.scrollWidth,height:e.scrollHeight}})),r=!0;break;case"viewport":a=String((()=>{const e=window.visualViewport;return{x:e.pageLeft,y:e.pageTop,width:e.width,height:e.height}}))}const n=await this.getOrCreateSandbox(void 0),s=await n.callFunction(a,{type:"undefined"},[],!1,"none",{},!1);(0,ie.assert)("success"===s.type);const o=me(s.result);(0,ie.assert)(o);const i=e.clip?function(e,t){e=fe(e),t=fe(t);const a=Math.max(e.x,t.x),r=Math.max(e.y,t.y);return{x:a,y:r,width:Math.max(Math.min(e.x+e.width,t.x+t.width)-a,0),height:Math.max(Math.min(e.y+e.height,t.y+t.height)-r,0)}}(await this.#Q(e.clip),o):o;if(0===i.width||0===i.height)throw new oe.UnableToCaptureScreenException(`Unable to capture screenshot with zero dimensions: width=${i.width}, height=${i.height}`);return await this.#$.cdpClient.sendCommand("Page.captureScreenshot",{clip:{...i,scale:1},...t,captureBeyondViewport:r})}async print(e){const t={};if(void 0!==e.background&&(t.printBackground=e.background),void 0!==e.margin?.bottom&&(t.marginBottom=(0,ue.inchesFromCm)(e.margin.bottom)),void 0!==e.margin?.left&&(t.marginLeft=(0,ue.inchesFromCm)(e.margin.left)),void 0!==e.margin?.right&&(t.marginRight=(0,ue.inchesFromCm)(e.margin.right)),void 0!==e.margin?.top&&(t.marginTop=(0,ue.inchesFromCm)(e.margin.top)),void 0!==e.orientation&&(t.landscape="landscape"===e.orientation),void 0!==e.page?.height&&(t.paperHeight=(0,ue.inchesFromCm)(e.page.height)),void 0!==e.page?.width&&(t.paperWidth=(0,ue.inchesFromCm)(e.page.width)),void 0!==e.pageRanges){for(const t of e.pageRanges){if("number"==typeof t)continue;const e=t.split("-");if(e.length<1||e.length>2)throw new oe.InvalidArgumentException(`Invalid page range: ${t} is not a valid integer range.`);if(1===e.length){ge(e[0]??"");continue}let a,r;const[n="",s=""]=e;if(a=""===n?1:ge(n),r=""===s?Number.MAX_SAFE_INTEGER:ge(s),a>r)throw new oe.InvalidArgumentException(`Invalid page range: ${n} > ${s}`)}t.pageRanges=e.pageRanges.join(",")}void 0!==e.scale&&(t.scale=e.scale),void 0!==e.shrinkToFit&&(t.preferCSSPageSize=!e.shrinkToFit);try{return{data:(await this.#$.cdpClient.sendCommand("Page.printToPDF",t)).data}}catch(e){if("invalid print parameters: content area is empty"===e.message)throw new oe.UnsupportedOperationException(e.message);throw e}}async#Q(e){switch(e.type){case"box":return{x:e.x,y:e.y,width:e.width,height:e.height};case"element":{const t=await this.getOrCreateSandbox(void 0),a=await t.callFunction(String((e=>e instanceof Element)),{type:"undefined"},[e.element],!1,"none",{});if("exception"===a.type)throw new oe.NoSuchElementException(`Element '${e.element.sharedId}' was not found`);if((0,ie.assert)("boolean"===a.result.type),!a.result.value)throw new oe.NoSuchElementException(`Node '${e.element.sharedId}' is not an Element`);{const a=await t.callFunction(String((e=>{const t=e.getBoundingClientRect();return{x:t.x,y:t.y,height:t.height,width:t.width}})),{type:"undefined"},[e.element],!1,"none",{});(0,ie.assert)("success"===a.type);const r=me(a.result);if(!r)throw new oe.UnableToCaptureScreenException(`Could not get bounding box for Element '${e.element.sharedId}'`);return r}}}}async close(){await this.#$.cdpClient.sendCommand("Page.close")}async traverseHistory(e){if(0===e)return;const t=await this.#$.cdpClient.sendCommand("Page.getNavigationHistory"),a=t.entries[t.currentIndex+e];if(!a)throw new oe.NoSuchHistoryEntryException(`No history entry at delta ${e}`);await this.#$.cdpClient.sendCommand("Page.navigateToHistoryEntry",{entryId:a.id})}}function pe(e){return["://",""].includes(e)&&(e="null"),e}function me(e){if("object"!==e.type||void 0===e.value)return;const t=e.value.find((([e])=>"x"===e))?.[1],a=e.value.find((([e])=>"y"===e))?.[1],r=e.value.find((([e])=>"height"===e))?.[1],n=e.value.find((([e])=>"width"===e))?.[1];return"number"===t?.type&&"number"===a?.type&&"number"===r?.type&&"number"===n?.type?{x:t.value,y:a.value,width:n.value,height:r.value}:void 0}function fe(e){return{...e.width<0?{x:e.x+e.width,width:-e.width}:{x:e.x,width:e.width},...e.height<0?{y:e.y+e.height,height:-e.height}:{y:e.y,height:e.height}}}function ge(e){if(e=e.trim(),!/^[0-9]+$/.test(e))throw new oe.InvalidArgumentException(`Invalid integer: ${e}`);return parseInt(e)}W.BrowsingContextImpl=he,W.serializeOrigin=pe;var ye={},Se={},ve={};Object.defineProperty(ve,"__esModule",{value:!0}),ve.getRemoteValuesText=ve.logMessageFormatter=void 0;const we=J,Ce=["%s","%d","%i","%f","%o","%O","%c"];function be(e){return Ce.some((t=>e.includes(t)))}function xe(e){let t="";const a=e[0].value.toString(),r=e.slice(1,void 0),n=a.split(new RegExp(Ce.map((e=>`(${e})`)).join("|"),"g"));for(const a of n)if(void 0!==a&&""!==a)if(be(a)){const n=r.shift();(0,we.assert)(n,`Less value is provided: "${ke(e,!1)}"`),"%s"===a?t+=Ie(n):"%d"===a||"%i"===a?"bigint"===n.type||"number"===n.type||"string"===n.type?t+=parseInt(n.value.toString(),10):t+="NaN":"%f"===a?"bigint"===n.type||"number"===n.type||"string"===n.type?t+=parseFloat(n.value.toString()):t+="NaN":t+=Pe(n)}else t+=a;if(r.length>0)throw new Error(`More value is provided: "${ke(e,!1)}"`);return t}function Pe(e){if("array"!==e.type&&"bigint"!==e.type&&"date"!==e.type&&"number"!==e.type&&"object"!==e.type&&"string"!==e.type)return Ie(e);if("bigint"===e.type)return`${e.value.toString()}n`;if("number"===e.type)return e.value.toString();if(["date","string"].includes(e.type))return JSON.stringify(e.value);if("object"===e.type)return`{${e.value.map((e=>`${JSON.stringify(e[0])}:${Pe(e[1])}`)).join(",")}}`;if("array"===e.type)return`[${e.value?.map((e=>Pe(e))).join(",")??""}]`;throw Error(`Invalid value type: ${e}`)}function Ie(e){if(!Object.hasOwn(e,"value"))return e.type;switch(e.type){case"string":case"number":case"boolean":case"bigint":return String(e.value);case"regexp":return`/${e.value.pattern}/${e.value.flags??""}`;case"date":return new Date(e.value).toString();case"object":return`Object(${e.value?.length??""})`;case"array":return`Array(${e.value?.length??""})`;case"map":return`Map(${e.value?.length})`;case"set":return`Set(${e.value?.length})`;default:return e.type}}function ke(e,t){const a=e[0];return a?"string"===a.type&&be(a.value.toString())&&t?xe(e):e.map((e=>Ie(e))).join(" "):""}ve.logMessageFormatter=xe,ve.getRemoteValuesText=ke,Object.defineProperty(Se,"__esModule",{value:!0}),Se.LogManager=void 0;const Re=g,Ee=l,_e=ve;function Te(e){const t=e?.callFrames.map((e=>({columnNumber:e.columnNumber,functionName:e.functionName,lineNumber:e.lineNumber,url:e.url})));return t?{callFrames:t}:void 0}class Ne{#y;#C;#$;#t;constructor(e,t,a,r){this.#$=e,this.#C=t,this.#y=a,this.#t=r}static create(e,t,a,r){const n=new Ne(e,t,a,r);return n.#ee(),n}#ee(){this.#$.cdpClient.on("Runtime.consoleAPICalled",(e=>{const t=this.#C.findRealm({cdpSessionId:this.#$.cdpSessionId,executionContextId:e.executionContextId});if(void 0===t)return void this.#t?.(Ee.LogType.cdp,e);const a=void 0===t?Promise.resolve(e.args):Promise.all(e.args.map((e=>t.serializeCdpObject(e,"none"))));for(const r of t.associatedBrowsingContexts)this.#y.registerPromiseEvent(a.then((a=>{return{kind:"success",value:{type:"event",method:Re.ChromiumBidi.Log.EventNames.LogEntryAdded,params:{level:(r=e.type,["error","assert"].includes(r)?"error":["debug","trace"].includes(r)?"debug":["warn","warning"].includes(r)?"warn":"info"),source:t.source,text:(0,_e.getRemoteValuesText)(a,!0),timestamp:Math.round(e.timestamp),stackTrace:Te(e.stackTrace),type:"console",method:"warning"===e.type?"warn":e.type,args:a}}};var r})),r.id,Re.ChromiumBidi.Log.EventNames.LogEntryAdded)})),this.#$.cdpClient.on("Runtime.exceptionThrown",(e=>{const t=this.#C.findRealm({cdpSessionId:this.#$.cdpSessionId,executionContextId:e.exceptionDetails.executionContextId});if(void 0!==t)for(const a of t.associatedBrowsingContexts)this.#y.registerPromiseEvent(Ne.#te(e,t).then((a=>({kind:"success",value:{type:"event",method:Re.ChromiumBidi.Log.EventNames.LogEntryAdded,params:{level:"error",source:t.source,text:a,timestamp:Math.round(e.timestamp),stackTrace:Te(e.exceptionDetails.stackTrace),type:"javascript"}}}))),a.id,Re.ChromiumBidi.Log.EventNames.LogEntryAdded);else this.#t?.(Ee.LogType.cdp,e)}))}static async#te(e,t){return e.exceptionDetails.exception?void 0===t?JSON.stringify(e.exceptionDetails.exception):await t.stringifyObject(e.exceptionDetails.exception):e.exceptionDetails.text}}Se.LogManager=Ne;var je={},Oe={},Me={};Object.defineProperty(Me,"__esModule",{value:!0}),Me.bidiToCdpCookie=Me.cdpToBiDiCookie=Me.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction=Me.cdpFetchHeadersFromBidiNetworkHeaders=Me.bidiNetworkHeadersFromCdpFetchHeaders=Me.cdpNetworkHeadersFromBidiNetworkHeaders=Me.bidiNetworkHeadersFromCdpNetworkHeaders=Me.computeHeadersSize=void 0;const Ae=I;function Be(e){switch(e){case"Strict":return"strict";case"None":return"none";default:return"lax"}}function ze(e){switch(e){case"strict":return"Strict";case"lax":return"Lax";case"none":return"None"}throw new Ae.InvalidArgumentException(`Unknown 'sameSite' value ${e}`)}Me.computeHeadersSize=function(e){const t=e.reduce(((e,t)=>`${e}${t.name}: ${t.value.value}\r\n`),"");return(new TextEncoder).encode(t).length},Me.bidiNetworkHeadersFromCdpNetworkHeaders=function(e){return e?Object.entries(e).map((([e,t])=>({name:e,value:{type:"string",value:t}}))):[]},Me.cdpNetworkHeadersFromBidiNetworkHeaders=function(e){if(void 0!==e)return e.reduce(((e,t)=>(e[t.name]=t.value.value,e)),{})},Me.bidiNetworkHeadersFromCdpFetchHeaders=function(e){return e?e.map((({name:e,value:t})=>({name:e,value:{type:"string",value:t}}))):[]},Me.cdpFetchHeadersFromBidiNetworkHeaders=function(e){if(void 0!==e)return e.map((({name:e,value:t})=>({name:e,value:t.value})))},Me.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction=function(e){switch(e){case"default":return"Default";case"cancel":return"CancelAuth";case"provideCredentials":return"ProvideCredentials"}},Me.cdpToBiDiCookie=function(e){const t={name:e.name,value:{type:"string",value:e.value},domain:e.domain,path:e.path,size:e.size,httpOnly:e.httpOnly,secure:e.secure,sameSite:void 0===e.sameSite?"none":Be(e.sameSite),...e.expires>=0?{expiry:e.expires}:void 0};return t["goog:session"]=e.session,t["goog:priority"]=e.priority,t["goog:sameParty"]=e.sameParty,t["goog:sourceScheme"]=e.sourceScheme,t["goog:sourcePort"]=e.sourcePort,void 0!==e.partitionKey&&(t["goog:partitionKey"]=e.partitionKey),void 0!==e.partitionKeyOpaque&&(t["goog:partitionKeyOpaque"]=e.partitionKeyOpaque),t},Me.bidiToCdpCookie=function(e,t){if("string"!==e.cookie.value.type)throw new Ae.UnsupportedOperationException("Only string cookie values are supported");const a=e.cookie.value.value,r={name:e.cookie.name,value:a,domain:e.cookie.domain,path:e.cookie.path??"/",secure:e.cookie.secure??!1,httpOnly:e.cookie.httpOnly??!1,...void 0!==t.sourceOrigin&&{partitionKey:t.sourceOrigin},...void 0!==e.cookie.expiry&&{expires:e.cookie.expiry},...void 0!==e.cookie.sameSite&&{sameSite:ze(e.cookie.sameSite)}};return void 0!==e.cookie["goog:url"]&&(r.url=e.cookie["goog:url"]),void 0!==e.cookie["goog:priority"]&&(r.priority=e.cookie["goog:priority"]),void 0!==e.cookie["goog:sameParty"]&&(r.sameParty=e.cookie["goog:sameParty"]),void 0!==e.cookie["goog:sourceScheme"]&&(r.sourceScheme=e.cookie["goog:sourceScheme"]),void 0!==e.cookie["goog:sourcePort"]&&(r.sourcePort=e.cookie["goog:sourcePort"]),r},Object.defineProperty(Oe,"__esModule",{value:!0}),Oe.NetworkRequest=void 0;const De=g,Le=J,Ze=G,Ue=Me;class Fe{static#ae="UNKNOWN";#re;#ne=void 0;#se=!1;#oe;#y;#ie;#ce={};#de={};#ue=new Ze.Deferred;#le=new Ze.Deferred;#he=new Ze.Deferred;#$;constructor(e,t,a,r,n=0){this.#re=e,this.#y=t,this.#ie=a,this.#$=r,this.#oe=n}get requestId(){return this.#re}get url(){return this.#de.info?.url??this.#ce.info?.request.url}get redirectCount(){return this.#oe}get cdpTarget(){return this.#$}isRedirecting(){return Boolean(this.#ce.info)}handleRedirect(e){this.#pe(),this.#me(),this.#de.hasExtraInfo=e.redirectHasExtraInfo,this.#de.info=e.redirectResponse,this.#fe(!0)}#fe(e=!1){const t=e||Boolean(this.#ce.extraInfo)||this.#se||Boolean(this.#de.info&&!this.#de.hasExtraInfo)||"beforeRequestSent"===this.#ne;this.#ce.info&&t&&this.#ue.resolve({kind:"success",value:void 0});const a=Boolean(this.#de.extraInfo)||this.#se||Boolean(this.#de.info&&!this.#de.hasExtraInfo)||"responseStarted"===this.#ne;this.#de.info&&a&&(this.#le.resolve({kind:"success",value:void 0}),this.#he.resolve({kind:"success",value:void 0}))}onRequestWillBeSentEvent(e){this.#ce.info=e,this.#ge(),this.#fe()}onRequestWillBeSentExtraInfoEvent(e){this.#ce.extraInfo=e,this.#fe()}onResponseReceivedExtraInfoEvent(e){this.#de.extraInfo=e,this.#fe()}onResponseReceivedEvent(e){this.#de.hasExtraInfo=e.hasExtraInfo,this.#de.info=e.response,this.#pe(),this.#me(),this.#fe()}onServedFromCache(){this.#se=!0,this.#fe()}onLoadingFailedEvent(e){this.#ue.resolve({kind:"success",value:void 0}),this.#le.resolve({kind:"error",error:new Error("Network event loading failed")}),this.#he.resolve({kind:"error",error:new Error("Network event loading failed")}),this.#y.registerEvent({type:"event",method:De.ChromiumBidi.Network.EventNames.FetchError,params:{...this.#ye(),errorText:e.errorText}},this.#Se)}onRequestPaused(e){if(this.#ve())return void this.continueRequest(e.requestId).catch((()=>{}));let t;t=void 0===e.responseErrorReason&&void 0===e.responseStatusCode?"beforeRequestSent":401===e.responseStatusCode&&"Unauthorized"===e.responseStatusText?"authRequired":"responseStarted";const a=(0,Ue.bidiNetworkHeadersFromCdpFetchHeaders)(e.responseHeaders);this.#ie.addBlockedRequest(this.requestId,{request:e.requestId,phase:t,response:{url:e.request.url,protocol:"",status:e.responseStatusCode??0,statusText:e.responseStatusText??"",fromCache:!1,headers:a,mimeType:"",bytesReceived:0,headersSize:(0,Ue.computeHeadersSize)(a),bodySize:0,content:{size:0},authChallenge:void 0}}),this.#ne=t,this.#fe()}async failRequest(e,t){await this.#$.cdpClient.sendCommand("Fetch.failRequest",{requestId:e,errorReason:t}),this.#ne=void 0}async continueRequest(e,t,a,r){await this.#$.cdpClient.sendCommand("Fetch.continueRequest",{requestId:e,url:t,method:a,headers:r}),this.#ne=void 0}async continueResponse(e,t,a,r){await this.#$.cdpClient.sendCommand("Fetch.continueResponse",{requestId:e,responseCode:t,responsePhrase:a,responseHeaders:r}),this.#ne=void 0}async continueWithAuth(e,t,a,r){await this.#$.cdpClient.sendCommand("Fetch.continueWithAuth",{requestId:e,authChallengeResponse:{response:t,username:a,password:r}}),this.#ne=void 0}async provideResponse(e,t,a,r,n){await this.#$.cdpClient.sendCommand("Fetch.fulfillRequest",{requestId:e,responseCode:t,responsePhrase:a,responseHeaders:r,...n?{body:btoa(n)}:{}}),this.#ne=void 0}dispose(){const e={kind:"error",error:new Error("Network processor detached")};this.#ue.resolve(e),this.#le.resolve(e),this.#he.resolve(e)}get#Se(){return this.#ce.info?.frameId??null}get statusCode(){return this.#de.info?.status??this.#de.extraInfo?.statusCode??-1}#ye(e){const t=void 0!==e&&e===this.#ne,a=this.#ie.getNetworkIntercepts(this.#re,e);return{isBlocked:t,context:this.#Se,navigation:this.#we(),redirectCount:this.#oe,request:this.#Ce(),timestamp:Math.round(1e3*(this.#ce.info?.wallTime??0)),intercepts:t?a:void 0}}#we(){return this.#ce.info&&this.#ce.info.loaderId&&this.#ce.info.loaderId===this.#ce.info.requestId?this.#ce.info.loaderId:null}#Ce(){const e=this.#ce.extraInfo?Fe.#be(this.#ce.extraInfo.associatedCookies):[],t=(0,Ue.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#ce.info?.request.headers);return{request:this.#ce.info?.requestId??Fe.#ae,url:this.#ce.info?.request.url??Fe.#ae,method:this.#ce.info?.request.method??Fe.#ae,headers:t,cookies:e,headersSize:(0,Ue.computeHeadersSize)(t),bodySize:0,timings:this.#xe()}}#xe(){return{timeOrigin:0,requestTime:0,redirectStart:0,redirectEnd:0,fetchStart:0,dnsStart:0,dnsEnd:0,connectStart:0,connectEnd:0,tlsStart:0,requestStart:0,responseStart:0,responseEnd:0}}#ge(){this.#ve()||this.#y.registerPromiseEvent(this.#ue.then((e=>{if("success"===e.kind)try{return{kind:"success",value:Object.assign(this.#Pe(),{type:"event"})}}catch(e){return{kind:"error",error:e instanceof Error?e:new Error("Unknown")}}return e})),this.#Se,De.ChromiumBidi.Network.EventNames.BeforeRequestSent)}#Pe(){return(0,Le.assert)(this.#ce.info,"RequestWillBeSentEvent is not set"),{method:De.ChromiumBidi.Network.EventNames.BeforeRequestSent,params:{...this.#ye("beforeRequestSent"),initiator:{type:Fe.#Ie(this.#ce.info.initiator.type)}}}}#pe(){this.#ve()||this.#y.registerPromiseEvent(this.#le.then((e=>{if("success"===e.kind)try{return{kind:"success",value:Object.assign(this.#ke(),{type:"event"})}}catch(e){return{kind:"error",error:e instanceof Error?e:new Error("Unknown")}}return e})),this.#Se,De.ChromiumBidi.Network.EventNames.ResponseStarted)}#ke(){(0,Le.assert)(this.#ce.info,"RequestWillBeSentEvent is not set"),(0,Le.assert)(this.#de.info,"ResponseReceivedEvent is not set"),this.#de.info.fromDiskCache&&(this.#de.extraInfo=void 0);const e=(0,Ue.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#de.info.headers);return{method:De.ChromiumBidi.Network.EventNames.ResponseStarted,params:{...this.#ye(),response:{url:this.#de.info.url??Fe.#ae,protocol:this.#de.info.protocol??"",status:this.statusCode,statusText:this.#de.info.statusText,fromCache:this.#de.info.fromDiskCache||this.#de.info.fromPrefetchCache||this.#se,headers:e,mimeType:this.#de.info.mimeType,bytesReceived:this.#de.info.encodedDataLength,headersSize:(0,Ue.computeHeadersSize)(e),bodySize:0,content:{size:0}}}}}#me(){this.#ve()||this.#y.registerPromiseEvent(this.#he.then((e=>{if("success"===e.kind)try{return{kind:"success",value:Object.assign(this.#Re(),{type:"event"})}}catch(e){return{kind:"error",error:e instanceof Error?e:new Error("Unknown")}}return e})),this.#Se,De.ChromiumBidi.Network.EventNames.ResponseCompleted)}#Re(){(0,Le.assert)(this.#ce.info,"RequestWillBeSentEvent is not set"),(0,Le.assert)(this.#de.info,"ResponseReceivedEvent is not set"),this.#de.info.fromDiskCache&&(this.#de.extraInfo=void 0);const e=(0,Ue.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#de.info.headers);return{method:De.ChromiumBidi.Network.EventNames.ResponseCompleted,params:{...this.#ye(),response:{url:this.#de.info.url??Fe.#ae,protocol:this.#de.info.protocol??"",status:this.statusCode,statusText:this.#de.info.statusText,fromCache:this.#de.info.fromDiskCache||this.#de.info.fromPrefetchCache||this.#se,headers:e,mimeType:this.#de.info.mimeType,bytesReceived:this.#de.info.encodedDataLength,headersSize:(0,Ue.computeHeadersSize)(e),bodySize:0,content:{size:0}}}}}#ve(){return this.#ce.info?.request.url.endsWith("/favicon.ico")??!1}static#Ie(e){switch(e){case"parser":case"script":case"preflight":return e;default:return"other"}}static#be(e){return e.filter((({blockedReasons:e})=>!Array.isArray(e)||0===e.length)).map((({cookie:e})=>(0,Ue.cdpToBiDiCookie)(e)))}}Oe.NetworkRequest=Fe,Object.defineProperty(je,"__esModule",{value:!0}),je.NetworkManager=void 0;const qe=Oe;class Ve{#$;#y;#ie;constructor(e,t,a){this.#$=e,this.#y=t,this.#ie=a}get cdpTarget(){return this.#$}#Ee(e,t){let a=this.#ie.getRequest(e);return a||(a=new qe.NetworkRequest(e,this.#y,this.#ie,this.#$,t),this.#ie.addRequest(a),a)}static create(e,t,a){const r=new Ve(e,t,a);return e.browserCdpClient.on("Target.detachedFromTarget",(t=>{e.cdpClient.sessionId===t.sessionId&&r.#ie.disposeRequestMap()})),e.cdpClient.on("Network.requestWillBeSent",(e=>{const t=r.#ie.getRequest(e.requestId);t&&t.isRedirecting()?(t.handleRedirect(e),r.#ie.deleteRequest(e.requestId),r.#Ee(e.requestId,t.redirectCount+1).onRequestWillBeSentEvent(e)):t?t.onRequestWillBeSentEvent(e):r.#Ee(e.requestId).onRequestWillBeSentEvent(e)})),e.cdpClient.on("Network.requestWillBeSentExtraInfo",(e=>{r.#Ee(e.requestId).onRequestWillBeSentExtraInfoEvent(e)})),e.cdpClient.on("Network.responseReceived",(e=>{r.#Ee(e.requestId).onResponseReceivedEvent(e)})),e.cdpClient.on("Network.responseReceivedExtraInfo",(e=>{r.#Ee(e.requestId).onResponseReceivedExtraInfoEvent(e)})),e.cdpClient.on("Network.requestServedFromCache",(e=>{r.#Ee(e.requestId).onServedFromCache()})),e.cdpClient.on("Network.loadingFailed",(e=>{r.#Ee(e.requestId).onLoadingFailedEvent(e)})),e.cdpClient.on("Fetch.requestPaused",(e=>{e.networkId&&r.#Ee(e.networkId).onRequestPaused(e)})),r}}je.NetworkManager=Ve,Object.defineProperty(ye,"__esModule",{value:!0}),ye.CdpTarget=void 0;const $e=G,Ke=Se,He=je;class We{#u;#g;#o;#y;#_e;#ie;#Te=new $e.Deferred;#Ne;static create(e,t,a,r,n,s,o,i,c){const d=new We(e,t,a,n,s,o,i);return Ke.LogManager.create(d,r,n,c),He.NetworkManager.create(d,n,o),d.#je(),d.#Oe(),d}constructor(e,t,a,r,n,s,o){this.#u=e,this.#g=t,this.#y=r,this.#_e=n,this.#ie=s,this.#o=a,this.#Ne=o}get unblocked(){return this.#Te}get id(){return this.#u}get cdpClient(){return this.#g}get browserCdpClient(){return this.#o}get cdpSessionId(){return this.#g.sessionId}async fetchEnable(){await this.#g.sendCommand("Fetch.enable",this.#ie.getFetchEnableParams())}async fetchDisable(){await this.#g.sendCommand("Fetch.disable")}async#Oe(){try{await Promise.all([this.#g.sendCommand("Runtime.enable"),this.#g.sendCommand("Page.enable"),this.#g.sendCommand("Page.setLifecycleEventsEnabled",{enabled:!0}),this.#g.sendCommand("Security.setIgnoreCertificateErrors",{ignore:this.#Ne}),this.#g.sendCommand("Network.enable"),this.fetchEnable(),this.#g.sendCommand("Target.setAutoAttach",{autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),this.#Me(),this.#g.sendCommand("Runtime.runIfWaitingForDebugger")])}catch(e){if(!this.#g.isCloseError(e))return void this.#Te.resolve({kind:"error",error:e})}this.#Te.resolve({kind:"success",value:void 0})}#je(){this.#g.on("*",((e,t)=>{"string"==typeof e&&this.#y.registerEvent({type:"event",method:`cdp.${e}`,params:{event:e,params:t,session:this.cdpSessionId}},null)}))}getChannels(){return this.#_e.find().flatMap((e=>e.channels))}async#Me(){for(const e of this.#_e.find({global:!0}))await e.initInTarget(this,!0)}}ye.CdpTarget=We,Object.defineProperty(j,"__esModule",{value:!0}),j.BrowsingContextProcessor=void 0;const Je=g,Ge=l,Xe=O,Ye=W,Qe=ye;j.BrowsingContextProcessor=class{#o;#c;#Ae;#y;#i;#ie;#Ne;#z;#_e;#C;#Be;#t;constructor(e,t,a,r,n,s,o,i,c,d,u,l){this.#Ne=c,this.#c=e,this.#o=t,this.#Ae=a,this.#y=r,this.#i=n,this.#_e=i,this.#ie=o,this.#C=s,this.#z=d,this.#Be=u,this.#t=l,this.#je(t)}getTree(e){return{contexts:(void 0===e.root?this.#i.getTopLevelContexts():[this.#i.getContext(e.root)]).map((t=>t.serializeToBidiValue(e.maxDepth??Number.MAX_VALUE)))}}async create(e){let t,a=e.userContext??"default";if(void 0!==e.referenceContext){if(t=this.#i.getContext(e.referenceContext),!t.isTopLevelContext())throw new Je.InvalidArgumentException("referenceContext should be a top-level context");a=t.userContext}let r,n=!1;switch(e.type){case"tab":n=!1;break;case"window":n=!0}if("default"!==a){const e=this.#i.getAllContexts().filter((e=>e.userContext===a));e.length||(n=!0)}try{r=await this.#o.sendCommand("Target.createTarget",{url:"about:blank",newWindow:n,browserContextId:"default"===a?void 0:a})}catch(e){if(e.message.startsWith("Failed to find browser context with id")||"browserContextId"===e.message)throw new Je.NoSuchUserContextException(`The context ${a} was not found`);throw e}const s=r.targetId,o=this.#i.getContext(s);return await o.lifecycleLoaded(),{context:o.id}}navigate(e){return this.#i.getContext(e.context).navigate(e.url,e.wait??"none")}reload(e){return this.#i.getContext(e.context).reload(e.ignoreCache??!1,e.wait??"none")}async activate(e){const t=this.#i.getContext(e.context);if(!t.isTopLevelContext())throw new Je.InvalidArgumentException("Activation is only supported on the top-level context");return await t.activate(),{}}async captureScreenshot(e){const t=this.#i.getContext(e.context);return await t.captureScreenshot(e)}async print(e){const t=this.#i.getContext(e.context);return await t.print(e)}async setViewport(e){const t=this.#i.getContext(e.context);if(!t.isTopLevelContext())throw new Je.InvalidArgumentException("Emulating viewport is only supported on the top-level context");return await t.setViewport(e.viewport,e.devicePixelRatio),{}}async traverseHistory(e){const t=this.#i.getContext(e.context);if(!t)throw new Je.InvalidArgumentException(`No browsing context with id ${e.context}`);return await t.traverseHistory(e.delta),{}}async handleUserPrompt(e){const t=this.#i.getContext(e.context);return await t.handleUserPrompt(e),{}}async close(e){const t=this.#i.getContext(e.context);if(!t.isTopLevelContext())throw new Je.InvalidArgumentException(`Non top-level browsing context ${t.id} cannot be closed.`);try{const a=new Promise((t=>{const a=r=>{r.targetId===e.context&&(this.#o.off("Target.detachedFromTarget",a),t())};this.#o.on("Target.detachedFromTarget",a)}));e.promptUnload?await t.close():await this.#o.sendCommand("Target.closeTarget",{targetId:e.context}),await a}catch(e){if(-32e3!==e.code||"Not attached to an active page"!==e.message)throw e}return{}}#je(e){e.on("Target.attachedToTarget",(t=>{this.#ze(t,e)})),e.on("Target.detachedFromTarget",(e=>{this.#De(e)})),e.on("Target.targetInfoChanged",(e=>{this.#Le(e)})),e.on("Page.frameAttached",(e=>{this.#Ze(e)})),e.on("Page.frameDetached",(e=>{this.#Ue(e)}))}#Ze(e){const t=this.#i.findContext(e.parentFrameId);void 0!==t&&Ye.BrowsingContextImpl.create(t.cdpTarget,this.#C,e.frameId,e.parentFrameId,t.userContext,this.#y,this.#i,this.#z,this.#t)}#Ue(e){"swap"!==e.reason&&this.#i.findContext(e.frameId)?.dispose()}#ze(e,t){const{sessionId:a,targetInfo:r}=e,n=this.#c.getCdpClient(a);switch(this.#t?.(Ge.LogType.debugInfo,"AttachedToTarget event received:",e),r.type){case"page":case"iframe":{if(r.targetId===this.#Ae)break;const e=this.#Fe(n,r),t=this.#i.findContext(r.targetId);return void(t?t.updateCdpTarget(e):Ye.BrowsingContextImpl.create(e,this.#C,r.targetId,null,r.browserContextId&&r.browserContextId!==this.#Be?r.browserContextId:"default",this.#y,this.#i,this.#z,this.#t))}case"worker":{const e=t.sessionId&&this.#i.findContextBySession(t.sessionId);if(!e)break;const a=this.#Fe(n,r);return void this.#qe(a,this.#C.getRealm({browsingContextId:e.id,type:"window",sandbox:void 0}))}}n.sendCommand("Runtime.runIfWaitingForDebugger").then((()=>t.sendCommand("Target.detachFromTarget",e))).catch((e=>this.#t?.(Ge.LogType.debugError,e)))}#Fe(e,t){return this.#je(e),Qe.CdpTarget.create(t.targetId,e,this.#o,this.#C,this.#y,this.#_e,this.#ie,this.#Ne,this.#t)}#Ve=new Map;#qe(e,t){e.cdpClient.on("Runtime.executionContextCreated",(a=>{const{uniqueId:r,id:n,origin:s}=a.context,o=new Xe.DedicatedWorkerRealm(e.cdpClient,this.#y,n,this.#t,(0,Ye.serializeOrigin)(s),t,r,this.#C);this.#Ve.set(e.cdpSessionId,o)}))}#De(e){const t=this.#i.findContextBySession(e.sessionId);if(t)return t.dispose(),void this.#_e.find({targetId:t.id}).map((e=>e.dispose(t.id)));const a=this.#Ve.get(e.sessionId);a&&this.#C.deleteRealms({cdpSessionId:a.cdpClient.sessionId})}#Le(e){const t=this.#i.findContext(e.targetInfo.targetId);t&&t.onTargetInfoChanged(e)}};var et={},tt={},at={};Object.defineProperty(at,"__esModule",{value:!0}),at.WheelSource=at.PointerSource=at.KeySource=at.NoneSource=void 0;at.NoneSource=class{type="none"};at.KeySource=class{type="key";pressed=new Set;#$e=0;get modifiers(){return this.#$e}get alt(){return 1==(1&this.#$e)}set alt(e){this.#Ke(e,1)}get ctrl(){return 2==(2&this.#$e)}set ctrl(e){this.#Ke(e,2)}get meta(){return 4==(4&this.#$e)}set meta(e){this.#Ke(e,4)}get shift(){return 8==(8&this.#$e)}set shift(e){this.#Ke(e,8)}#Ke(e,t){e?this.#$e|=t:this.#$e&=~t}};at.PointerSource=class{type="pointer";subtype;pointerId;pressed=new Set;x=0;y=0;constructor(e,t){this.pointerId=e,this.subtype=t}get buttons(){let e=0;for(const t of this.pressed)switch(t){case 0:e|=1;break;case 1:e|=4;break;case 2:e|=2;break;case 3:e|=8;break;case 4:e|=16}return e}static ClickContext=class e{static#He=500;static#We=2;count=0;#Je;#Ge;#Xe;constructor(e,t,a){this.#Je=e,this.#Ge=t,this.#Xe=a}compare(t){return t.#Xe-this.#Xe>e.#He||Math.abs(t.#Je-this.#Je)>e.#We||Math.abs(t.#Ge-this.#Ge)>e.#We}};#Ye=new Map;setClickCount(e,t){let a=this.#Ye.get(e);return a&&!a.compare(t)||(a=t),++a.count,this.#Ye.set(e,a),a.count}getClickCount(e){return this.#Ye.get(e)?.count??0}};at.WheelSource=class{type="wheel"};var rt={};Object.defineProperty(rt,"__esModule",{value:!0}),rt.getKeyLocation=rt.getKeyCode=rt.getNormalizedKey=void 0,rt.getNormalizedKey=function(e){switch(e){case"\ue000":return"Unidentified";case"\ue001":return"Cancel";case"\ue002":return"Help";case"\ue003":return"Backspace";case"\ue004":return"Tab";case"\ue005":return"Clear";case"\ue006":return"Return";case"\ue007":return"Enter";case"\ue008":case"\ue050":return"Shift";case"\ue009":case"\ue051":return"Control";case"\ue00a":case"\ue052":return"Alt";case"\ue00b":return"Pause";case"\ue00c":return"Escape";case"\ue00d":return" ";case"\ue00e":case"\ue054":return"PageUp";case"\ue00f":case"\ue055":return"PageDown";case"\ue010":case"\ue056":return"End";case"\ue011":case"\ue057":return"Home";case"\ue012":case"\ue058":return"ArrowLeft";case"\ue013":case"\ue059":return"ArrowUp";case"\ue014":case"\ue05a":return"ArrowRight";case"\ue015":case"\ue05b":return"ArrowDown";case"\ue016":case"\ue05c":return"Insert";case"\ue017":case"\ue05d":return"Delete";case"\ue018":return";";case"\ue019":return"=";case"\ue01a":return"0";case"\ue01b":return"1";case"\ue01c":return"2";case"\ue01d":return"3";case"\ue01e":return"4";case"\ue01f":return"5";case"\ue020":return"6";case"\ue021":return"7";case"\ue022":return"8";case"\ue023":return"9";case"\ue024":return"*";case"\ue025":return"+";case"\ue026":return",";case"\ue027":return"-";case"\ue028":return".";case"\ue029":return"/";case"\ue031":return"F1";case"\ue032":return"F2";case"\ue033":return"F3";case"\ue034":return"F4";case"\ue035":return"F5";case"\ue036":return"F6";case"\ue037":return"F7";case"\ue038":return"F8";case"\ue039":return"F9";case"\ue03a":return"F10";case"\ue03b":return"F11";case"\ue03c":return"F12";case"\ue03d":case"\ue053":return"Meta";case"\ue040":return"ZenkakuHankaku";default:return e}},rt.getKeyCode=function(e){switch(e){case"`":case"~":return"Backquote";case"\\":case"|":return"Backslash";case"\ue003":return"Backspace";case"[":case"{":return"BracketLeft";case"]":case"}":return"BracketRight";case",":case"<":return"Comma";case"0":case")":return"Digit0";case"1":case"!":return"Digit1";case"2":case"@":return"Digit2";case"3":case"#":return"Digit3";case"4":case"$":return"Digit4";case"5":case"%":return"Digit5";case"6":case"^":return"Digit6";case"7":case"&":return"Digit7";case"8":case"*":return"Digit8";case"9":case"(":return"Digit9";case"=":case"+":return"Equal";case"a":case"A":return"KeyA";case"b":case"B":return"KeyB";case"c":case"C":return"KeyC";case"d":case"D":return"KeyD";case"e":case"E":return"KeyE";case"f":case"F":return"KeyF";case"g":case"G":return"KeyG";case"h":case"H":return"KeyH";case"i":case"I":return"KeyI";case"j":case"J":return"KeyJ";case"k":case"K":return"KeyK";case"l":case"L":return"KeyL";case"m":case"M":return"KeyM";case"n":case"N":return"KeyN";case"o":case"O":return"KeyO";case"p":case"P":return"KeyP";case"q":case"Q":return"KeyQ";case"r":case"R":return"KeyR";case"s":case"S":return"KeyS";case"t":case"T":return"KeyT";case"u":case"U":return"KeyU";case"v":case"V":return"KeyV";case"w":case"W":return"KeyW";case"x":case"X":return"KeyX";case"y":case"Y":return"KeyY";case"z":case"Z":return"KeyZ";case"-":case"_":return"Minus";case".":return"Period";case"'":case'"':return"Quote";case";":case":":return"Semicolon";case"/":case"?":return"Slash";case"\ue00a":return"AltLeft";case"\ue052":return"AltRight";case"\ue009":return"ControlLeft";case"\ue051":return"ControlRight";case"\ue006":return"Enter";case"\ue03d":return"MetaLeft";case"\ue053":return"MetaRight";case"\ue008":return"ShiftLeft";case"\ue050":return"ShiftRight";case" ":case"\ue00d":return"Space";case"\ue004":return"Tab";case"\ue017":return"Delete";case"\ue010":return"End";case"\ue002":return"Help";case"\ue011":return"Home";case"\ue016":return"Insert";case"\ue00f":return"PageDown";case"\ue00e":return"PageUp";case"\ue015":return"ArrowDown";case"\ue012":return"ArrowLeft";case"\ue014":return"ArrowRight";case"\ue013":return"ArrowUp";case"\ue00c":return"Escape";case"\ue031":return"F1";case"\ue032":return"F2";case"\ue033":return"F3";case"\ue034":return"F4";case"\ue035":return"F5";case"\ue036":return"F6";case"\ue037":return"F7";case"\ue038":return"F8";case"\ue039":return"F9";case"\ue03a":return"F10";case"\ue03b":return"F11";case"\ue03c":return"F12";case"\ue01a":case"\ue05c":return"Numpad0";case"\ue01b":case"\ue056":return"Numpad1";case"\ue01c":case"\ue05b":return"Numpad2";case"\ue01d":case"\ue055":return"Numpad3";case"\ue01e":case"\ue058":return"Numpad4";case"\ue01f":return"Numpad5";case"\ue020":case"\ue05a":return"Numpad6";case"\ue021":case"\ue057":return"Numpad7";case"\ue022":case"\ue059":return"Numpad8";case"\ue023":case"\ue054":return"Numpad9";case"\ue025":return"NumpadAdd";case"\ue026":return"NumpadComma";case"\ue028":case"\ue05d":return"NumpadDecimal";case"\ue029":return"NumpadDivide";case"\ue007":return"NumpadEnter";case"\ue024":return"NumpadMultiply";case"\ue027":return"NumpadSubtract";default:return}},rt.getKeyLocation=function(e){switch(e){case"\ue007":case"\ue008":case"\ue009":case"\ue00a":case"\ue03d":return 1;case"\ue01a":case"\ue01b":case"\ue01c":case"\ue01d":case"\ue01e":case"\ue01f":case"\ue020":case"\ue021":case"\ue022":case"\ue023":case"\ue024":case"\ue025":case"\ue026":case"\ue027":case"\ue028":case"\ue029":case"\ue054":case"\ue055":case"\ue056":case"\ue057":case"\ue058":case"\ue059":case"\ue05a":case"\ue05b":case"\ue05c":case"\ue05d":return 3;case"\ue050":case"\ue051":case"\ue052":case"\ue053":return 2;default:return 0}};var nt={};Object.defineProperty(nt,"__esModule",{value:!0}),nt.KeyToKeyCode=void 0,nt.KeyToKeyCode={0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,Abort:3,Help:6,Backspace:8,Tab:9,Numpad5:12,NumpadEnter:13,Enter:13,"\\r":13,"\\n":13,ShiftLeft:16,ShiftRight:16,ControlLeft:17,ControlRight:17,AltLeft:18,AltRight:18,Pause:19,CapsLock:20,Escape:27,Convert:28,NonConvert:29,Space:32,Numpad9:33,PageUp:33,Numpad3:34,PageDown:34,End:35,Numpad1:35,Home:36,Numpad7:36,ArrowLeft:37,Numpad4:37,Numpad8:38,ArrowUp:38,ArrowRight:39,Numpad6:39,Numpad2:40,ArrowDown:40,Select:41,Open:43,PrintScreen:44,Insert:45,Numpad0:45,Delete:46,NumpadDecimal:46,Digit0:48,Digit1:49,Digit2:50,Digit3:51,Digit4:52,Digit5:53,Digit6:54,Digit7:55,Digit8:56,Digit9:57,KeyA:65,KeyB:66,KeyC:67,KeyD:68,KeyE:69,KeyF:70,KeyG:71,KeyH:72,KeyI:73,KeyJ:74,KeyK:75,KeyL:76,KeyM:77,KeyN:78,KeyO:79,KeyP:80,KeyQ:81,KeyR:82,KeyS:83,KeyT:84,KeyU:85,KeyV:86,KeyW:87,KeyX:88,KeyY:89,KeyZ:90,MetaLeft:91,MetaRight:92,ContextMenu:93,NumpadMultiply:106,NumpadAdd:107,NumpadSubtract:109,NumpadDivide:111,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,F13:124,F14:125,F15:126,F16:127,F17:128,F18:129,F19:130,F20:131,F21:132,F22:133,F23:134,F24:135,NumLock:144,ScrollLock:145,AudioVolumeMute:173,AudioVolumeDown:174,AudioVolumeUp:175,MediaTrackNext:176,MediaTrackPrevious:177,MediaStop:178,MediaPlayPause:179,Semicolon:186,Equal:187,NumpadEqual:187,Comma:188,Minus:189,Period:190,Slash:191,Backquote:192,BracketLeft:219,Backslash:220,BracketRight:221,Quote:222,AltGraph:225,Props:247,Cancel:3,Clear:12,Shift:16,Control:17,Alt:18,Accept:30,ModeChange:31," ":32,Print:42,Execute:43,"\\u0000":46,a:65,b:66,c:67,d:68,e:69,f:70,g:71,h:72,i:73,j:74,k:75,l:76,m:77,n:78,o:79,p:80,q:81,r:82,s:83,t:84,u:85,v:86,w:87,x:88,y:89,z:90,Meta:91,"*":106,"+":107,"-":109,"/":111,";":186,"=":187,",":188,".":190,"`":192,"[":219,"\\\\":220,"]":221,"'":222,Attn:246,CrSel:247,ExSel:248,EraseEof:249,Play:250,ZoomOut:251,")":48,"!":49,"@":50,"#":51,$:52,"%":53,"^":54,"&":55,"(":57,A:65,B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,":":186,"<":188,_:189,">":190,"?":191,"~":192,"{":219,"|":220,"}":221,'"':222,Camera:44,EndCall:95,VolumeDown:182,VolumeUp:183},Object.defineProperty(tt,"__esModule",{value:!0}),tt.ActionDispatcher=void 0;const st=g,ot=J,it=at,ct=rt,dt=nt,ut=(e=>{const t=e.getClientRects()[0],a=Math.max(0,Math.min(t.x,t.x+t.width)),r=Math.min(window.innerWidth,Math.max(t.x,t.x+t.width)),n=Math.max(0,Math.min(t.y,t.y+t.height));return[a+(r-a>>1),n+(Math.min(window.innerHeight,Math.max(t.y,t.y+t.height))-n>>1)]}).toString(),lt=(()=>navigator.platform.toLowerCase().includes("mac")).toString();tt.ActionDispatcher=class{static isMacOS=async e=>{const t=await(await e.getOrCreateSandbox(void 0)).callFunction(lt,{type:"undefined"},[],!1,"none",{});return(0,ot.assert)("exception"!==t.type),(0,ot.assert)("boolean"===t.result.type),t.result.value};#Qe=0;#et=0;#tt;#Se;#at;constructor(e,t,a){this.#tt=e,this.#Se=t,this.#at=a}async dispatchActions(e){await this.#tt.queue.run((async()=>{for(const t of e)await this.dispatchTickActions(t)}))}async dispatchTickActions(e){this.#Qe=performance.now(),this.#et=0;for(const{action:t}of e)"duration"in t&&void 0!==t.duration&&(this.#et=Math.max(this.#et,t.duration));const t=[new Promise((e=>setTimeout(e,this.#et)))];for(const a of e)t.push(this.#rt(a));await Promise.all(t)}async#rt({id:e,action:t}){const a=this.#tt.get(e),r=this.#tt.getGlobalKeyState();switch(t.type){case"keyDown":await this.#nt(a,t),this.#tt.cancelList.push({id:e,action:{...t,type:"keyUp"}});break;case"keyUp":await this.#st(a,t);break;case"pause":break;case"pointerDown":await this.#ot(a,r,t),this.#tt.cancelList.push({id:e,action:{...t,type:"pointerUp"}});break;case"pointerMove":await this.#it(a,r,t);break;case"pointerUp":await this.#ct(a,r,t);break;case"scroll":await this.#dt(a,r,t)}}#ot(e,t,a){const{button:r}=a;if(e.pressed.has(r))return;e.pressed.add(r);const{x:n,y:s,subtype:o}=e,{width:i,height:c,pressure:d,twist:u,tangentialPressure:l}=a,{tiltX:h,tiltY:p}=ft(a),{modifiers:m}=t;switch(o){case"mouse":case"pen":return this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchMouseEvent",{type:"mousePressed",x:n,y:s,modifiers:m,button:mt(r),buttons:e.buttons,clickCount:e.setClickCount(r,new it.PointerSource.ClickContext(n,s,performance.now())),pointerType:o,tangentialPressure:l,tiltX:h,tiltY:p,twist:u,force:d});case"touch":return this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchTouchEvent",{type:"touchStart",touchPoints:[{x:n,y:s,...gt(i??1,c??1),tangentialPressure:l,tiltX:h,tiltY:p,twist:u,force:d,id:e.pointerId}],modifiers:m})}}#ct(e,t,a){const{button:r}=a;if(!e.pressed.has(r))return;e.pressed.delete(r);const{x:n,y:s,subtype:o}=e,{modifiers:i}=t;switch(o){case"mouse":case"pen":return this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchMouseEvent",{type:"mouseReleased",x:n,y:s,modifiers:i,button:mt(r),buttons:e.buttons,clickCount:e.getClickCount(r),pointerType:o});case"touch":return this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchTouchEvent",{type:"touchEnd",touchPoints:[{x:n,y:s,id:e.pointerId}],modifiers:i})}}async#it(e,t,a){const{x:r,y:n,subtype:s}=e,{width:o,height:i,pressure:c,twist:d,tangentialPressure:u,x:l,y:h,origin:p="viewport",duration:m=this.#et}=a,{tiltX:f,tiltY:g}=ft(a),{targetX:y,targetY:S}=await this.#ut(p,l,h,r,n);if(y<0||S<0)throw new st.MoveTargetOutOfBoundsException(`Cannot move beyond viewport (x: ${y}, y: ${S})`);let v;do{const a=m>0?(performance.now()-this.#Qe)/m:1;let l,h;if(v=a>=1,v?(l=y,h=S):(l=Math.round(a*(y-r)+r),h=Math.round(a*(S-n)+n)),e.x!==l||e.y!==h){const{modifiers:a}=t;switch(s){case"mouse":await this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchMouseEvent",{type:"mouseMoved",x:l,y:h,modifiers:a,clickCount:0,button:mt(e.pressed.values().next().value??5),buttons:e.buttons,pointerType:s,tangentialPressure:u,tiltX:f,tiltY:g,twist:d,force:c});break;case"pen":0!==e.pressed.size&&await this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchMouseEvent",{type:"mouseMoved",x:l,y:h,modifiers:a,clickCount:0,button:mt(e.pressed.values().next().value??5),buttons:e.buttons,pointerType:s,tangentialPressure:u,tiltX:f,tiltY:g,twist:d,force:c});break;case"touch":0!==e.pressed.size&&await this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchTouchEvent",{type:"touchMove",touchPoints:[{x:l,y:h,...gt(o??1,i??1),tangentialPressure:u,tiltX:f,tiltY:g,twist:d,force:c,id:e.pointerId}],modifiers:a})}e.x=l,e.y=h}}while(!v)}async#ut(e,t,a,r,n){let s,o;switch(e){case"viewport":s=t,o=a;break;case"pointer":s=r+t,o=n+a;break;default:{const{x:r,y:n}=await async function(e,t){const a=await e.getOrCreateSandbox(void 0),r=await a.callFunction(ut,{type:"undefined"},[t],!1,"none",{});if("exception"===r.type)throw new st.NoSuchElementException(`Origin element ${t.sharedId} was not found`);(0,ot.assert)("array"===r.result.type),(0,ot.assert)("number"===r.result.value?.[0]?.type),(0,ot.assert)("number"===r.result.value?.[1]?.type);const{result:{value:[{value:n},{value:s}]}}=r;return{x:n,y:s}}(this.#Se,e.element);s=r+t,o=n+a;break}}return{targetX:s,targetY:o}}async#dt(e,t,a){const{deltaX:r,deltaY:n,x:s,y:o,origin:i="viewport",duration:c=this.#et}=a;if("pointer"===i)throw new st.InvalidArgumentException('"pointer" origin is invalid for scrolling.');const{targetX:d,targetY:u}=await this.#ut(i,s,o,0,0);if(d<0||u<0)throw new st.MoveTargetOutOfBoundsException(`Cannot move beyond viewport (x: ${d}, y: ${u})`);let l,h=0,p=0;do{const e=c>0?(performance.now()-this.#Qe)/c:1;let a,s;if(l=e>=1,l?(a=r-h,s=n-p):(a=Math.round(e*r-h),s=Math.round(e*n-p)),0!==a||0!==s){const{modifiers:e}=t;await this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchMouseEvent",{type:"mouseWheel",deltaX:a,deltaY:s,x:d,y:u,modifiers:e}),h+=a,p+=s}}while(!l)}async#nt(e,t){if([...t.value].length>1)throw new st.InvalidArgumentException(`Invalid key value: ${t.value}`);const a=t.value,r=(0,ct.getNormalizedKey)(a),n=e.pressed.has(r),s=(0,ct.getKeyCode)(a),o=(0,ct.getKeyLocation)(a);switch(r){case"Alt":e.alt=!0;break;case"Shift":e.shift=!0;break;case"Control":e.ctrl=!0;break;case"Meta":e.meta=!0}e.pressed.add(r);const{modifiers:i}=e,c=ht(r,e),d=pt(s??"",e)??c;let u;if(this.#at&&e.meta)switch(s){case"KeyA":u="SelectAll";break;case"KeyC":u="Copy";break;case"KeyV":u=e.shift?"PasteAndMatchStyle":"Paste";break;case"KeyX":u="Cut";break;case"KeyZ":u=e.shift?"Redo":"Undo"}const l=[this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchKeyEvent",{type:d?"keyDown":"rawKeyDown",windowsVirtualKeyCode:dt.KeyToKeyCode[r],key:r,code:s,text:d,unmodifiedText:c,autoRepeat:n,isSystemKey:e.alt||void 0,location:o<3?o:void 0,isKeypad:3===o,modifiers:i,commands:u?[u]:void 0})];"Escape"===r&&(e.alt||(!this.#at||e.ctrl||e.meta)&&this.#at||l.push(this.#Se.cdpTarget.cdpClient.sendCommand("Input.cancelDragging"))),await Promise.all(l)}#st(e,t){if([...t.value].length>1)throw new st.InvalidArgumentException(`Invalid key value: ${t.value}`);const a=t.value,r=(0,ct.getNormalizedKey)(a);if(!e.pressed.has(r))return;const n=(0,ct.getKeyCode)(a),s=(0,ct.getKeyLocation)(a);switch(r){case"Alt":e.alt=!1;break;case"Shift":e.shift=!1;break;case"Control":e.ctrl=!1;break;case"Meta":e.meta=!1}e.pressed.delete(r);const{modifiers:o}=e,i=ht(r,e),c=pt(n??"",e)??i;return this.#Se.cdpTarget.cdpClient.sendCommand("Input.dispatchKeyEvent",{type:"keyUp",windowsVirtualKeyCode:dt.KeyToKeyCode[r],key:r,code:n,text:c,unmodifiedText:i,location:s<3?s:void 0,isSystemKey:e.alt||void 0,isKeypad:3===s,modifiers:o})}};const ht=(e,t)=>"Enter"===e?"\r":1===[...e].length?t.shift?e.toLocaleUpperCase("en-US"):e:void 0,pt=(e,t)=>{if(t.ctrl){switch(e){case"Digit2":if(t.shift)return"\0";break;case"KeyA":return"\x01";case"KeyB":return"\x02";case"KeyC":return"\x03";case"KeyD":return"\x04";case"KeyE":return"\x05";case"KeyF":return"\x06";case"KeyG":return"\x07";case"KeyH":return"\b";case"KeyI":return"\t";case"KeyJ":return"\n";case"KeyK":return"\v";case"KeyL":return"\f";case"KeyM":return"\r";case"KeyN":return"\x0e";case"KeyO":return"\x0f";case"KeyP":return"\x10";case"KeyQ":return"\x11";case"KeyR":return"\x12";case"KeyS":return"\x13";case"KeyT":return"\x14";case"KeyU":return"\x15";case"KeyV":return"\x16";case"KeyW":return"\x17";case"KeyX":return"\x18";case"KeyY":return"\x19";case"KeyZ":return"\x1a";case"BracketLeft":return"\x1b";case"Backslash":return"\x1c";case"BracketRight":return"\x1d";case"Digit6":if(t.shift)return"\x1e";break;case"Minus":return"\x1f"}return""}if(t.alt)return""};function mt(e){switch(e){case 0:return"left";case 1:return"middle";case 2:return"right";case 3:return"back";case 4:return"forward";default:return"none"}}function ft(e){const t=e.altitudeAngle??0,a=e.azimuthAngle??0;let r=0,n=0;if(0===t&&(0!==a&&a!==2*Math.PI||(r=Math.PI/2),a===Math.PI/2&&(n=Math.PI/2),a===Math.PI&&(r=-Math.PI/2),a===3*Math.PI/2&&(n=-Math.PI/2),a>0&&a<Math.PI/2&&(r=Math.PI/2,n=Math.PI/2),a>Math.PI/2&&a<Math.PI&&(r=-Math.PI/2,n=Math.PI/2),a>Math.PI&&a<3*Math.PI/2&&(r=-Math.PI/2,n=-Math.PI/2),a>3*Math.PI/2&&a<2*Math.PI&&(r=Math.PI/2,n=-Math.PI/2)),0!==t){const e=Math.tan(t);r=Math.atan(Math.cos(a)/e),n=Math.atan(Math.sin(a)/e)}const s=180/Math.PI;return{tiltX:Math.round(r*s),tiltY:Math.round(n*s)}}function gt(e,t){return{radiusX:e?e/2:.5,radiusY:t?t/2:.5}}var yt={},St={},vt={};Object.defineProperty(vt,"__esModule",{value:!0}),vt.Mutex=void 0;vt.Mutex=class{#lt=!1;#ht=[];acquire(){const e={resolved:!1};return this.#lt?new Promise((t=>{this.#ht.push((()=>t(this.#pt.bind(this,e))))})):(this.#lt=!0,Promise.resolve(this.#pt.bind(this,e)))}#pt(e){if(e.resolved)throw new Error("Cannot release more than once.");e.resolved=!0;const t=this.#ht.shift();t?t():this.#lt=!1}async run(e){const t=await this.acquire();try{return await e()}finally{t()}}},Object.defineProperty(St,"__esModule",{value:!0}),St.InputState=void 0;const wt=g,Ct=vt,bt=at;St.InputState=class{cancelList=[];#mt=new Map;#ft=new Ct.Mutex;getOrCreate(e,t,a){let r=this.#mt.get(e);if(!r){switch(t){case"none":r=new bt.NoneSource;break;case"key":r=new bt.KeySource;break;case"pointer":{let e="mouse"===a?0:2;const t=new Set;for(const[,e]of this.#mt)"pointer"===e.type&&t.add(e.pointerId);for(;t.has(e);)++e;r=new bt.PointerSource(e,a);break}case"wheel":r=new bt.WheelSource;break;default:throw new wt.InvalidArgumentException(`Expected "none", "key", "pointer", or "wheel". Found unknown source type ${t}.`)}return this.#mt.set(e,r),r}if(r.type!==t)throw new wt.InvalidArgumentException(`Input source type of ${e} is ${r.type}, but received ${t}.`);return r}get(e){const t=this.#mt.get(e);if(!t)throw new wt.UnknownErrorException("Internal error.");return t}getGlobalKeyState(){const e=new bt.KeySource;for(const[,t]of this.#mt)if("key"===t.type){for(const a of t.pressed)e.pressed.add(a);e.alt||=t.alt,e.ctrl||=t.ctrl,e.meta||=t.meta,e.shift||=t.shift}return e}get queue(){return this.#ft}},Object.defineProperty(yt,"__esModule",{value:!0}),yt.InputStateManager=void 0;const xt=J,Pt=St;class It extends WeakMap{get(e){return(0,xt.assert)(e.isTopLevelContext()),this.has(e)||this.set(e,new Pt.InputState),super.get(e)}}yt.InputStateManager=It,Object.defineProperty(et,"__esModule",{value:!0}),et.InputProcessor=void 0;const kt=g,Rt=J,Et=tt,_t=yt;et.InputProcessor=class{#i;#C;#gt=new _t.InputStateManager;constructor(e,t){this.#i=e,this.#C=t}async performActions(e){const t=this.#i.getContext(e.context),a=this.#gt.get(t.top),r=this.#yt(e,a),n=new Et.ActionDispatcher(a,t,await Et.ActionDispatcher.isMacOS(t).catch((()=>!1)));return await n.dispatchActions(r),{}}async releaseActions(e){const t=this.#i.getContext(e.context),a=t.top,r=this.#gt.get(a),n=new Et.ActionDispatcher(r,t,await Et.ActionDispatcher.isMacOS(t).catch((()=>!1)));return await n.dispatchTickActions(r.cancelList.reverse()),this.#gt.delete(a),{}}async setFiles(e){const t=this.#C.findRealm({browsingContextId:e.context});if(void 0===t)throw new kt.NoSuchFrameException(`Could not find browsingContext ${e.context}`);let a;try{const r=await t.callFunction(String((function(){return this instanceof HTMLInputElement&&"file"===this.type&&!this.disabled})),e.element,[],!1,"none",{},!1);(0,Rt.assert)("success"===r.type),(0,Rt.assert)("boolean"===r.result.type),a=r.result.value}catch{throw new kt.NoSuchElementException(`Could not find element ${e.element.sharedId}`)}if(!a)throw new kt.UnableToSetFileInputException(`Element ${e.element.sharedId} is not a mutable file input.`);const r=[];for(let a=0;a<e.files.length;++a){const a=await t.callFunction(String((function(e){return this.files?this.files.item(e):null})),e.element,[{type:"number",value:0}],!1,"root",{},!1);if((0,Rt.assert)("success"===a.type),"object"!==a.result.type)break;const{handle:n}=a.result;(0,Rt.assert)(void 0!==n);const{path:s}=await t.cdpClient.sendCommand("DOM.getFileInfo",{objectId:n});r.push(s),t.disown(n).catch(void 0)}r.sort();const n=[...e.files].sort();if(r.length!==e.files.length||n.some(((e,t)=>r[t]!==e))){const{objectId:a}=await t.deserializeForCdp(e.element);(0,Rt.assert)(void 0!==a),await t.cdpClient.sendCommand("DOM.setFileInputFiles",{files:e.files,objectId:a})}else await t.callFunction(String((function(){this.dispatchEvent(new Event("cancel",{bubbles:!0}))})),e.element,[],!1,"none",{},!1);return{}}#yt(e,t){const a=[];for(const r of e.actions){switch(r.type){case"pointer":{r.parameters??={pointerType:"mouse"},r.parameters.pointerType??="mouse";const e=t.getOrCreate(r.id,"pointer",r.parameters.pointerType);if(e.subtype!==r.parameters.pointerType)throw new kt.InvalidArgumentException(`Expected input source ${r.id} to be ${e.subtype}; got ${r.parameters.pointerType}.`);break}default:t.getOrCreate(r.id,r.type)}const e=r.actions.map((e=>({id:r.id,action:e})));for(let t=0;t<e.length;t++)a.length===t&&a.push([]),a[t].push(e[t])}return a}};var Tt={},Nt={},jt={},Ot=Object.defineProperty,Mt=Object.getOwnPropertyDescriptor,At=Object.getOwnPropertyNames,Bt=Object.prototype.hasOwnProperty,zt={};((e,t)=>{for(var a in t)Ot(e,a,{get:t[a],enumerable:!0})})(zt,{URLPattern:()=>xa});var Dt,Lt=(Dt=zt,((e,t,a,r)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let n of At(t))!Bt.call(e,n)&&n!==a&&Ot(e,n,{get:()=>t[n],enumerable:!(r=Mt(t,n))||r.enumerable});return e})(Ot({},"__esModule",{value:!0}),Dt)),Zt=class{type=3;name="";prefix="";value="";suffix="";modifier=3;constructor(e,t,a,r,n,s){this.type=e,this.name=t,this.prefix=a,this.value=r,this.suffix=n,this.modifier=s}hasCustomName(){return""!==this.name&&"number"!=typeof this.name}},Ut=/[$_\p{ID_Start}]/u,Ft=/[$_\u200C\u200D\p{ID_Continue}]/u,qt=".*";function Vt(e,t){return(t?/^[\x00-\xFF]*$/:/^[\x00-\x7F]*$/).test(e)}function $t(e,t=!1){let a=[],r=0;for(;r<e.length;){let n=e[r],s=function(n){if(!t)throw new TypeError(n);a.push({type:"INVALID_CHAR",index:r,value:e[r++]})};if("*"!==n)if("+"!==n&&"?"!==n)if("\\"!==n)if("{"!==n)if("}"!==n)if(":"!==n)if("("!==n)a.push({type:"CHAR",index:r,value:e[r++]});else{let t=1,n="",o=r+1,i=!1;if("?"===e[o]){s(`Pattern cannot start with "?" at ${o}`);continue}for(;o<e.length;){if(!Vt(e[o],!1)){s(`Invalid character '${e[o]}' at ${o}.`),i=!0;break}if("\\"!==e[o]){if(")"===e[o]){if(t--,0===t){o++;break}}else if("("===e[o]&&(t++,"?"!==e[o+1])){s(`Capturing groups are not allowed at ${o}`),i=!0;break}n+=e[o++]}else n+=e[o++]+e[o++]}if(i)continue;if(t){s(`Unbalanced pattern at ${r}`);continue}if(!n){s(`Missing pattern at ${r}`);continue}a.push({type:"REGEX",index:r,value:n}),r=o}else{let t="",n=r+1;for(;n<e.length;){let a=e.substr(n,1);if(!(n===r+1&&Ut.test(a)||n!==r+1&&Ft.test(a)))break;t+=e[n++]}if(!t){s(`Missing parameter name at ${r}`);continue}a.push({type:"NAME",index:r,value:t}),r=n}else a.push({type:"CLOSE",index:r,value:e[r++]});else a.push({type:"OPEN",index:r,value:e[r++]});else a.push({type:"ESCAPED_CHAR",index:r++,value:e[r++]});else a.push({type:"OTHER_MODIFIER",index:r,value:e[r++]});else a.push({type:"ASTERISK",index:r,value:e[r++]})}return a.push({type:"END",index:r,value:""}),a}function Kt(e,t={}){let a=$t(e);t.delimiter??="/#?",t.prefixes??="./";let r=`[^${Ht(t.delimiter)}]+?`,n=[],s=0,o=0,i=new Set,c=e=>{if(o<a.length&&a[o].type===e)return a[o++].value},d=()=>c("OTHER_MODIFIER")??c("ASTERISK"),u=e=>{let t=c(e);if(void 0!==t)return t;let{type:r,index:n}=a[o];throw new TypeError(`Unexpected ${r} at ${n}, expected ${e}`)},l=()=>{let e,t="";for(;e=c("CHAR")??c("ESCAPED_CHAR");)t+=e;return t},h=t.encodePart||(e=>e),p="",m=e=>{p+=e},f=()=>{p.length&&(n.push(new Zt(3,"","",h(p),"",3)),p="")},g=(e,t,a,o,c)=>{let d,u=3;switch(c){case"?":u=1;break;case"*":u=0;break;case"+":u=2}if(!t&&!a&&3===u)return void m(e);if(f(),!t&&!a){if(!e)return;return void n.push(new Zt(3,"","",h(e),"",u))}d=a?"*"===a?qt:a:r;let l,p=2;if(d===r?(p=1,d=""):d===qt&&(p=0,d=""),t?l=t:a&&(l=s++),i.has(l))throw new TypeError(`Duplicate name '${l}'.`);i.add(l),n.push(new Zt(p,l,h(e),d,h(o),u))};for(;o<a.length;){let e=c("CHAR"),a=c("NAME"),r=c("REGEX");if(!a&&!r&&(r=c("ASTERISK")),a||r){let n=e??"";-1===t.prefixes.indexOf(n)&&(m(n),n=""),f(),g(n,a,r,"",d());continue}let n=e??c("ESCAPED_CHAR");if(n)m(n);else if(c("OPEN")){let e=l(),t=c("NAME"),a=c("REGEX");!t&&!a&&(a=c("ASTERISK"));let r=l();u("CLOSE"),g(e,t,a,r,d())}else f(),u("END")}return n}function Ht(e){return e.replace(/([.+*?^${}()[\]|/\\])/g,"\\$1")}function Wt(e){return e&&e.ignoreCase?"ui":"u"}function Jt(e){switch(e){case 0:return"*";case 1:return"?";case 2:return"+";case 3:return""}}function Gt(e,t,a={}){a.delimiter??="/#?",a.prefixes??="./",a.sensitive??=!1,a.strict??=!1,a.end??=!0,a.start??=!0,a.endsWith="";let r=a.start?"^":"";for(let n of e){if(3===n.type){3===n.modifier?r+=Ht(n.value):r+=`(?:${Ht(n.value)})${Jt(n.modifier)}`;continue}t&&t.push(n.name);let e=`[^${Ht(a.delimiter)}]+?`,s=n.value;(1===n.type?s=e:0===n.type&&(s=qt),n.prefix.length||n.suffix.length)?3!==n.modifier&&1!==n.modifier?(r+=`(?:${Ht(n.prefix)}`,r+=`((?:${s})(?:`,r+=Ht(n.suffix),r+=Ht(n.prefix),r+=`(?:${s}))*)${Ht(n.suffix)})`,0===n.modifier&&(r+="?")):(r+=`(?:${Ht(n.prefix)}(${s})${Ht(n.suffix)})`,r+=Jt(n.modifier)):3===n.modifier||1===n.modifier?r+=`(${s})${Jt(n.modifier)}`:r+=`((?:${s})${Jt(n.modifier)})`}let n=`[${Ht(a.endsWith)}]|$`,s=`[${Ht(a.delimiter)}]`;if(a.end)return a.strict||(r+=`${s}?`),a.endsWith.length?r+=`(?=${n})`:r+="$",new RegExp(r,Wt(a));a.strict||(r+=`(?:${s}(?=${n}))?`);let o=!1;if(e.length){let t=e[e.length-1];3===t.type&&3===t.modifier&&(o=a.delimiter.indexOf(t)>-1)}return o||(r+=`(?=${s}|${n})`),new RegExp(r,Wt(a))}var Xt={delimiter:"",prefixes:"",sensitive:!0,strict:!0},Yt={delimiter:".",prefixes:"",sensitive:!0,strict:!0},Qt={delimiter:"/",prefixes:"/",sensitive:!0,strict:!0};function ea(e,t){return e.startsWith(t)?e.substring(t.length,e.length):e}function ta(e){return!(!e||e.length<2)&&("["===e[0]||("\\"===e[0]||"{"===e[0])&&"["===e[1])}var aa=["ftp","file","http","https","ws","wss"];function ra(e){if(!e)return!0;for(let t of aa)if(e.test(t))return!0;return!1}function na(e){switch(e){case"ws":case"http":return"80";case"wws":case"https":return"443";case"ftp":return"21";default:return""}}function sa(e){if(""===e)return e;if(/^[-+.A-Za-z0-9]*$/.test(e))return e.toLowerCase();throw new TypeError(`Invalid protocol '${e}'.`)}function oa(e){if(""===e)return e;let t=new URL("https://example.com");return t.username=e,t.username}function ia(e){if(""===e)return e;let t=new URL("https://example.com");return t.password=e,t.password}function ca(e){if(""===e)return e;if(/[\t\n\r #%/:<>?@[\]^\\|]/g.test(e))throw new TypeError(`Invalid hostname '${e}'`);let t=new URL("https://example.com");return t.hostname=e,t.hostname}function da(e){if(""===e)return e;if(/[^0-9a-fA-F[\]:]/g.test(e))throw new TypeError(`Invalid IPv6 hostname '${e}'`);return e.toLowerCase()}function ua(e){if(""===e||/^[0-9]*$/.test(e)&&parseInt(e)<=65535)return e;throw new TypeError(`Invalid port '${e}'.`)}function la(e){if(""===e)return e;let t=new URL("https://example.com");return t.pathname="/"!==e[0]?"/-"+e:e,"/"!==e[0]?t.pathname.substring(2,t.pathname.length):t.pathname}function ha(e){return""===e?e:new URL(`data:${e}`).pathname}function pa(e){if(""===e)return e;let t=new URL("https://example.com");return t.search=e,t.search.substring(1,t.search.length)}function ma(e){if(""===e)return e;let t=new URL("https://example.com");return t.hash=e,t.hash.substring(1,t.hash.length)}var fa=class{#St;#vt=[];#wt={};#Ct=0;#bt=1;#xt=0;#Pt=0;#It=0;#kt=0;#Rt=!1;constructor(e){this.#St=e}get result(){return this.#wt}parse(){for(this.#vt=$t(this.#St,!0);this.#Ct<this.#vt.length;this.#Ct+=this.#bt){if(this.#bt=1,"END"===this.#vt[this.#Ct].type){if(0===this.#Pt){this.#Et(),this.#_t()?this.#Tt(9,1):this.#Nt()?this.#Tt(8,1):this.#Tt(7,0);continue}if(2===this.#Pt){this.#jt(5);continue}this.#Tt(10,0);break}if(this.#It>0){if(!this.#Ot())continue;this.#It-=1}if(this.#Mt())this.#It+=1;else switch(this.#Pt){case 0:this.#At()&&this.#jt(1);break;case 1:if(this.#At()){this.#Bt();let e=7,t=1;this.#zt()?(e=2,t=3):this.#Rt&&(e=2),this.#Tt(e,t)}break;case 2:this.#Dt()?this.#jt(3):(this.#Je()||this.#Nt()||this.#_t())&&this.#jt(5);break;case 3:this.#Lt()?this.#Tt(4,1):this.#Dt()&&this.#Tt(5,1);break;case 4:this.#Dt()&&this.#Tt(5,1);break;case 5:this.#Ge()?this.#kt+=1:this.#Zt()&&(this.#kt-=1),this.#Ut()&&!this.#kt?this.#Tt(6,1):this.#Je()?this.#Tt(7,0):this.#Nt()?this.#Tt(8,1):this.#_t()&&this.#Tt(9,1);break;case 6:this.#Je()?this.#Tt(7,0):this.#Nt()?this.#Tt(8,1):this.#_t()&&this.#Tt(9,1);break;case 7:this.#Nt()?this.#Tt(8,1):this.#_t()&&this.#Tt(9,1);break;case 8:this.#_t()&&this.#Tt(9,1)}}void 0!==this.#wt.hostname&&void 0===this.#wt.port&&(this.#wt.port="")}#Tt(e,t){switch(this.#Pt){case 0:case 2:break;case 1:this.#wt.protocol=this.#Ft();break;case 3:this.#wt.username=this.#Ft();break;case 4:this.#wt.password=this.#Ft();break;case 5:this.#wt.hostname=this.#Ft();break;case 6:this.#wt.port=this.#Ft();break;case 7:this.#wt.pathname=this.#Ft();break;case 8:this.#wt.search=this.#Ft();break;case 9:this.#wt.hash=this.#Ft()}0!==this.#Pt&&10!==e&&([1,2,3,4].includes(this.#Pt)&&[6,7,8,9].includes(e)&&(this.#wt.hostname??=""),[1,2,3,4,5,6].includes(this.#Pt)&&[8,9].includes(e)&&(this.#wt.pathname??=this.#Rt?"/":""),[1,2,3,4,5,6,7].includes(this.#Pt)&&9===e&&(this.#wt.search??="")),this.#qt(e,t)}#qt(e,t){this.#Pt=e,this.#xt=this.#Ct+t,this.#Ct+=t,this.#bt=0}#Et(){this.#Ct=this.#xt,this.#bt=0}#jt(e){this.#Et(),this.#Pt=e}#Vt(e){return e<0&&(e=this.#vt.length-e),e<this.#vt.length?this.#vt[e]:this.#vt[this.#vt.length-1]}#$t(e,t){let a=this.#Vt(e);return a.value===t&&("CHAR"===a.type||"ESCAPED_CHAR"===a.type||"INVALID_CHAR"===a.type)}#At(){return this.#$t(this.#Ct,":")}#zt(){return this.#$t(this.#Ct+1,"/")&&this.#$t(this.#Ct+2,"/")}#Dt(){return this.#$t(this.#Ct,"@")}#Lt(){return this.#$t(this.#Ct,":")}#Ut(){return this.#$t(this.#Ct,":")}#Je(){return this.#$t(this.#Ct,"/")}#Nt(){if(this.#$t(this.#Ct,"?"))return!0;if("?"!==this.#vt[this.#Ct].value)return!1;let e=this.#Vt(this.#Ct-1);return"NAME"!==e.type&&"REGEX"!==e.type&&"CLOSE"!==e.type&&"ASTERISK"!==e.type}#_t(){return this.#$t(this.#Ct,"#")}#Mt(){return"OPEN"==this.#vt[this.#Ct].type}#Ot(){return"CLOSE"==this.#vt[this.#Ct].type}#Ge(){return this.#$t(this.#Ct,"[")}#Zt(){return this.#$t(this.#Ct,"]")}#Ft(){let e=this.#vt[this.#Ct],t=this.#Vt(this.#xt).index;return this.#St.substring(t,e.index)}#Bt(){let e={};Object.assign(e,Xt),e.encodePart=sa;let t=function(e,t,a){return Gt(Kt(e,a),t,a)}(this.#Ft(),void 0,e);this.#Rt=ra(t)}},ga=["protocol","username","password","hostname","port","pathname","search","hash"],ya="*";function Sa(e,t){if("string"!=typeof e)throw new TypeError("parameter 1 is not of type 'string'.");let a=new URL(e,t);return{protocol:a.protocol.substring(0,a.protocol.length-1),username:a.username,password:a.password,hostname:a.hostname,port:a.port,pathname:a.pathname,search:""!==a.search?a.search.substring(1,a.search.length):void 0,hash:""!==a.hash?a.hash.substring(1,a.hash.length):void 0}}function va(e,t){return t?Ca(e):e}function wa(e,t,a){let r;if("string"==typeof t.baseURL)try{r=new URL(t.baseURL),void 0===t.protocol&&(e.protocol=va(r.protocol.substring(0,r.protocol.length-1),a)),!a&&void 0===t.protocol&&void 0===t.hostname&&void 0===t.port&&void 0===t.username&&(e.username=va(r.username,a)),!a&&void 0===t.protocol&&void 0===t.hostname&&void 0===t.port&&void 0===t.username&&void 0===t.password&&(e.password=va(r.password,a)),void 0===t.protocol&&void 0===t.hostname&&(e.hostname=va(r.hostname,a)),void 0===t.protocol&&void 0===t.hostname&&void 0===t.port&&(e.port=va(r.port,a)),void 0===t.protocol&&void 0===t.hostname&&void 0===t.port&&void 0===t.pathname&&(e.pathname=va(r.pathname,a)),void 0===t.protocol&&void 0===t.hostname&&void 0===t.port&&void 0===t.pathname&&void 0===t.search&&(e.search=va(r.search.substring(1,r.search.length),a)),void 0===t.protocol&&void 0===t.hostname&&void 0===t.port&&void 0===t.pathname&&void 0===t.search&&void 0===t.hash&&(e.hash=va(r.hash.substring(1,r.hash.length),a))}catch{throw new TypeError(`invalid baseURL '${t.baseURL}'.`)}if("string"==typeof t.protocol&&(e.protocol=function(e,t){return e=function(e,t){return e.endsWith(t)?e.substr(0,e.length-t.length):e}(e,":"),t||""===e?e:sa(e)}(t.protocol,a)),"string"==typeof t.username&&(e.username=function(e,t){if(t||""===e)return e;let a=new URL("https://example.com");return a.username=e,a.username}(t.username,a)),"string"==typeof t.password&&(e.password=function(e,t){if(t||""===e)return e;let a=new URL("https://example.com");return a.password=e,a.password}(t.password,a)),"string"==typeof t.hostname&&(e.hostname=function(e,t){return t||""===e?e:ta(e)?da(e):ca(e)}(t.hostname,a)),"string"==typeof t.port&&(e.port=function(e,t,a){return na(t)===e&&(e=""),a||""===e?e:ua(e)}(t.port,e.protocol,a)),"string"==typeof t.pathname){if(e.pathname=t.pathname,r&&!function(e,t){return!(!e.length||"/"!==e[0]&&(!t||e.length<2||"\\"!=e[0]&&"{"!=e[0]||"/"!=e[1]))}(e.pathname,a)){let t=r.pathname.lastIndexOf("/");t>=0&&(e.pathname=va(r.pathname.substring(0,t+1),a)+e.pathname)}e.pathname=function(e,t,a){if(a||""===e)return e;if(t&&!aa.includes(t))return new URL(`${t}:${e}`).pathname;let r="/"==e[0];return e=new URL(r?e:"/-"+e,"https://example.com").pathname,r||(e=e.substring(2,e.length)),e}(e.pathname,e.protocol,a)}return"string"==typeof t.search&&(e.search=function(e,t){if(e=ea(e,"?"),t||""===e)return e;let a=new URL("https://example.com");return a.search=e,a.search?a.search.substring(1,a.search.length):""}(t.search,a)),"string"==typeof t.hash&&(e.hash=function(e,t){if(e=ea(e,"#"),t||""===e)return e;let a=new URL("https://example.com");return a.hash=e,a.hash?a.hash.substring(1,a.hash.length):""}(t.hash,a)),e}function Ca(e){return e.replace(/([+*?:{}()\\])/g,"\\$1")}function ba(e,t){t.delimiter??="/#?",t.prefixes??="./",t.sensitive??=!1,t.strict??=!1,t.end??=!0,t.start??=!0,t.endsWith="";let a=`[^${function(e){return e.replace(/([.+*?^${}()[\]|/\\])/g,"\\$1")}(t.delimiter)}]+?`,r=/[$_\u200C\u200D\p{ID_Continue}]/u,n="";for(let s=0;s<e.length;++s){let o=e[s];if(3===o.type){if(3===o.modifier){n+=Ca(o.value);continue}n+=`{${Ca(o.value)}}${Jt(o.modifier)}`;continue}let i=o.hasCustomName(),c=!!o.suffix.length||!!o.prefix.length&&(1!==o.prefix.length||!t.prefixes.includes(o.prefix)),d=s>0?e[s-1]:null,u=s<e.length-1?e[s+1]:null;if(!c&&i&&1===o.type&&3===o.modifier&&u&&!u.prefix.length&&!u.suffix.length)if(3===u.type){let e=u.value.length>0?u.value[0]:"";c=r.test(e)}else c=!u.hasCustomName();if(!c&&!o.prefix.length&&d&&3===d.type){let e=d.value[d.value.length-1];c=t.prefixes.includes(e)}c&&(n+="{"),n+=Ca(o.prefix),i&&(n+=`:${o.name}`),2===o.type?n+=`(${o.value})`:1===o.type?i||(n+=`(${a})`):0===o.type&&(i||d&&3!==d.type&&3===d.modifier&&!c&&""===o.prefix?n+="(.*)":n+="*"),1===o.type&&i&&o.suffix.length&&r.test(o.suffix[0])&&(n+="\\"),n+=Ca(o.suffix),c&&(n+="}"),3!==o.modifier&&(n+=Jt(o.modifier))}return n}var xa=class{#St;#vt={};#wt={};#Ct={};#bt={};#xt=!1;constructor(e={},t,a){try{let r;if("string"==typeof t?r=t:a=t,"string"==typeof e){let t=new fa(e);if(t.parse(),e=t.result,void 0===r&&"string"!=typeof e.protocol)throw new TypeError("A base URL must be provided for a relative constructor string.");e.baseURL=r}else{if(!e||"object"!=typeof e)throw new TypeError("parameter 1 is not of type 'string' and cannot convert to dictionary.");if(r)throw new TypeError("parameter 1 is not of type 'string'.")}typeof a>"u"&&(a={ignoreCase:!1});let n,s={ignoreCase:!0===a.ignoreCase},o={pathname:ya,protocol:ya,username:ya,password:ya,hostname:ya,port:ya,search:ya,hash:ya};for(n of(this.#St=wa(o,e,!0),na(this.#St.protocol)===this.#St.port&&(this.#St.port=""),ga)){if(!(n in this.#St))continue;let e={},t=this.#St[n];switch(this.#wt[n]=[],n){case"protocol":Object.assign(e,Xt),e.encodePart=sa;break;case"username":Object.assign(e,Xt),e.encodePart=oa;break;case"password":Object.assign(e,Xt),e.encodePart=ia;break;case"hostname":Object.assign(e,Yt),ta(t)?e.encodePart=da:e.encodePart=ca;break;case"port":Object.assign(e,Xt),e.encodePart=ua;break;case"pathname":ra(this.#vt.protocol)?(Object.assign(e,Qt,s),e.encodePart=la):(Object.assign(e,Xt,s),e.encodePart=ha);break;case"search":Object.assign(e,Xt,s),e.encodePart=pa;break;case"hash":Object.assign(e,Xt,s),e.encodePart=ma}try{this.#bt[n]=Kt(t,e),this.#vt[n]=Gt(this.#bt[n],this.#wt[n],e),this.#Ct[n]=ba(this.#bt[n],e),this.#xt=this.#xt||this.#bt[n].some((e=>2===e.type))}catch{throw new TypeError(`invalid ${n} pattern '${this.#St[n]}'.`)}}}catch(e){throw new TypeError(`Failed to construct 'URLPattern': ${e.message}`)}}test(e={},t){let a,r={pathname:"",protocol:"",username:"",password:"",hostname:"",port:"",search:"",hash:""};if("string"!=typeof e&&t)throw new TypeError("parameter 1 is not of type 'string'.");if(typeof e>"u")return!1;try{r=wa(r,"object"==typeof e?e:Sa(e,t),!1)}catch{return!1}for(a of ga)if(!this.#vt[a].exec(r[a]))return!1;return!0}exec(e={},t){let a={pathname:"",protocol:"",username:"",password:"",hostname:"",port:"",search:"",hash:""};if("string"!=typeof e&&t)throw new TypeError("parameter 1 is not of type 'string'.");if(typeof e>"u")return;try{a=wa(a,"object"==typeof e?e:Sa(e,t),!1)}catch{return null}let r,n={};for(r of(n.inputs=t?[e,t]:[e],ga)){let e=this.#vt[r].exec(a[r]);if(!e)return null;let t={};for(let[a,n]of this.#wt[r].entries())if("string"==typeof n||"number"==typeof n){let r=e[a+1];t[n]=r}n[r]={input:a[r]??"",groups:t}}return n}static compareComponent(e,t,a){let r=(e,t)=>{for(let a of["type","modifier","prefix","value","suffix"]){if(e[a]<t[a])return-1;if(e[a]!==t[a])return 1}return 0},n=new Zt(3,"","","","",3),s=new Zt(0,"","","","",3),o=(e,t)=>{let a=0;for(;a<Math.min(e.length,t.length);++a){let n=r(e[a],t[a]);if(n)return n}return e.length===t.length?0:r(e[a]??n,t[a]??n)};return t.#Ct[e]||a.#Ct[e]?t.#Ct[e]&&!a.#Ct[e]?o(t.#bt[e],[s]):!t.#Ct[e]&&a.#Ct[e]?o([s],a.#bt[e]):o(t.#bt[e],a.#bt[e]):0}get protocol(){return this.#Ct.protocol}get username(){return this.#Ct.username}get password(){return this.#Ct.password}get hostname(){return this.#Ct.hostname}get port(){return this.#Ct.port}get pathname(){return this.#Ct.pathname}get search(){return this.#Ct.search}get hash(){return this.#Ct.hash}get hasRegExpGroups(){return this.#xt}};const{URLPattern:Pa}=Lt;var Ia={URLPattern:Pa};globalThis.URLPattern||(globalThis.URLPattern=Pa),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.URLPattern=void 0;const t=Ia;Object.defineProperty(e,"URLPattern",{enumerable:!0,get:function(){return t.URLPattern}}),"URLPattern"in globalThis&&(t.URLPattern=globalThis.URLPattern)}(jt),Object.defineProperty(Nt,"__esModule",{value:!0}),Nt.NetworkStorage=void 0;const ka=g,Ra=jt,Ea=A;class _a{#Kt=new Map;#Ht=new Map;#Wt=new Map;disposeRequestMap(){for(const e of this.#Kt.values())e.dispose();this.#Kt.clear()}addIntercept(e){const t=(0,Ea.uuidv4)();return this.#Ht.set(t,e),t}removeIntercept(e){if(!this.#Ht.has(e))throw new ka.NoSuchInterceptException(`Intercept '${e}' does not exist.`);this.#Ht.delete(e)}hasIntercepts(){return this.#Ht.size>0}getFetchEnableParams(){const e=[];for(const t of this.#Ht.values())for(const a of t.phases){const r=_a.requestStageFromPhase(a);if(0!==t.urlPatterns.length)for(const a of t.urlPatterns){const t=_a.cdpFromSpecUrlPattern(a);e.push({urlPattern:t,requestStage:r})}else e.push({urlPattern:"*",requestStage:r})}return{patterns:e,handleAuthRequests:[...this.#Ht.values()].some((e=>e.phases.includes("authRequired")))}}getRequest(e){return this.#Kt.get(e)}addRequest(e){this.#Kt.set(e.requestId,e)}deleteRequest(e){const t=this.#Kt.get(e);t&&(t.dispose(),this.#Kt.delete(e))}hasNetworkRequests(){return this.#Kt.size>0}hasBlockedRequests(){return this.#Wt.size>0}static cdpFromSpecUrlPattern(e){switch(e.type){case"string":return e.pattern;case"pattern":return _a.buildUrlPatternString(e)}}static buildUrlPatternString({protocol:e,hostname:t,port:a,pathname:r,search:n}){if(!(e||t||a||r||n))return"*";let s="";return e&&(s+=e,e.endsWith(":")||(s+=":"),_a.isSpecialScheme(e)&&(s+="//")),t&&(s+=t),a&&(s+=`:${a}`),r&&(r.startsWith("/")||(s+="/"),s+=r),n&&(n.startsWith("?")||(s+="?"),s+=n),s}static requestStageFromPhase(e){switch(e){case"beforeRequestSent":return"Request";case"responseStarted":case"authRequired":return"Response"}}static isSpecialScheme(e){return["ftp","file","http","https","ws","wss"].includes(e.replace(/:$/,""))}addBlockedRequest(e,t){this.#Wt.set(e,t)}removeBlockedRequest(e){this.#Wt.delete(e)}getBlockedRequest(e){return this.#Wt.get(e)}getNetworkIntercepts(e,t){const a=this.#Kt.get(e);if(!a)return[];const r=[];for(const[e,{phases:n,urlPatterns:s}]of this.#Ht.entries())t&&n.includes(t)&&(0===s.length||s.some((e=>_a.matchUrlPattern(e,a.url))))&&r.push(e);return r}static matchUrlPattern(e,t){switch(e.type){case"string":return e.pattern===t;case"pattern":return null!==new Ra.URLPattern({protocol:e.protocol,hostname:e.hostname,port:e.port,pathname:e.pathname,search:e.search}).exec(t)}}}Nt.NetworkStorage=_a,Object.defineProperty(Tt,"__esModule",{value:!0}),Tt.NetworkProcessor=void 0;const Ta=g,Na=J,ja=Nt,Oa=Me;class Ma{#i;#ie;constructor(e,t){this.#i=e,this.#ie=t}async addIntercept(e){e.phases.includes("authRequired")&&!e.phases.includes("beforeRequestSent")&&e.phases.unshift("beforeRequestSent");const t=e.urlPatterns??[],a=Ma.parseUrlPatterns(t),r=this.#ie.addIntercept({urlPatterns:a,phases:e.phases});return await this.#Jt(),{intercept:r}}async continueRequest(e){const t=e.request,{request:a,phase:r}=this.#Gt(t);if("beforeRequestSent"!==r)throw new Ta.InvalidArgumentException(`Blocked request for network id '${t}' is not in 'BeforeRequestSent' phase`);void 0!==e.url&&Ma.parseUrlString(e.url);const{url:n,method:s,headers:o}=e,i=(0,Oa.cdpFetchHeadersFromBidiNetworkHeaders)(o),c=this.#Xt(t);return await c.continueRequest(a,n,s,i),this.#ie.removeBlockedRequest(t),{}}async continueResponse(e){const t=e.request,{request:a,phase:r}=this.#Gt(t);if("beforeRequestSent"===r)throw new Ta.InvalidArgumentException(`Blocked request for network id '${t}' is in 'BeforeRequestSent' phase`);const{statusCode:n,reasonPhrase:s,headers:o}=e,i=(0,Oa.cdpFetchHeadersFromBidiNetworkHeaders)(o),c=this.#Xt(t);return await c.continueResponse(a,n,s,i),this.#ie.removeBlockedRequest(t),{}}async continueWithAuth(e){const t=e.request,{request:a,phase:r}=this.#Gt(t);if("authRequired"!==r)throw new Ta.InvalidArgumentException(`Blocked request for network id '${t}' is not in 'AuthRequired' phase`);const n=this.#Xt(t);let s,o;if("provideCredentials"===e.action){const{credentials:t}=e;s=t.username,o=t.password,(0,Na.assert)("password"===t.type,`Credentials type ${t.type} must be 'password'`)}const i=(0,Oa.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction)(e.action);return await n.continueWithAuth(a,i,s,o),{}}async failRequest({request:e}){const{request:t,phase:a}=this.#Gt(e);if("authRequired"===a)throw new Ta.InvalidArgumentException(`Blocked request for network id '${e}' is in 'AuthRequired' phase`);const r=this.#Xt(e);return await r.failRequest(t,"Failed"),this.#ie.removeBlockedRequest(e),{}}async provideResponse(e){const{statusCode:t,reasonPhrase:a,headers:r,body:n,request:s}=e,{request:o}=this.#Gt(s),i=(0,Oa.cdpFetchHeadersFromBidiNetworkHeaders)(r),c=this.#Xt(s);return await c.provideResponse(o,t??c.statusCode,a,i,n?.value),this.#ie.removeBlockedRequest(s),{}}async removeIntercept(e){return this.#ie.removeIntercept(e.intercept),await this.#Jt(),{}}async#Yt(){await Promise.all(this.#i.getAllContexts().map((async e=>{await e.cdpTarget.fetchEnable()})))}async#Qt(){await Promise.all(this.#i.getAllContexts().map((async e=>{await e.cdpTarget.fetchDisable()})))}async#Jt(){this.#ie.hasIntercepts()||this.#ie.hasBlockedRequests()||this.#ie.hasNetworkRequests()?await this.#Yt():await this.#Qt()}#Gt(e){const t=this.#ie.getBlockedRequest(e);if(!t)throw new Ta.NoSuchRequestException(`No blocked request found for network id '${e}'`);return t}#Xt(e){const t=this.#ie.getRequest(e);if(!t)throw new Ta.NoSuchRequestException(`Network request with ID ${e} doesn't exist`);return t}static parseUrlString(e){try{return new URL(e)}catch(t){throw new Ta.InvalidArgumentException(`Invalid URL '${e}': ${t}`)}}static parseUrlPatterns(e){return e.map((e=>{switch(e.type){case"string":return Ma.parseUrlString(e.pattern),e;case"pattern":if(void 0===e.protocol&&void 0===e.hostname&&void 0===e.port&&void 0===e.pathname&&void 0===e.search)return e;if(""===e.protocol)throw new Ta.InvalidArgumentException("URL pattern must specify a protocol");if(""===e.hostname)throw new Ta.InvalidArgumentException("URL pattern must specify a hostname");if((e.hostname?.length??0)>0){if(e.protocol?.match(/^file/i))throw new Ta.InvalidArgumentException("URL pattern protocol cannot be 'file'");if(e.hostname?.includes(":"))throw new Ta.InvalidArgumentException("URL pattern hostname must not contain a colon")}if(""===e.port)throw new Ta.InvalidArgumentException("URL pattern must specify a port");try{new URL(ja.NetworkStorage.buildUrlPatternString(e))}catch(e){throw new Ta.InvalidArgumentException(`${e}`)}return e}}))}}Tt.NetworkProcessor=Ma;var Aa={};Object.defineProperty(Aa,"__esModule",{value:!0}),Aa.PermissionsProcessor=void 0;const Ba=g;Aa.PermissionsProcessor=class{#o;constructor(e){this.#o=e}async setPermissions(e){try{await this.#o.sendCommand("Browser.setPermission",{origin:e.origin,permission:{name:e.descriptor.name},setting:e.state})}catch(e){if("Permission can't be granted to opaque origins."===e.message)return{};throw new Ba.InvalidArgumentException(e.message)}return{}}};var za={};Object.defineProperty(za,"__esModule",{value:!0}),za.PreloadScriptStorage=void 0;za.PreloadScriptStorage=class{#ea=new Set;find(e){return e?[...this.#ea].filter((t=>(void 0===e.id||e.id===t.id)&&(!(void 0!==e.targetId&&!t.targetIds.has(e.targetId))&&(void 0===e.global||!(e.global&&void 0!==t.contexts||!e.global&&void 0===t.contexts))))):[...this.#ea]}add(e){this.#ea.add(e)}remove(e){for(const t of this.find(e))this.#ea.delete(t)}};var Da={},La={};Object.defineProperty(La,"__esModule",{value:!0}),La.PreloadScript=void 0;const Za=A,Ua=B;La.PreloadScript=class{#u=(0,Za.uuidv4)();#ta=[];#aa;#ra=new Set;#na;#sa;#oa;get id(){return this.#u}get targetIds(){return this.#ra}constructor(e,t){this.#na=e.arguments?.map((e=>new Ua.ChannelProxy(e.value,t)))??[],this.#aa=e.functionDeclaration,this.#sa=e.sandbox,this.#oa=e.contexts}get channels(){return this.#na}get contexts(){return this.#oa}#ia(){const e=`[${this.channels.map((e=>e.getEvalInWindowStr())).join(", ")}]`;return`(()=>{(${this.#aa})(...${e})})()`}async initInTargets(e,t){await Promise.all(Array.from(e).map((e=>this.initInTarget(e,t))))}async initInTarget(e,t){const a=await e.cdpClient.sendCommand("Page.addScriptToEvaluateOnNewDocument",{source:this.#ia(),worldName:this.#sa,runImmediately:t});this.#ta.push({target:e,preloadScriptId:a.identifier}),this.#ra.add(e.id)}async remove(){for(const e of this.#ta){const t=e.target,a=e.preloadScriptId;await t.cdpClient.sendCommand("Page.removeScriptToEvaluateOnNewDocument",{identifier:a})}}dispose(e){this.#ta=this.#ta.filter((t=>t.target?.id!==e)),this.#ra.delete(e)}},Object.defineProperty(Da,"__esModule",{value:!0}),Da.ScriptProcessor=void 0;const Fa=g,qa=La;Da.ScriptProcessor=class{#i;#C;#_e;#t;constructor(e,t,a,r){this.#i=e,this.#C=t,this.#_e=a,this.#t=r}async addPreloadScript(e){const t=new Set;if(e.contexts){if(0===e.contexts.length)throw new Fa.InvalidArgumentException("Contexts list is empty.");for(const a of e.contexts){const e=this.#i.getContext(a);if(!e.isTopLevelContext())throw new Fa.InvalidArgumentException(`Non top-level context '${a}' given.`);t.add(e)}}const a=new qa.PreloadScript(e,this.#t);this.#_e.add(a);const r=0===t.size?new Set(this.#i.getTopLevelContexts().map((e=>e.cdpTarget))):new Set([...t.values()].map((e=>e.cdpTarget)));return await a.initInTargets(r,!1),{script:a.id}}async removePreloadScript(e){const t=e.script,a=this.#_e.find({id:t});if(0===a.length)throw new Fa.NoSuchScriptException(`No preload script with BiDi ID '${t}'`);return await Promise.all(a.map((e=>e.remove()))),this.#_e.remove({id:t}),{}}async callFunction(e){const t=await this.#ca(e.target);return await t.callFunction(e.functionDeclaration,e.this??{type:"undefined"},e.arguments??[],e.awaitPromise,e.resultOwnership??"none",e.serializationOptions??{},e.userActivation??!1)}async evaluate(e){const t=await this.#ca(e.target);return await t.evaluate(e.expression,e.awaitPromise,e.resultOwnership??"none",e.serializationOptions??{},e.userActivation??!1)}async disown(e){const t=await this.#ca(e.target);return await Promise.all(e.handles.map((async e=>await t.disown(e)))),{}}getRealms(e){void 0!==e.context&&this.#i.getContext(e.context);return{realms:this.#C.findRealms({browsingContextId:e.context,type:e.type}).map((e=>e.realmInfo))}}async#ca(e){if("realm"in e)return this.#C.getRealm({realmId:e.realm});const t=this.#i.getContext(e.context);return await t.getOrCreateSandbox(e.sandbox)}};var Va={};Object.defineProperty(Va,"__esModule",{value:!0}),Va.SessionProcessor=void 0;Va.SessionProcessor=class{#y;constructor(e){this.#y=e}status(){return{ready:!1,message:"already connected"}}subscribe(e,t=null){return this.#y.subscribe(e.events,e.contexts??[null],t),{}}unsubscribe(e,t=null){return this.#y.unsubscribe(e.events,e.contexts??[null],t),{}}};var $a={};Object.defineProperty($a,"__esModule",{value:!0}),$a.StorageProcessor=void 0;const Ka=g,Ha=J,Wa=l,Ja=Tt,Ga=Me;$a.StorageProcessor=class{#o;#i;#t;constructor(e,t,a){this.#i=t,this.#o=e,this.#t=a}async getCookies(e){const t=this.#da(e.partition);return{cookies:(await this.#o.sendCommand("Storage.getCookies",{browserContextId:t.userContext})).cookies.filter((e=>void 0===t.sourceOrigin||e.partitionKey===t.sourceOrigin)).map((e=>(0,Ga.cdpToBiDiCookie)(e))).filter((t=>this.#ua(t,e.filter))),partitionKey:t}}async setCookie(e){const t=this.#da(e.partition),a=(0,Ga.bidiToCdpCookie)(e,t);try{await this.#o.sendCommand("Storage.setCookies",{cookies:[a],browserContextId:t.userContext})}catch(e){throw this.#t?.(Wa.LogType.debugError,e),new Ka.UnableToSetCookieException(e.toString())}return{partitionKey:t}}#la(e){const t=e.context,a=this.#i.getContext(t);return{userContext:"default"===a.userContext?void 0:a.userContext}}#ha(e){const t=new Map;let a=e.sourceOrigin;if(void 0!==a){const e=Ja.NetworkProcessor.parseUrlString(a);a="null"===e.origin?e.origin:`${e.protocol}//${e.hostname}`}const r="default"===e.userContext?void 0:e.userContext;for(const[a,r]of Object.entries(e))void 0===a||void 0===r||["type","sourceOrigin","userContext"].includes(a)||t.set(a,r);return t.size>0&&this.#t?.(Wa.LogType.debugInfo,`Unsupported partition keys: ${JSON.stringify(Object.fromEntries(t))}`),{...void 0===a?{}:{sourceOrigin:a},...void 0===r?{}:{userContext:r}}}#da(e){return void 0===e?{}:"context"===e.type?this.#la(e):((0,Ha.assert)("storageKey"===e.type,"Unknown partition type"),this.#ha(e))}#ua(e,t){return void 0===t||!(void 0!==t.domain&&t.domain!==e.domain||void 0!==t.name&&t.name!==e.name||void 0!==t.value&&(t.value.type!==e.value.type||t.value.value!==e.value.value)||void 0!==t.path&&t.path!==e.path||void 0!==t.size&&t.size!==e.size||void 0!==t.httpOnly&&t.httpOnly!==e.httpOnly||void 0!==t.secure&&t.secure!==e.secure||void 0!==t.sameSite&&t.sameSite!==e.sameSite||void 0!==t.expiry&&t.expiry!==e.expiry)}};var Xa={};Object.defineProperty(Xa,"__esModule",{value:!0}),Xa.OutgoingMessage=void 0;class Ya{#pa;#ma;constructor(e,t=null){this.#pa=e,this.#ma=t}static createFromPromise(e,t){return e.then((e=>"success"===e.kind?{kind:"success",value:new Ya(e.value,t)}:e))}static createResolved(e,t){return Promise.resolve({kind:"success",value:new Ya(e,t)})}get message(){return this.#pa}get channel(){return this.#ma}}Xa.OutgoingMessage=Ya,Object.defineProperty(f,"__esModule",{value:!0}),f.CommandProcessor=void 0;const Qa=g,er=s,tr=l,ar=E,rr=_,nr=N,sr=j,or=et,ir=Tt,cr=Nt,dr=Aa,ur=za,lr=Da,hr=Va,pr=$a,mr=Xa;class fr extends er.EventEmitter{#fa;#ga;#ya;#Sa;#va;#wa;#Ca;#ba;#xa;#Pa;#t;constructor(e,t,a,r,n,s,o,i,c,d=new ar.BidiNoOpParser,u){super(),this.#Pa=d,this.#t=u;const l=new cr.NetworkStorage,h=new ur.PreloadScriptStorage;this.#fa=new rr.BrowserProcessor(t),this.#ga=new sr.BrowsingContextProcessor(e,t,r,a,s,o,l,h,i,c,n,u),this.#ya=new nr.CdpProcessor(s,e,t),this.#Sa=new or.InputProcessor(s,o),this.#va=new ir.NetworkProcessor(s,l),this.#wa=new dr.PermissionsProcessor(t),this.#Ca=new lr.ScriptProcessor(s,o,h,u),this.#ba=new hr.SessionProcessor(a),this.#xa=new pr.StorageProcessor(t,s,u)}async#Ia(e){switch(e.method){case"session.end":case"session.new":break;case"browser.close":return this.#fa.close();case"browser.createUserContext":return await this.#fa.createUserContext();case"browser.getUserContexts":return await this.#fa.getUserContexts();case"browser.removeUserContext":return await this.#fa.removeUserContext(e.params.userContext);case"browsingContext.activate":return await this.#ga.activate(this.#Pa.parseActivateParams(e.params));case"browsingContext.captureScreenshot":return await this.#ga.captureScreenshot(this.#Pa.parseCaptureScreenshotParams(e.params));case"browsingContext.close":return await this.#ga.close(this.#Pa.parseCloseParams(e.params));case"browsingContext.create":return await this.#ga.create(this.#Pa.parseCreateParams(e.params));case"browsingContext.getTree":return this.#ga.getTree(this.#Pa.parseGetTreeParams(e.params));case"browsingContext.handleUserPrompt":return await this.#ga.handleUserPrompt(this.#Pa.parseHandleUserPromptParams(e.params));case"browsingContext.locateNodes":throw new Qa.UnsupportedOperationException(`Command '${e.method}' not yet implemented.`);case"browsingContext.navigate":return await this.#ga.navigate(this.#Pa.parseNavigateParams(e.params));case"browsingContext.print":return await this.#ga.print(this.#Pa.parsePrintParams(e.params));case"browsingContext.reload":return await this.#ga.reload(this.#Pa.parseReloadParams(e.params));case"browsingContext.setViewport":return await this.#ga.setViewport(this.#Pa.parseSetViewportParams(e.params));case"browsingContext.traverseHistory":return await this.#ga.traverseHistory(this.#Pa.parseTraverseHistoryParams(e.params));case"cdp.getSession":return this.#ya.getSession(this.#Pa.parseGetSessionParams(e.params));case"cdp.sendCommand":return await this.#ya.sendCommand(this.#Pa.parseSendCommandParams(e.params));case"input.performActions":return await this.#Sa.performActions(this.#Pa.parsePerformActionsParams(e.params));case"input.releaseActions":return await this.#Sa.releaseActions(this.#Pa.parseReleaseActionsParams(e.params));case"input.setFiles":return await this.#Sa.setFiles(this.#Pa.parseSetFilesParams(e.params));case"network.addIntercept":return await this.#va.addIntercept(this.#Pa.parseAddInterceptParams(e.params));case"network.continueRequest":return await this.#va.continueRequest(this.#Pa.parseContinueRequestParams(e.params));case"network.continueResponse":return await this.#va.continueResponse(this.#Pa.parseContinueResponseParams(e.params));case"network.continueWithAuth":return await this.#va.continueWithAuth(this.#Pa.parseContinueWithAuthParams(e.params));case"network.failRequest":return await this.#va.failRequest(this.#Pa.parseFailRequestParams(e.params));case"network.provideResponse":return await this.#va.provideResponse(this.#Pa.parseProvideResponseParams(e.params));case"network.removeIntercept":return await this.#va.removeIntercept(this.#Pa.parseRemoveInterceptParams(e.params));case"permissions.setPermission":return await this.#wa.setPermissions(this.#Pa.parseSetPermissionsParams(e.params));case"script.addPreloadScript":return await this.#Ca.addPreloadScript(this.#Pa.parseAddPreloadScriptParams(e.params));case"script.callFunction":return await this.#Ca.callFunction(this.#Pa.parseCallFunctionParams(e.params));case"script.disown":return await this.#Ca.disown(this.#Pa.parseDisownParams(e.params));case"script.evaluate":return await this.#Ca.evaluate(this.#Pa.parseEvaluateParams(e.params));case"script.getRealms":return this.#Ca.getRealms(this.#Pa.parseGetRealmsParams(e.params));case"script.removePreloadScript":return await this.#Ca.removePreloadScript(this.#Pa.parseRemovePreloadScriptParams(e.params));case"session.status":return this.#ba.status();case"session.subscribe":return this.#ba.subscribe(this.#Pa.parseSubscribeParams(e.params),e.channel);case"session.unsubscribe":return this.#ba.unsubscribe(this.#Pa.parseSubscribeParams(e.params),e.channel);case"storage.deleteCookies":throw new Qa.UnsupportedOperationException(`${e.method} is not supported yet`);case"storage.getCookies":return await this.#xa.getCookies(this.#Pa.parseGetCookiesParams(e.params));case"storage.setCookie":return await this.#xa.setCookie(this.#Pa.parseSetCookieParams(e.params))}throw new Qa.UnknownCommandException(`Unknown command '${e.method}'.`)}async processCommand(e){try{const t=await this.#Ia(e),a={type:"success",id:e.id,result:t};this.emit("response",{message:mr.OutgoingMessage.createResolved(a,e.channel),event:e.method})}catch(t){if(t instanceof Qa.Exception)this.emit("response",{message:mr.OutgoingMessage.createResolved(t.toErrorResponse(e.id),e.channel),event:e.method});else{const a=t;this.#t?.(tr.LogType.bidi,a),this.emit("response",{message:mr.OutgoingMessage.createResolved(new Qa.UnknownErrorException(a.message,a.stack).toErrorResponse(e.id),e.channel),event:e.method})}}}}f.CommandProcessor=fr;var gr={};Object.defineProperty(gr,"__esModule",{value:!0}),gr.BrowsingContextStorage=void 0;const yr=g;gr.BrowsingContextStorage=class{#oa=new Map;getTopLevelContexts(){return this.getAllContexts().filter((e=>e.isTopLevelContext()))}getAllContexts(){return Array.from(this.#oa.values())}deleteContextById(e){this.#oa.delete(e)}deleteContext(e){this.#oa.delete(e.id)}addContext(e){this.#oa.set(e.id,e)}hasContext(e){return this.#oa.has(e)}findContext(e){return this.#oa.get(e)}findTopLevelContextId(e){if(null===e)return null;const t=this.findContext(e),a=t?.parentId??null;return null===a?e:this.findTopLevelContextId(a)}findContextBySession(e){for(const t of this.#oa.values())if(t.cdpTarget.cdpSessionId===e)return t}getContext(e){const t=this.findContext(e);if(void 0===t)throw new yr.NoSuchFrameException(`Context ${e} not found`);return t}};var Sr={};Object.defineProperty(Sr,"__esModule",{value:!0}),Sr.RealmStorage=void 0;const vr=g,wr=Q;Sr.RealmStorage=class{#ka=new Map;#Ra=new Map;get knownHandlesToRealmMap(){return this.#ka}addRealm(e){this.#Ra.set(e.realmId,e)}findRealms(e){return Array.from(this.#Ra.values()).filter((t=>(void 0===e.realmId||e.realmId===t.realmId)&&(!(void 0!==e.browsingContextId&&!t.associatedBrowsingContexts.map((e=>e.id)).includes(e.browsingContextId))&&((void 0===e.sandbox||t instanceof wr.WindowRealm&&e.sandbox===t.sandbox)&&((void 0===e.executionContextId||e.executionContextId===t.executionContextId)&&((void 0===e.origin||e.origin===t.origin)&&((void 0===e.type||e.type===t.realmType)&&(void 0===e.cdpSessionId||e.cdpSessionId===t.cdpClient.sessionId))))))))}findRealm(e){const t=this.findRealms(e);if(1===t.length)return t[0]}getRealm(e){const t=this.findRealm(e);if(void 0===t)throw new vr.NoSuchFrameException(`Realm ${JSON.stringify(e)} not found`);return t}deleteRealms(e){this.findRealms(e).map((e=>{e.dispose(),this.#Ra.delete(e.realmId),Array.from(this.knownHandlesToRealmMap.entries()).filter((([,t])=>t===e.realmId)).map((([e])=>this.knownHandlesToRealmMap.delete(e)))}))}};var Cr={},br={};Object.defineProperty(br,"__esModule",{value:!0}),br.Buffer=void 0;br.Buffer=class{#Ea;#_a=[];#Ta;constructor(e,t){this.#Ea=e,this.#Ta=t}get(){return this.#_a}add(e){for(this.#_a.push(e);this.#_a.length>this.#Ea;){const e=this.#_a.shift();void 0!==e&&this.#Ta?.(e)}}};var xr={};Object.defineProperty(xr,"__esModule",{value:!0}),xr.DefaultMap=void 0;class Pr extends Map{#Na;constructor(e,t){super(t),this.#Na=e}get(e){return this.has(e)||this.set(e,this.#Na(e)),super.get(e)}}xr.DefaultMap=Pr;var Ir={};Object.defineProperty(Ir,"__esModule",{value:!0}),Ir.IdWrapper=void 0;class kr{static#ja=0;#u;constructor(){this.#u=++kr.#ja}get id(){return this.#u}}Ir.IdWrapper=kr;var Rr={};Object.defineProperty(Rr,"__esModule",{value:!0}),Rr.assertSupportedEvent=Rr.isCdpEvent=void 0;const Er=g;function _r(e){return e.split(".").at(0)?.startsWith(Er.ChromiumBidi.BiDiModule.Cdp)??!1}Rr.isCdpEvent=_r,Rr.assertSupportedEvent=function(e){if(!Er.ChromiumBidi.EVENT_NAMES.has(e)&&!_r(e))throw new Er.InvalidArgumentException(`Unknown event: ${e}`)};var Tr={};Object.defineProperty(Tr,"__esModule",{value:!0}),Tr.SubscriptionManager=Tr.unrollEvents=Tr.cartesianProduct=void 0;const Nr=g,jr=Rr;function Or(...e){return e.reduce(((e,t)=>e.flatMap((e=>t.map((t=>[e,t].flat()))))))}function Mr(e){const t=new Set;function a(e){for(const a of e)t.add(a)}for(const r of e)switch(r){case Nr.ChromiumBidi.BiDiModule.BrowsingContext:a(Object.values(Nr.ChromiumBidi.BrowsingContext.EventNames));break;case Nr.ChromiumBidi.BiDiModule.Log:a(Object.values(Nr.ChromiumBidi.Log.EventNames));break;case Nr.ChromiumBidi.BiDiModule.Network:a(Object.values(Nr.ChromiumBidi.Network.EventNames));break;case Nr.ChromiumBidi.BiDiModule.Script:a(Object.values(Nr.ChromiumBidi.Script.EventNames));break;default:t.add(r)}return[...t.values()]}Tr.cartesianProduct=Or,Tr.unrollEvents=Mr;Tr.SubscriptionManager=class{#Oa=0;#Ma=new Map;#i;constructor(e){this.#i=e}getChannelsSubscribedToEvent(e,t){return Array.from(this.#Ma.keys()).map((a=>({priority:this.#Aa(e,t,a),channel:a}))).filter((({priority:e})=>null!==e)).sort(((e,t)=>e.priority-t.priority)).map((({channel:e})=>e))}#Aa(e,t,a){const r=this.#Ma.get(a);if(void 0===r)return null;const n=this.#i.findTopLevelContextId(t),s=[...new Set([null,n])].map((t=>{const a=r.get(t)?.get(e);if((0,jr.isCdpEvent)(e)){const e=r.get(t)?.get(Nr.ChromiumBidi.BiDiModule.Cdp);return a&&e?Math.min(a,e):a??e}return a})).filter((e=>void 0!==e));return 0===s.length?null:Math.min(...s)}subscribe(e,t,a){switch(t=this.#i.findTopLevelContextId(t),e){case Nr.ChromiumBidi.BiDiModule.BrowsingContext:return void Object.values(Nr.ChromiumBidi.BrowsingContext.EventNames).map((e=>this.subscribe(e,t,a)));case Nr.ChromiumBidi.BiDiModule.Log:return void Object.values(Nr.ChromiumBidi.Log.EventNames).map((e=>this.subscribe(e,t,a)));case Nr.ChromiumBidi.BiDiModule.Network:return void Object.values(Nr.ChromiumBidi.Network.EventNames).map((e=>this.subscribe(e,t,a)));case Nr.ChromiumBidi.BiDiModule.Script:return void Object.values(Nr.ChromiumBidi.Script.EventNames).map((e=>this.subscribe(e,t,a)))}this.#Ma.has(a)||this.#Ma.set(a,new Map);const r=this.#Ma.get(a);r.has(t)||r.set(t,new Map);const n=r.get(t);n.has(e)||n.set(e,this.#Oa++)}unsubscribeAll(e,t,a){for(const e of t)null!==e&&this.#i.getContext(e);Or(Mr(e),t).map((([e,t])=>this.#Ba(e,t,a))).forEach((e=>e()))}unsubscribe(e,t,a){this.unsubscribeAll([e],[t],a)}#Ba(e,t,a){if(t=this.#i.findTopLevelContextId(t),!this.#Ma.has(a))throw new Nr.InvalidArgumentException(`Cannot unsubscribe from ${e}, ${null===t?"null":t}. No subscription found.`);const r=this.#Ma.get(a);if(!r.has(t))throw new Nr.InvalidArgumentException(`Cannot unsubscribe from ${e}, ${null===t?"null":t}. No subscription found.`);const n=r.get(t);if(!n.has(e))throw new Nr.InvalidArgumentException(`Cannot unsubscribe from ${e}, ${null===t?"null":t}. No subscription found.`);return()=>{n.delete(e),0===n.size&&r.delete(e),0===r.size&&this.#Ma.delete(a)}}},Object.defineProperty(Cr,"__esModule",{value:!0}),Cr.EventManager=void 0;const Ar=g,Br=br,zr=xr,Dr=s,Lr=Ir,Zr=Xa,Ur=Rr,Fr=Tr;class qr{#za=new Lr.IdWrapper;#Da;#La;constructor(e,t){this.#La=e,this.#Da=t}get id(){return this.#za.id}get contextId(){return this.#Da}get event(){return this.#La}}const Vr=new Map([[Ar.ChromiumBidi.Log.EventNames.LogEntryAdded,100]]);class $r extends Dr.EventEmitter{#Za=new zr.DefaultMap((()=>new Set));#Ua=new Map;#Fa=new Map;#qa;#i;constructor(e){super(),this.#i=e,this.#qa=new Fr.SubscriptionManager(e)}static#Va(e,t,a){return JSON.stringify({eventName:e,browsingContext:t,channel:a})}registerEvent(e,t){this.registerPromiseEvent(Promise.resolve({kind:"success",value:e}),t,e.method)}registerPromiseEvent(e,t,a){const r=new qr(e,t),n=this.#qa.getChannelsSubscribedToEvent(a,t);this.#$a(r,a);for(const t of n)this.emit("event",{message:Zr.OutgoingMessage.createFromPromise(e,t),event:a}),this.#Ka(r,t,a)}subscribe(e,t,a){for(const t of e)(0,Ur.assertSupportedEvent)(t);for(const e of t)null!==e&&this.#i.getContext(e);for(const r of e)for(const e of t){this.#qa.subscribe(r,e,a);for(const t of this.#Ha(r,e,a))this.emit("event",{message:Zr.OutgoingMessage.createFromPromise(t.event,a),event:r}),this.#Ka(t,a,r)}}unsubscribe(e,t,a){for(const t of e)(0,Ur.assertSupportedEvent)(t);this.#qa.unsubscribeAll(e,t,a)}#$a(e,t){if(!Vr.has(t))return;const a=$r.#Va(t,e.contextId);this.#Ua.has(a)||this.#Ua.set(a,new Br.Buffer(Vr.get(t))),this.#Ua.get(a).add(e),this.#Za.get(t).add(e.contextId)}#Ka(e,t,a){if(!Vr.has(a))return;const r=$r.#Va(a,e.contextId,t);this.#Fa.set(r,Math.max(this.#Fa.get(r)??0,e.id))}#Ha(e,t,a){const r=$r.#Va(e,t),n=$r.#Va(e,t,a),s=this.#Fa.get(n)??-1/0,o=this.#Ua.get(r)?.get().filter((e=>e.id>s))??[];return null===t&&Array.from(this.#Za.get(e).keys()).filter((e=>null!==e&&this.#i.hasContext(e))).map((t=>this.#Ha(e,t,a))).forEach((e=>o.push(...e))),o.sort(((e,t)=>e.id-t.id))}}Cr.EventManager=$r,Object.defineProperty(n,"__esModule",{value:!0}),n.BidiServer=void 0;const Kr=s,Hr=l,Wr=h,Jr=f,Gr=gr,Xr=Sr,Yr=Cr;class Qr extends Kr.EventEmitter{#Wa;#Ja;#Ga;#y;#i=new Gr.BrowsingContextStorage;#t;#Xa=e=>{this.#Ga.processCommand(e).catch((e=>{this.#t?.(Hr.LogType.debugError,e)}))};#Ya=async e=>{const t=e.message;null!==e.channel&&(t.channel=e.channel),await this.#Ja.sendMessage(t)};constructor(e,t,a,r,n,s,o,i){super(),this.#t=i,this.#Wa=new Wr.ProcessingQueue(this.#Ya,this.#t),this.#Ja=e,this.#Ja.setOnMessage(this.#Xa),this.#y=new Yr.EventManager(this.#i),this.#Ga=new Jr.CommandProcessor(t,a,this.#y,r,n,this.#i,new Xr.RealmStorage,s?.acceptInsecureCerts??!1,s?.sharedIdWithFrame??!1,o,this.#t),this.#y.on("event",(({message:e,event:t})=>{this.emitOutgoingMessage(e,t)})),this.#Ga.on("response",(({message:e,event:t})=>{this.emitOutgoingMessage(e,t)}))}static async createAndStart(e,t,a,r,n,s,o){const[{browserContextIds:i},{targetInfos:c}]=await Promise.all([a.sendCommand("Target.getBrowserContexts"),a.sendCommand("Target.getTargets")]);let d="default";for(const e of c)if(e.browserContextId&&!i.includes(e.browserContextId)){d=e.browserContextId;break}const u=new Qr(e,t,a,r,d,n,s,o);return await a.sendCommand("Target.setDiscoverTargets",{discover:!0}),await a.sendCommand("Target.setAutoAttach",{autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),await u.#Qa(),u}emitOutgoingMessage(e,t){this.#Wa.add(e,t)}close(){this.#Ja.close()}async#Qa(){await Promise.all(this.#i.getTopLevelContexts().map((e=>e.lifecycleLoaded())))}}n.BidiServer=Qr,function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.OutgoingMessage=e.EventEmitter=e.BidiServer=void 0;var t=n;Object.defineProperty(e,"BidiServer",{enumerable:!0,get:function(){return t.BidiServer}});var a=s;Object.defineProperty(e,"EventEmitter",{enumerable:!0,get:function(){return a.EventEmitter}});var r=Xa;Object.defineProperty(e,"OutgoingMessage",{enumerable:!0,get:function(){return r.OutgoingMessage}})}(r);var en={},tn={};Object.defineProperty(tn,"__esModule",{value:!0}),tn.MapperCdpClient=tn.CloseError=void 0;const an=s;class rn extends Error{}tn.CloseError=rn;class nn extends an.EventEmitter{#c;#er;constructor(e,t){super(),this.#c=e,this.#er=t}get sessionId(){return this.#er}sendCommand(e,...t){return this.#c.sendCommand(e,t[0],this.#er)}isCloseError(e){return e instanceof rn}}tn.MapperCdpClient=nn,Object.defineProperty(en,"__esModule",{value:!0}),en.MapperCdpConnection=void 0;const sn=l,on=tn;class cn{static LOGGER_PREFIX_RECV=`${sn.LogType.cdp}:RECV \u25c2`;static LOGGER_PREFIX_SEND=`${sn.LogType.cdp}:SEND \u25b8`;#tr;#Ja;#ar=new Map;#rr=new Map;#t;#nr=0;constructor(e,t){this.#Ja=e,this.#t=t,this.#Ja.setOnMessage(this.#sr),this.#tr=this.#or(void 0)}close(){this.#Ja.close();for(const[,{reject:e,error:t}]of this.#rr)e(t);this.#rr.clear(),this.#ar.clear()}async createBrowserSession(){const{sessionId:e}=await this.#tr.sendCommand("Target.attachToBrowserTarget");return this.#or(e)}getCdpClient(e){const t=this.#ar.get(e);if(!t)throw new Error(`Unknown CDP session ID: ${e}`);return t}sendCommand(e,t,a){return new Promise(((r,n)=>{const s=this.#nr++;this.#rr.set(s,{resolve:r,reject:n,error:new on.CloseError(`${e} ${JSON.stringify(t)} ${a??""} call rejected because the connection has been closed.`)});const o={id:s,method:e,params:t};a&&(o.sessionId=a),this.#Ja.sendMessage(JSON.stringify(o))?.catch((e=>{this.#t?.(sn.LogType.debugError,e),this.#Ja.close()})),this.#t?.(cn.LOGGER_PREFIX_SEND,o)}))}#sr=e=>{const t=JSON.parse(e);if(this.#t?.(cn.LOGGER_PREFIX_RECV,t),"Target.attachedToTarget"===t.method){const{sessionId:e}=t.params;this.#or(e)}if(void 0!==t.id){const e=this.#rr.get(t.id);this.#rr.delete(t.id),e&&(t.result?e.resolve(t.result):t.error&&e.reject(t.error))}else if(t.method){const e=this.#ar.get(t.sessionId??void 0);if(e?.emit(t.method,t.params||{}),"Target.detachedFromTarget"===t.method){const{sessionId:e}=t.params,a=this.#ar.get(e);a&&(this.#ar.delete(e),a.removeAllListeners())}}};#or(e){const t=new on.MapperCdpClient(this,e);return this.#ar.set(e,t),t}}en.MapperCdpConnection=cn;var dn={},un={},ln={},hn={},pn={},mn={},fn={};!function(e){var t;Object.defineProperty(e,"__esModule",{value:!0}),e.getParsedType=e.ZodParsedType=e.objectUtil=e.util=void 0,function(e){e.assertEqual=e=>e,e.assertIs=function(e){},e.assertNever=function(e){throw new Error},e.arrayToEnum=e=>{const t={};for(const a of e)t[a]=a;return t},e.getValidEnumValues=t=>{const a=e.objectKeys(t).filter((e=>"number"!=typeof t[t[e]])),r={};for(const e of a)r[e]=t[e];return e.objectValues(r)},e.objectValues=t=>e.objectKeys(t).map((function(e){return t[e]})),e.objectKeys="function"==typeof Object.keys?e=>Object.keys(e):e=>{const t=[];for(const a in e)Object.prototype.hasOwnProperty.call(e,a)&&t.push(a);return t},e.find=(e,t)=>{for(const a of e)if(t(a))return a},e.isInteger="function"==typeof Number.isInteger?e=>Number.isInteger(e):e=>"number"==typeof e&&isFinite(e)&&Math.floor(e)===e,e.joinValues=function(e,t=" | "){return e.map((e=>"string"==typeof e?`'${e}'`:e)).join(t)},e.jsonStringifyReplacer=(e,t)=>"bigint"==typeof t?t.toString():t}(t=e.util||(e.util={})),(e.objectUtil||(e.objectUtil={})).mergeShapes=(e,t)=>({...e,...t}),e.ZodParsedType=t.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]);e.getParsedType=t=>{switch(typeof t){case"undefined":return e.ZodParsedType.undefined;case"string":return e.ZodParsedType.string;case"number":return isNaN(t)?e.ZodParsedType.nan:e.ZodParsedType.number;case"boolean":return e.ZodParsedType.boolean;case"function":return e.ZodParsedType.function;case"bigint":return e.ZodParsedType.bigint;case"symbol":return e.ZodParsedType.symbol;case"object":return Array.isArray(t)?e.ZodParsedType.array:null===t?e.ZodParsedType.null:t.then&&"function"==typeof t.then&&t.catch&&"function"==typeof t.catch?e.ZodParsedType.promise:"undefined"!=typeof Map&&t instanceof Map?e.ZodParsedType.map:"undefined"!=typeof Set&&t instanceof Set?e.ZodParsedType.set:"undefined"!=typeof Date&&t instanceof Date?e.ZodParsedType.date:e.ZodParsedType.object;default:return e.ZodParsedType.unknown}}}(fn);var gn={};Object.defineProperty(gn,"__esModule",{value:!0}),gn.ZodError=gn.quotelessJson=gn.ZodIssueCode=void 0;const yn=fn;gn.ZodIssueCode=yn.util.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]);gn.quotelessJson=e=>JSON.stringify(e,null,2).replace(/"([^"]+)":/g,"$1:");class Sn extends Error{constructor(e){super(),this.issues=[],this.addIssue=e=>{this.issues=[...this.issues,e]},this.addIssues=(e=[])=>{this.issues=[...this.issues,...e]};const t=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,t):this.__proto__=t,this.name="ZodError",this.issues=e}get errors(){return this.issues}format(e){const t=e||function(e){return e.message},a={_errors:[]},r=e=>{for(const n of e.issues)if("invalid_union"===n.code)n.unionErrors.map(r);else if("invalid_return_type"===n.code)r(n.returnTypeError);else if("invalid_arguments"===n.code)r(n.argumentsError);else if(0===n.path.length)a._errors.push(t(n));else{let e=a,r=0;for(;r<n.path.length;){const a=n.path[r];r===n.path.length-1?(e[a]=e[a]||{_errors:[]},e[a]._errors.push(t(n))):e[a]=e[a]||{_errors:[]},e=e[a],r++}}};return r(this),a}toString(){return this.message}get message(){return JSON.stringify(this.issues,yn.util.jsonStringifyReplacer,2)}get isEmpty(){return 0===this.issues.length}flatten(e=(e=>e.message)){const t={},a=[];for(const r of this.issues)r.path.length>0?(t[r.path[0]]=t[r.path[0]]||[],t[r.path[0]].push(e(r))):a.push(e(r));return{formErrors:a,fieldErrors:t}}get formErrors(){return this.flatten()}}gn.ZodError=Sn,Sn.create=e=>new Sn(e),Object.defineProperty(mn,"__esModule",{value:!0});const vn=fn,wn=gn;mn.default=(e,t)=>{let a;switch(e.code){case wn.ZodIssueCode.invalid_type:a=e.received===vn.ZodParsedType.undefined?"Required":`Expected ${e.expected}, received ${e.received}`;break;case wn.ZodIssueCode.invalid_literal:a=`Invalid literal value, expected ${JSON.stringify(e.expected,vn.util.jsonStringifyReplacer)}`;break;case wn.ZodIssueCode.unrecognized_keys:a=`Unrecognized key(s) in object: ${vn.util.joinValues(e.keys,", ")}`;break;case wn.ZodIssueCode.invalid_union:a="Invalid input";break;case wn.ZodIssueCode.invalid_union_discriminator:a=`Invalid discriminator value. Expected ${vn.util.joinValues(e.options)}`;break;case wn.ZodIssueCode.invalid_enum_value:a=`Invalid enum value. Expected ${vn.util.joinValues(e.options)}, received '${e.received}'`;break;case wn.ZodIssueCode.invalid_arguments:a="Invalid function arguments";break;case wn.ZodIssueCode.invalid_return_type:a="Invalid function return type";break;case wn.ZodIssueCode.invalid_date:a="Invalid date";break;case wn.ZodIssueCode.invalid_string:"object"==typeof e.validation?"includes"in e.validation?(a=`Invalid input: must include "${e.validation.includes}"`,"number"==typeof e.validation.position&&(a=`${a} at one or more positions greater than or equal to ${e.validation.position}`)):"startsWith"in e.validation?a=`Invalid input: must start with "${e.validation.startsWith}"`:"endsWith"in e.validation?a=`Invalid input: must end with "${e.validation.endsWith}"`:vn.util.assertNever(e.validation):a="regex"!==e.validation?`Invalid ${e.validation}`:"Invalid";break;case wn.ZodIssueCode.too_small:a="array"===e.type?`Array must contain ${e.exact?"exactly":e.inclusive?"at least":"more than"} ${e.minimum} element(s)`:"string"===e.type?`String must contain ${e.exact?"exactly":e.inclusive?"at least":"over"} ${e.minimum} character(s)`:"number"===e.type?`Number must be ${e.exact?"exactly equal to ":e.inclusive?"greater than or equal to ":"greater than "}${e.minimum}`:"date"===e.type?`Date must be ${e.exact?"exactly equal to ":e.inclusive?"greater than or equal to ":"greater than "}${new Date(Number(e.minimum))}`:"Invalid input";break;case wn.ZodIssueCode.too_big:a="array"===e.type?`Array must contain ${e.exact?"exactly":e.inclusive?"at most":"less than"} ${e.maximum} element(s)`:"string"===e.type?`String must contain ${e.exact?"exactly":e.inclusive?"at most":"under"} ${e.maximum} character(s)`:"number"===e.type?`Number must be ${e.exact?"exactly":e.inclusive?"less than or equal to":"less than"} ${e.maximum}`:"bigint"===e.type?`BigInt must be ${e.exact?"exactly":e.inclusive?"less than or equal to":"less than"} ${e.maximum}`:"date"===e.type?`Date must be ${e.exact?"exactly":e.inclusive?"smaller than or equal to":"smaller than"} ${new Date(Number(e.maximum))}`:"Invalid input";break;case wn.ZodIssueCode.custom:a="Invalid input";break;case wn.ZodIssueCode.invalid_intersection_types:a="Intersection results could not be merged";break;case wn.ZodIssueCode.not_multiple_of:a=`Number must be a multiple of ${e.multipleOf}`;break;case wn.ZodIssueCode.not_finite:a="Number must be finite";break;default:a=t.defaultError,vn.util.assertNever(e)}return{message:a}};var Cn=e&&e.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(pn,"__esModule",{value:!0}),pn.getErrorMap=pn.setErrorMap=pn.defaultErrorMap=void 0;const bn=Cn(mn);pn.defaultErrorMap=bn.default;let xn=bn.default;pn.setErrorMap=function(e){xn=e},pn.getErrorMap=function(){return xn};var Pn={};!function(t){var a=e&&e.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isAsync=t.isValid=t.isDirty=t.isAborted=t.OK=t.DIRTY=t.INVALID=t.ParseStatus=t.addIssueToContext=t.EMPTY_PATH=t.makeIssue=void 0;const r=pn,n=a(mn);t.makeIssue=e=>{const{data:t,path:a,errorMaps:r,issueData:n}=e,s=[...a,...n.path||[]],o={...n,path:s};let i="";const c=r.filter((e=>!!e)).slice().reverse();for(const e of c)i=e(o,{data:t,defaultError:i}).message;return{...n,path:s,message:n.message||i}},t.EMPTY_PATH=[],t.addIssueToContext=function(e,a){const s=(0,t.makeIssue)({issueData:a,data:e.data,path:e.path,errorMaps:[e.common.contextualErrorMap,e.schemaErrorMap,(0,r.getErrorMap)(),n.default].filter((e=>!!e))});e.common.issues.push(s)};class s{constructor(){this.value="valid"}dirty(){"valid"===this.value&&(this.value="dirty")}abort(){"aborted"!==this.value&&(this.value="aborted")}static mergeArray(e,a){const r=[];for(const n of a){if("aborted"===n.status)return t.INVALID;"dirty"===n.status&&e.dirty(),r.push(n.value)}return{status:e.value,value:r}}static async mergeObjectAsync(e,t){const a=[];for(const e of t)a.push({key:await e.key,value:await e.value});return s.mergeObjectSync(e,a)}static mergeObjectSync(e,a){const r={};for(const n of a){const{key:a,value:s}=n;if("aborted"===a.status)return t.INVALID;if("aborted"===s.status)return t.INVALID;"dirty"===a.status&&e.dirty(),"dirty"===s.status&&e.dirty(),"__proto__"===a.value||void 0===s.value&&!n.alwaysSet||(r[a.value]=s.value)}return{status:e.value,value:r}}}t.ParseStatus=s,t.INVALID=Object.freeze({status:"aborted"});t.DIRTY=e=>({status:"dirty",value:e});t.OK=e=>({status:"valid",value:e});t.isAborted=e=>"aborted"===e.status;t.isDirty=e=>"dirty"===e.status;t.isValid=e=>"valid"===e.status;t.isAsync=e=>"undefined"!=typeof Promise&&e instanceof Promise}(Pn);var In={};Object.defineProperty(In,"__esModule",{value:!0});var kn,Rn={},En={};kn=En,Object.defineProperty(kn,"__esModule",{value:!0}),kn.errorUtil=void 0,function(e){e.errToObj=e=>"string"==typeof e?{message:e}:e||{},e.toString=e=>"string"==typeof e?e:null==e?void 0:e.message}(kn.errorUtil||(kn.errorUtil={})),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.date=e.boolean=e.bigint=e.array=e.any=e.coerce=e.ZodFirstPartyTypeKind=e.late=e.ZodSchema=e.Schema=e.custom=e.ZodReadonly=e.ZodPipeline=e.ZodBranded=e.BRAND=e.ZodNaN=e.ZodCatch=e.ZodDefault=e.ZodNullable=e.ZodOptional=e.ZodTransformer=e.ZodEffects=e.ZodPromise=e.ZodNativeEnum=e.ZodEnum=e.ZodLiteral=e.ZodLazy=e.ZodFunction=e.ZodSet=e.ZodMap=e.ZodRecord=e.ZodTuple=e.ZodIntersection=e.ZodDiscriminatedUnion=e.ZodUnion=e.ZodObject=e.ZodArray=e.ZodVoid=e.ZodNever=e.ZodUnknown=e.ZodAny=e.ZodNull=e.ZodUndefined=e.ZodSymbol=e.ZodDate=e.ZodBoolean=e.ZodBigInt=e.ZodNumber=e.ZodString=e.ZodType=void 0,e.NEVER=e.void=e.unknown=e.union=e.undefined=e.tuple=e.transformer=e.symbol=e.string=e.strictObject=e.set=e.record=e.promise=e.preprocess=e.pipeline=e.ostring=e.optional=e.onumber=e.oboolean=e.object=e.number=e.nullable=e.null=e.never=e.nativeEnum=e.nan=e.map=e.literal=e.lazy=e.intersection=e.instanceof=e.function=e.enum=e.effect=e.discriminatedUnion=void 0;const t=pn,a=En,r=Pn,n=fn,s=gn;class o{constructor(e,t,a,r){this._cachedPath=[],this.parent=e,this.data=t,this._path=a,this._key=r}get path(){return this._cachedPath.length||(this._key instanceof Array?this._cachedPath.push(...this._path,...this._key):this._cachedPath.push(...this._path,this._key)),this._cachedPath}}const i=(e,t)=>{if((0,r.isValid)(t))return{success:!0,data:t.value};if(!e.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,get error(){if(this._error)return this._error;const t=new s.ZodError(e.common.issues);return this._error=t,this._error}}};function c(e){if(!e)return{};const{errorMap:t,invalid_type_error:a,required_error:r,description:n}=e;if(t&&(a||r))throw new Error('Can\'t use "invalid_type_error" or "required_error" in conjunction with custom error map.');if(t)return{errorMap:t,description:n};return{errorMap:(e,t)=>"invalid_type"!==e.code?{message:t.defaultError}:void 0===t.data?{message:null!=r?r:t.defaultError}:{message:null!=a?a:t.defaultError},description:n}}class d{constructor(e){this.spa=this.safeParseAsync,this._def=e,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.brand=this.brand.bind(this),this.default=this.default.bind(this),this.catch=this.catch.bind(this),this.describe=this.describe.bind(this),this.pipe=this.pipe.bind(this),this.readonly=this.readonly.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this)}get description(){return this._def.description}_getType(e){return(0,n.getParsedType)(e.data)}_getOrReturnCtx(e,t){return t||{common:e.parent.common,data:e.data,parsedType:(0,n.getParsedType)(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}_processInputParams(e){return{status:new r.ParseStatus,ctx:{common:e.parent.common,data:e.data,parsedType:(0,n.getParsedType)(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}}_parseSync(e){const t=this._parse(e);if((0,r.isAsync)(t))throw new Error("Synchronous parse encountered promise.");return t}_parseAsync(e){const t=this._parse(e);return Promise.resolve(t)}parse(e,t){const a=this.safeParse(e,t);if(a.success)return a.data;throw a.error}safeParse(e,t){var a;const r={common:{issues:[],async:null!==(a=null==t?void 0:t.async)&&void 0!==a&&a,contextualErrorMap:null==t?void 0:t.errorMap},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:(0,n.getParsedType)(e)},s=this._parseSync({data:e,path:r.path,parent:r});return i(r,s)}async parseAsync(e,t){const a=await this.safeParseAsync(e,t);if(a.success)return a.data;throw a.error}async safeParseAsync(e,t){const a={common:{issues:[],contextualErrorMap:null==t?void 0:t.errorMap,async:!0},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:(0,n.getParsedType)(e)},s=this._parse({data:e,path:a.path,parent:a}),o=await((0,r.isAsync)(s)?s:Promise.resolve(s));return i(a,o)}refine(e,t){const a=e=>"string"==typeof t||void 0===t?{message:t}:"function"==typeof t?t(e):t;return this._refinement(((t,r)=>{const n=e(t),o=()=>r.addIssue({code:s.ZodIssueCode.custom,...a(t)});return"undefined"!=typeof Promise&&n instanceof Promise?n.then((e=>!!e||(o(),!1))):!!n||(o(),!1)}))}refinement(e,t){return this._refinement(((a,r)=>!!e(a)||(r.addIssue("function"==typeof t?t(a,r):t),!1)))}_refinement(e){return new G({schema:this,typeName:se.ZodEffects,effect:{type:"refinement",refinement:e}})}superRefine(e){return this._refinement(e)}optional(){return X.create(this,this._def)}nullable(){return Y.create(this,this._def)}nullish(){return this.nullable().optional()}array(){return N.create(this,this._def)}promise(){return J.create(this,this._def)}or(e){return M.create([this,e],this._def)}and(e){return D.create(this,e,this._def)}transform(e){return new G({...c(this._def),schema:this,typeName:se.ZodEffects,effect:{type:"transform",transform:e}})}default(e){const t="function"==typeof e?e:()=>e;return new Q({...c(this._def),innerType:this,defaultValue:t,typeName:se.ZodDefault})}brand(){return new ae({typeName:se.ZodBranded,type:this,...c(this._def)})}catch(e){const t="function"==typeof e?e:()=>e;return new ee({...c(this._def),innerType:this,catchValue:t,typeName:se.ZodCatch})}describe(e){return new(0,this.constructor)({...this._def,description:e})}pipe(e){return re.create(this,e)}readonly(){return ne.create(this)}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}}e.ZodType=d,e.Schema=d,e.ZodSchema=d;const u=/^c[^\s-]{8,}$/i,l=/^[a-z][a-z0-9]*$/,h=/^[0-9A-HJKMNP-TV-Z]{26}$/,p=/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i,m=/^(?!\.)(?!.*\.\.)([A-Z0-9_+-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i;let f;const g=/^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/,y=/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;class S extends d{_parse(e){this._def.coerce&&(e.data=String(e.data));if(this._getType(e)!==n.ZodParsedType.string){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.string,received:t.parsedType}),r.INVALID}const t=new r.ParseStatus;let a;for(const d of this._def.checks)if("min"===d.kind)e.data.length<d.value&&(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_small,minimum:d.value,type:"string",inclusive:!0,exact:!1,message:d.message}),t.dirty());else if("max"===d.kind)e.data.length>d.value&&(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_big,maximum:d.value,type:"string",inclusive:!0,exact:!1,message:d.message}),t.dirty());else if("length"===d.kind){const n=e.data.length>d.value,o=e.data.length<d.value;(n||o)&&(a=this._getOrReturnCtx(e,a),n?(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_big,maximum:d.value,type:"string",inclusive:!0,exact:!0,message:d.message}):o&&(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_small,minimum:d.value,type:"string",inclusive:!0,exact:!0,message:d.message}),t.dirty())}else if("email"===d.kind)m.test(e.data)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{validation:"email",code:s.ZodIssueCode.invalid_string,message:d.message}),t.dirty());else if("emoji"===d.kind)f||(f=new RegExp("^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$","u")),f.test(e.data)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{validation:"emoji",code:s.ZodIssueCode.invalid_string,message:d.message}),t.dirty());else if("uuid"===d.kind)p.test(e.data)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{validation:"uuid",code:s.ZodIssueCode.invalid_string,message:d.message}),t.dirty());else if("cuid"===d.kind)u.test(e.data)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{validation:"cuid",code:s.ZodIssueCode.invalid_string,message:d.message}),t.dirty());else if("cuid2"===d.kind)l.test(e.data)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{validation:"cuid2",code:s.ZodIssueCode.invalid_string,message:d.message}),t.dirty());else if("ulid"===d.kind)h.test(e.data)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{validation:"ulid",code:s.ZodIssueCode.invalid_string,message:d.message}),t.dirty());else if("url"===d.kind)try{new URL(e.data)}catch(n){a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{validation:"url",code:s.ZodIssueCode.invalid_string,message:d.message}),t.dirty()}else if("regex"===d.kind){d.regex.lastIndex=0;d.regex.test(e.data)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{validation:"regex",code:s.ZodIssueCode.invalid_string,message:d.message}),t.dirty())}else if("trim"===d.kind)e.data=e.data.trim();else if("includes"===d.kind)e.data.includes(d.value,d.position)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_string,validation:{includes:d.value,position:d.position},message:d.message}),t.dirty());else if("toLowerCase"===d.kind)e.data=e.data.toLowerCase();else if("toUpperCase"===d.kind)e.data=e.data.toUpperCase();else if("startsWith"===d.kind)e.data.startsWith(d.value)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_string,validation:{startsWith:d.value},message:d.message}),t.dirty());else if("endsWith"===d.kind)e.data.endsWith(d.value)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_string,validation:{endsWith:d.value},message:d.message}),t.dirty());else if("datetime"===d.kind){((c=d).precision?c.offset?new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${c.precision}}(([+-]\\d{2}(:?\\d{2})?)|Z)$`):new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${c.precision}}Z$`):0===c.precision?c.offset?new RegExp("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(([+-]\\d{2}(:?\\d{2})?)|Z)$"):new RegExp("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"):c.offset?new RegExp("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(([+-]\\d{2}(:?\\d{2})?)|Z)$"):new RegExp("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$")).test(e.data)||(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_string,validation:"datetime",message:d.message}),t.dirty())}else"ip"===d.kind?(o=e.data,("v4"!==(i=d.version)&&i||!g.test(o))&&("v6"!==i&&i||!y.test(o))&&(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{validation:"ip",code:s.ZodIssueCode.invalid_string,message:d.message}),t.dirty())):n.util.assertNever(d);var o,i,c;return{status:t.value,value:e.data}}_regex(e,t,r){return this.refinement((t=>e.test(t)),{validation:t,code:s.ZodIssueCode.invalid_string,...a.errorUtil.errToObj(r)})}_addCheck(e){return new S({...this._def,checks:[...this._def.checks,e]})}email(e){return this._addCheck({kind:"email",...a.errorUtil.errToObj(e)})}url(e){return this._addCheck({kind:"url",...a.errorUtil.errToObj(e)})}emoji(e){return this._addCheck({kind:"emoji",...a.errorUtil.errToObj(e)})}uuid(e){return this._addCheck({kind:"uuid",...a.errorUtil.errToObj(e)})}cuid(e){return this._addCheck({kind:"cuid",...a.errorUtil.errToObj(e)})}cuid2(e){return this._addCheck({kind:"cuid2",...a.errorUtil.errToObj(e)})}ulid(e){return this._addCheck({kind:"ulid",...a.errorUtil.errToObj(e)})}ip(e){return this._addCheck({kind:"ip",...a.errorUtil.errToObj(e)})}datetime(e){var t;return"string"==typeof e?this._addCheck({kind:"datetime",precision:null,offset:!1,message:e}):this._addCheck({kind:"datetime",precision:void 0===(null==e?void 0:e.precision)?null:null==e?void 0:e.precision,offset:null!==(t=null==e?void 0:e.offset)&&void 0!==t&&t,...a.errorUtil.errToObj(null==e?void 0:e.message)})}regex(e,t){return this._addCheck({kind:"regex",regex:e,...a.errorUtil.errToObj(t)})}includes(e,t){return this._addCheck({kind:"includes",value:e,position:null==t?void 0:t.position,...a.errorUtil.errToObj(null==t?void 0:t.message)})}startsWith(e,t){return this._addCheck({kind:"startsWith",value:e,...a.errorUtil.errToObj(t)})}endsWith(e,t){return this._addCheck({kind:"endsWith",value:e,...a.errorUtil.errToObj(t)})}min(e,t){return this._addCheck({kind:"min",value:e,...a.errorUtil.errToObj(t)})}max(e,t){return this._addCheck({kind:"max",value:e,...a.errorUtil.errToObj(t)})}length(e,t){return this._addCheck({kind:"length",value:e,...a.errorUtil.errToObj(t)})}nonempty(e){return this.min(1,a.errorUtil.errToObj(e))}trim(){return new S({...this._def,checks:[...this._def.checks,{kind:"trim"}]})}toLowerCase(){return new S({...this._def,checks:[...this._def.checks,{kind:"toLowerCase"}]})}toUpperCase(){return new S({...this._def,checks:[...this._def.checks,{kind:"toUpperCase"}]})}get isDatetime(){return!!this._def.checks.find((e=>"datetime"===e.kind))}get isEmail(){return!!this._def.checks.find((e=>"email"===e.kind))}get isURL(){return!!this._def.checks.find((e=>"url"===e.kind))}get isEmoji(){return!!this._def.checks.find((e=>"emoji"===e.kind))}get isUUID(){return!!this._def.checks.find((e=>"uuid"===e.kind))}get isCUID(){return!!this._def.checks.find((e=>"cuid"===e.kind))}get isCUID2(){return!!this._def.checks.find((e=>"cuid2"===e.kind))}get isULID(){return!!this._def.checks.find((e=>"ulid"===e.kind))}get isIP(){return!!this._def.checks.find((e=>"ip"===e.kind))}get minLength(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxLength(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}}function v(e,t){const a=(e.toString().split(".")[1]||"").length,r=(t.toString().split(".")[1]||"").length,n=a>r?a:r;return parseInt(e.toFixed(n).replace(".",""))%parseInt(t.toFixed(n).replace(".",""))/Math.pow(10,n)}e.ZodString=S,S.create=e=>{var t;return new S({checks:[],typeName:se.ZodString,coerce:null!==(t=null==e?void 0:e.coerce)&&void 0!==t&&t,...c(e)})};class w extends d{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(e){this._def.coerce&&(e.data=Number(e.data));if(this._getType(e)!==n.ZodParsedType.number){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.number,received:t.parsedType}),r.INVALID}let t;const a=new r.ParseStatus;for(const o of this._def.checks)if("int"===o.kind)n.util.isInteger(e.data)||(t=this._getOrReturnCtx(e,t),(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:"integer",received:"float",message:o.message}),a.dirty());else if("min"===o.kind){(o.inclusive?e.data<o.value:e.data<=o.value)&&(t=this._getOrReturnCtx(e,t),(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.too_small,minimum:o.value,type:"number",inclusive:o.inclusive,exact:!1,message:o.message}),a.dirty())}else if("max"===o.kind){(o.inclusive?e.data>o.value:e.data>=o.value)&&(t=this._getOrReturnCtx(e,t),(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.too_big,maximum:o.value,type:"number",inclusive:o.inclusive,exact:!1,message:o.message}),a.dirty())}else"multipleOf"===o.kind?0!==v(e.data,o.value)&&(t=this._getOrReturnCtx(e,t),(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.not_multiple_of,multipleOf:o.value,message:o.message}),a.dirty()):"finite"===o.kind?Number.isFinite(e.data)||(t=this._getOrReturnCtx(e,t),(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.not_finite,message:o.message}),a.dirty()):n.util.assertNever(o);return{status:a.value,value:e.data}}gte(e,t){return this.setLimit("min",e,!0,a.errorUtil.toString(t))}gt(e,t){return this.setLimit("min",e,!1,a.errorUtil.toString(t))}lte(e,t){return this.setLimit("max",e,!0,a.errorUtil.toString(t))}lt(e,t){return this.setLimit("max",e,!1,a.errorUtil.toString(t))}setLimit(e,t,r,n){return new w({...this._def,checks:[...this._def.checks,{kind:e,value:t,inclusive:r,message:a.errorUtil.toString(n)}]})}_addCheck(e){return new w({...this._def,checks:[...this._def.checks,e]})}int(e){return this._addCheck({kind:"int",message:a.errorUtil.toString(e)})}positive(e){return this._addCheck({kind:"min",value:0,inclusive:!1,message:a.errorUtil.toString(e)})}negative(e){return this._addCheck({kind:"max",value:0,inclusive:!1,message:a.errorUtil.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:0,inclusive:!0,message:a.errorUtil.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:0,inclusive:!0,message:a.errorUtil.toString(e)})}multipleOf(e,t){return this._addCheck({kind:"multipleOf",value:e,message:a.errorUtil.toString(t)})}finite(e){return this._addCheck({kind:"finite",message:a.errorUtil.toString(e)})}safe(e){return this._addCheck({kind:"min",inclusive:!0,value:Number.MIN_SAFE_INTEGER,message:a.errorUtil.toString(e)})._addCheck({kind:"max",inclusive:!0,value:Number.MAX_SAFE_INTEGER,message:a.errorUtil.toString(e)})}get minValue(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}get isInt(){return!!this._def.checks.find((e=>"int"===e.kind||"multipleOf"===e.kind&&n.util.isInteger(e.value)))}get isFinite(){let e=null,t=null;for(const a of this._def.checks){if("finite"===a.kind||"int"===a.kind||"multipleOf"===a.kind)return!0;"min"===a.kind?(null===t||a.value>t)&&(t=a.value):"max"===a.kind&&(null===e||a.value<e)&&(e=a.value)}return Number.isFinite(t)&&Number.isFinite(e)}}e.ZodNumber=w,w.create=e=>new w({checks:[],typeName:se.ZodNumber,coerce:(null==e?void 0:e.coerce)||!1,...c(e)});class C extends d{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte}_parse(e){this._def.coerce&&(e.data=BigInt(e.data));if(this._getType(e)!==n.ZodParsedType.bigint){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.bigint,received:t.parsedType}),r.INVALID}let t;const a=new r.ParseStatus;for(const o of this._def.checks)if("min"===o.kind){(o.inclusive?e.data<o.value:e.data<=o.value)&&(t=this._getOrReturnCtx(e,t),(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.too_small,type:"bigint",minimum:o.value,inclusive:o.inclusive,message:o.message}),a.dirty())}else if("max"===o.kind){(o.inclusive?e.data>o.value:e.data>=o.value)&&(t=this._getOrReturnCtx(e,t),(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.too_big,type:"bigint",maximum:o.value,inclusive:o.inclusive,message:o.message}),a.dirty())}else"multipleOf"===o.kind?e.data%o.value!==BigInt(0)&&(t=this._getOrReturnCtx(e,t),(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.not_multiple_of,multipleOf:o.value,message:o.message}),a.dirty()):n.util.assertNever(o);return{status:a.value,value:e.data}}gte(e,t){return this.setLimit("min",e,!0,a.errorUtil.toString(t))}gt(e,t){return this.setLimit("min",e,!1,a.errorUtil.toString(t))}lte(e,t){return this.setLimit("max",e,!0,a.errorUtil.toString(t))}lt(e,t){return this.setLimit("max",e,!1,a.errorUtil.toString(t))}setLimit(e,t,r,n){return new C({...this._def,checks:[...this._def.checks,{kind:e,value:t,inclusive:r,message:a.errorUtil.toString(n)}]})}_addCheck(e){return new C({...this._def,checks:[...this._def.checks,e]})}positive(e){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!1,message:a.errorUtil.toString(e)})}negative(e){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!1,message:a.errorUtil.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!0,message:a.errorUtil.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!0,message:a.errorUtil.toString(e)})}multipleOf(e,t){return this._addCheck({kind:"multipleOf",value:e,message:a.errorUtil.toString(t)})}get minValue(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}}e.ZodBigInt=C,C.create=e=>{var t;return new C({checks:[],typeName:se.ZodBigInt,coerce:null!==(t=null==e?void 0:e.coerce)&&void 0!==t&&t,...c(e)})};class b extends d{_parse(e){this._def.coerce&&(e.data=Boolean(e.data));if(this._getType(e)!==n.ZodParsedType.boolean){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.boolean,received:t.parsedType}),r.INVALID}return(0,r.OK)(e.data)}}e.ZodBoolean=b,b.create=e=>new b({typeName:se.ZodBoolean,coerce:(null==e?void 0:e.coerce)||!1,...c(e)});class x extends d{_parse(e){this._def.coerce&&(e.data=new Date(e.data));if(this._getType(e)!==n.ZodParsedType.date){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.date,received:t.parsedType}),r.INVALID}if(isNaN(e.data.getTime())){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_date}),r.INVALID}const t=new r.ParseStatus;let a;for(const o of this._def.checks)"min"===o.kind?e.data.getTime()<o.value&&(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_small,message:o.message,inclusive:!0,exact:!1,minimum:o.value,type:"date"}),t.dirty()):"max"===o.kind?e.data.getTime()>o.value&&(a=this._getOrReturnCtx(e,a),(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_big,message:o.message,inclusive:!0,exact:!1,maximum:o.value,type:"date"}),t.dirty()):n.util.assertNever(o);return{status:t.value,value:new Date(e.data.getTime())}}_addCheck(e){return new x({...this._def,checks:[...this._def.checks,e]})}min(e,t){return this._addCheck({kind:"min",value:e.getTime(),message:a.errorUtil.toString(t)})}max(e,t){return this._addCheck({kind:"max",value:e.getTime(),message:a.errorUtil.toString(t)})}get minDate(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return null!=e?new Date(e):null}get maxDate(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return null!=e?new Date(e):null}}e.ZodDate=x,x.create=e=>new x({checks:[],coerce:(null==e?void 0:e.coerce)||!1,typeName:se.ZodDate,...c(e)});class P extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.symbol){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.symbol,received:t.parsedType}),r.INVALID}return(0,r.OK)(e.data)}}e.ZodSymbol=P,P.create=e=>new P({typeName:se.ZodSymbol,...c(e)});class I extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.undefined){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.undefined,received:t.parsedType}),r.INVALID}return(0,r.OK)(e.data)}}e.ZodUndefined=I,I.create=e=>new I({typeName:se.ZodUndefined,...c(e)});class k extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.null){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.null,received:t.parsedType}),r.INVALID}return(0,r.OK)(e.data)}}e.ZodNull=k,k.create=e=>new k({typeName:se.ZodNull,...c(e)});class R extends d{constructor(){super(...arguments),this._any=!0}_parse(e){return(0,r.OK)(e.data)}}e.ZodAny=R,R.create=e=>new R({typeName:se.ZodAny,...c(e)});class E extends d{constructor(){super(...arguments),this._unknown=!0}_parse(e){return(0,r.OK)(e.data)}}e.ZodUnknown=E,E.create=e=>new E({typeName:se.ZodUnknown,...c(e)});class _ extends d{_parse(e){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.never,received:t.parsedType}),r.INVALID}}e.ZodNever=_,_.create=e=>new _({typeName:se.ZodNever,...c(e)});class T extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.undefined){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.void,received:t.parsedType}),r.INVALID}return(0,r.OK)(e.data)}}e.ZodVoid=T,T.create=e=>new T({typeName:se.ZodVoid,...c(e)});class N extends d{_parse(e){const{ctx:t,status:a}=this._processInputParams(e),i=this._def;if(t.parsedType!==n.ZodParsedType.array)return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.array,received:t.parsedType}),r.INVALID;if(null!==i.exactLength){const e=t.data.length>i.exactLength.value,n=t.data.length<i.exactLength.value;(e||n)&&((0,r.addIssueToContext)(t,{code:e?s.ZodIssueCode.too_big:s.ZodIssueCode.too_small,minimum:n?i.exactLength.value:void 0,maximum:e?i.exactLength.value:void 0,type:"array",inclusive:!0,exact:!0,message:i.exactLength.message}),a.dirty())}if(null!==i.minLength&&t.data.length<i.minLength.value&&((0,r.addIssueToContext)(t,{code:s.ZodIssueCode.too_small,minimum:i.minLength.value,type:"array",inclusive:!0,exact:!1,message:i.minLength.message}),a.dirty()),null!==i.maxLength&&t.data.length>i.maxLength.value&&((0,r.addIssueToContext)(t,{code:s.ZodIssueCode.too_big,maximum:i.maxLength.value,type:"array",inclusive:!0,exact:!1,message:i.maxLength.message}),a.dirty()),t.common.async)return Promise.all([...t.data].map(((e,a)=>i.type._parseAsync(new o(t,e,t.path,a))))).then((e=>r.ParseStatus.mergeArray(a,e)));const c=[...t.data].map(((e,a)=>i.type._parseSync(new o(t,e,t.path,a))));return r.ParseStatus.mergeArray(a,c)}get element(){return this._def.type}min(e,t){return new N({...this._def,minLength:{value:e,message:a.errorUtil.toString(t)}})}max(e,t){return new N({...this._def,maxLength:{value:e,message:a.errorUtil.toString(t)}})}length(e,t){return new N({...this._def,exactLength:{value:e,message:a.errorUtil.toString(t)}})}nonempty(e){return this.min(1,e)}}function j(e){if(e instanceof O){const t={};for(const a in e.shape){const r=e.shape[a];t[a]=X.create(j(r))}return new O({...e._def,shape:()=>t})}return e instanceof N?new N({...e._def,type:j(e.element)}):e instanceof X?X.create(j(e.unwrap())):e instanceof Y?Y.create(j(e.unwrap())):e instanceof L?L.create(e.items.map((e=>j(e)))):e}e.ZodArray=N,N.create=(e,t)=>new N({type:e,minLength:null,maxLength:null,exactLength:null,typeName:se.ZodArray,...c(t)});class O extends d{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=this.extend}_getCached(){if(null!==this._cached)return this._cached;const e=this._def.shape(),t=n.util.objectKeys(e);return this._cached={shape:e,keys:t}}_parse(e){if(this._getType(e)!==n.ZodParsedType.object){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.object,received:t.parsedType}),r.INVALID}const{status:t,ctx:a}=this._processInputParams(e),{shape:i,keys:c}=this._getCached(),d=[];if(!(this._def.catchall instanceof _&&"strip"===this._def.unknownKeys))for(const e in a.data)c.includes(e)||d.push(e);const u=[];for(const e of c){const t=i[e],r=a.data[e];u.push({key:{status:"valid",value:e},value:t._parse(new o(a,r,a.path,e)),alwaysSet:e in a.data})}if(this._def.catchall instanceof _){const e=this._def.unknownKeys;if("passthrough"===e)for(const e of d)u.push({key:{status:"valid",value:e},value:{status:"valid",value:a.data[e]}});else if("strict"===e)d.length>0&&((0,r.addIssueToContext)(a,{code:s.ZodIssueCode.unrecognized_keys,keys:d}),t.dirty());else if("strip"!==e)throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{const e=this._def.catchall;for(const t of d){const r=a.data[t];u.push({key:{status:"valid",value:t},value:e._parse(new o(a,r,a.path,t)),alwaysSet:t in a.data})}}return a.common.async?Promise.resolve().then((async()=>{const e=[];for(const t of u){const a=await t.key;e.push({key:a,value:await t.value,alwaysSet:t.alwaysSet})}return e})).then((e=>r.ParseStatus.mergeObjectSync(t,e))):r.ParseStatus.mergeObjectSync(t,u)}get shape(){return this._def.shape()}strict(e){return a.errorUtil.errToObj,new O({...this._def,unknownKeys:"strict",...void 0!==e?{errorMap:(t,r)=>{var n,s,o,i;const c=null!==(o=null===(s=(n=this._def).errorMap)||void 0===s?void 0:s.call(n,t,r).message)&&void 0!==o?o:r.defaultError;return"unrecognized_keys"===t.code?{message:null!==(i=a.errorUtil.errToObj(e).message)&&void 0!==i?i:c}:{message:c}}}:{}})}strip(){return new O({...this._def,unknownKeys:"strip"})}passthrough(){return new O({...this._def,unknownKeys:"passthrough"})}extend(e){return new O({...this._def,shape:()=>({...this._def.shape(),...e})})}merge(e){return new O({unknownKeys:e._def.unknownKeys,catchall:e._def.catchall,shape:()=>({...this._def.shape(),...e._def.shape()}),typeName:se.ZodObject})}setKey(e,t){return this.augment({[e]:t})}catchall(e){return new O({...this._def,catchall:e})}pick(e){const t={};return n.util.objectKeys(e).forEach((a=>{e[a]&&this.shape[a]&&(t[a]=this.shape[a])})),new O({...this._def,shape:()=>t})}omit(e){const t={};return n.util.objectKeys(this.shape).forEach((a=>{e[a]||(t[a]=this.shape[a])})),new O({...this._def,shape:()=>t})}deepPartial(){return j(this)}partial(e){const t={};return n.util.objectKeys(this.shape).forEach((a=>{const r=this.shape[a];e&&!e[a]?t[a]=r:t[a]=r.optional()})),new O({...this._def,shape:()=>t})}required(e){const t={};return n.util.objectKeys(this.shape).forEach((a=>{if(e&&!e[a])t[a]=this.shape[a];else{let e=this.shape[a];for(;e instanceof X;)e=e._def.innerType;t[a]=e}})),new O({...this._def,shape:()=>t})}keyof(){return K(n.util.objectKeys(this.shape))}}e.ZodObject=O,O.create=(e,t)=>new O({shape:()=>e,unknownKeys:"strip",catchall:_.create(),typeName:se.ZodObject,...c(t)}),O.strictCreate=(e,t)=>new O({shape:()=>e,unknownKeys:"strict",catchall:_.create(),typeName:se.ZodObject,...c(t)}),O.lazycreate=(e,t)=>new O({shape:e,unknownKeys:"strip",catchall:_.create(),typeName:se.ZodObject,...c(t)});class M extends d{_parse(e){const{ctx:t}=this._processInputParams(e),a=this._def.options;if(t.common.async)return Promise.all(a.map((async e=>{const a={...t,common:{...t.common,issues:[]},parent:null};return{result:await e._parseAsync({data:t.data,path:t.path,parent:a}),ctx:a}}))).then((function(e){for(const t of e)if("valid"===t.result.status)return t.result;for(const a of e)if("dirty"===a.result.status)return t.common.issues.push(...a.ctx.common.issues),a.result;const a=e.map((e=>new s.ZodError(e.ctx.common.issues)));return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_union,unionErrors:a}),r.INVALID}));{let e;const n=[];for(const r of a){const a={...t,common:{...t.common,issues:[]},parent:null},s=r._parseSync({data:t.data,path:t.path,parent:a});if("valid"===s.status)return s;"dirty"!==s.status||e||(e={result:s,ctx:a}),a.common.issues.length&&n.push(a.common.issues)}if(e)return t.common.issues.push(...e.ctx.common.issues),e.result;const o=n.map((e=>new s.ZodError(e)));return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_union,unionErrors:o}),r.INVALID}}get options(){return this._def.options}}e.ZodUnion=M,M.create=(e,t)=>new M({options:e,typeName:se.ZodUnion,...c(t)});const A=e=>e instanceof V?A(e.schema):e instanceof G?A(e.innerType()):e instanceof $?[e.value]:e instanceof H?e.options:e instanceof W?Object.keys(e.enum):e instanceof Q?A(e._def.innerType):e instanceof I?[void 0]:e instanceof k?[null]:null;class B extends d{_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==n.ZodParsedType.object)return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.object,received:t.parsedType}),r.INVALID;const a=this.discriminator,o=t.data[a],i=this.optionsMap.get(o);return i?t.common.async?i._parseAsync({data:t.data,path:t.path,parent:t}):i._parseSync({data:t.data,path:t.path,parent:t}):((0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_union_discriminator,options:Array.from(this.optionsMap.keys()),path:[a]}),r.INVALID)}get discriminator(){return this._def.discriminator}get options(){return this._def.options}get optionsMap(){return this._def.optionsMap}static create(e,t,a){const r=new Map;for(const a of t){const t=A(a.shape[e]);if(!t)throw new Error(`A discriminator value for key \`${e}\` could not be extracted from all schema options`);for(const n of t){if(r.has(n))throw new Error(`Discriminator property ${String(e)} has duplicate value ${String(n)}`);r.set(n,a)}}return new B({typeName:se.ZodDiscriminatedUnion,discriminator:e,options:t,optionsMap:r,...c(a)})}}function z(e,t){const a=(0,n.getParsedType)(e),r=(0,n.getParsedType)(t);if(e===t)return{valid:!0,data:e};if(a===n.ZodParsedType.object&&r===n.ZodParsedType.object){const a=n.util.objectKeys(t),r=n.util.objectKeys(e).filter((e=>-1!==a.indexOf(e))),s={...e,...t};for(const a of r){const r=z(e[a],t[a]);if(!r.valid)return{valid:!1};s[a]=r.data}return{valid:!0,data:s}}if(a===n.ZodParsedType.array&&r===n.ZodParsedType.array){if(e.length!==t.length)return{valid:!1};const a=[];for(let r=0;r<e.length;r++){const n=z(e[r],t[r]);if(!n.valid)return{valid:!1};a.push(n.data)}return{valid:!0,data:a}}return a===n.ZodParsedType.date&&r===n.ZodParsedType.date&&+e==+t?{valid:!0,data:e}:{valid:!1}}e.ZodDiscriminatedUnion=B;class D extends d{_parse(e){const{status:t,ctx:a}=this._processInputParams(e),n=(e,n)=>{if((0,r.isAborted)(e)||(0,r.isAborted)(n))return r.INVALID;const o=z(e.value,n.value);return o.valid?(((0,r.isDirty)(e)||(0,r.isDirty)(n))&&t.dirty(),{status:t.value,value:o.data}):((0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_intersection_types}),r.INVALID)};return a.common.async?Promise.all([this._def.left._parseAsync({data:a.data,path:a.path,parent:a}),this._def.right._parseAsync({data:a.data,path:a.path,parent:a})]).then((([e,t])=>n(e,t))):n(this._def.left._parseSync({data:a.data,path:a.path,parent:a}),this._def.right._parseSync({data:a.data,path:a.path,parent:a}))}}e.ZodIntersection=D,D.create=(e,t,a)=>new D({left:e,right:t,typeName:se.ZodIntersection,...c(a)});class L extends d{_parse(e){const{status:t,ctx:a}=this._processInputParams(e);if(a.parsedType!==n.ZodParsedType.array)return(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.array,received:a.parsedType}),r.INVALID;if(a.data.length<this._def.items.length)return(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_small,minimum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),r.INVALID;!this._def.rest&&a.data.length>this._def.items.length&&((0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_big,maximum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),t.dirty());const i=[...a.data].map(((e,t)=>{const r=this._def.items[t]||this._def.rest;return r?r._parse(new o(a,e,a.path,t)):null})).filter((e=>!!e));return a.common.async?Promise.all(i).then((e=>r.ParseStatus.mergeArray(t,e))):r.ParseStatus.mergeArray(t,i)}get items(){return this._def.items}rest(e){return new L({...this._def,rest:e})}}e.ZodTuple=L,L.create=(e,t)=>{if(!Array.isArray(e))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new L({items:e,typeName:se.ZodTuple,rest:null,...c(t)})};class Z extends d{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){const{status:t,ctx:a}=this._processInputParams(e);if(a.parsedType!==n.ZodParsedType.object)return(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.object,received:a.parsedType}),r.INVALID;const i=[],c=this._def.keyType,d=this._def.valueType;for(const e in a.data)i.push({key:c._parse(new o(a,e,a.path,e)),value:d._parse(new o(a,a.data[e],a.path,e))});return a.common.async?r.ParseStatus.mergeObjectAsync(t,i):r.ParseStatus.mergeObjectSync(t,i)}get element(){return this._def.valueType}static create(e,t,a){return new Z(t instanceof d?{keyType:e,valueType:t,typeName:se.ZodRecord,...c(a)}:{keyType:S.create(),valueType:e,typeName:se.ZodRecord,...c(t)})}}e.ZodRecord=Z;class U extends d{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){const{status:t,ctx:a}=this._processInputParams(e);if(a.parsedType!==n.ZodParsedType.map)return(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.map,received:a.parsedType}),r.INVALID;const i=this._def.keyType,c=this._def.valueType,d=[...a.data.entries()].map((([e,t],r)=>({key:i._parse(new o(a,e,a.path,[r,"key"])),value:c._parse(new o(a,t,a.path,[r,"value"]))})));if(a.common.async){const e=new Map;return Promise.resolve().then((async()=>{for(const a of d){const n=await a.key,s=await a.value;if("aborted"===n.status||"aborted"===s.status)return r.INVALID;"dirty"!==n.status&&"dirty"!==s.status||t.dirty(),e.set(n.value,s.value)}return{status:t.value,value:e}}))}{const e=new Map;for(const a of d){const n=a.key,s=a.value;if("aborted"===n.status||"aborted"===s.status)return r.INVALID;"dirty"!==n.status&&"dirty"!==s.status||t.dirty(),e.set(n.value,s.value)}return{status:t.value,value:e}}}}e.ZodMap=U,U.create=(e,t,a)=>new U({valueType:t,keyType:e,typeName:se.ZodMap,...c(a)});class F extends d{_parse(e){const{status:t,ctx:a}=this._processInputParams(e);if(a.parsedType!==n.ZodParsedType.set)return(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.set,received:a.parsedType}),r.INVALID;const i=this._def;null!==i.minSize&&a.data.size<i.minSize.value&&((0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_small,minimum:i.minSize.value,type:"set",inclusive:!0,exact:!1,message:i.minSize.message}),t.dirty()),null!==i.maxSize&&a.data.size>i.maxSize.value&&((0,r.addIssueToContext)(a,{code:s.ZodIssueCode.too_big,maximum:i.maxSize.value,type:"set",inclusive:!0,exact:!1,message:i.maxSize.message}),t.dirty());const c=this._def.valueType;function d(e){const a=new Set;for(const n of e){if("aborted"===n.status)return r.INVALID;"dirty"===n.status&&t.dirty(),a.add(n.value)}return{status:t.value,value:a}}const u=[...a.data.values()].map(((e,t)=>c._parse(new o(a,e,a.path,t))));return a.common.async?Promise.all(u).then((e=>d(e))):d(u)}min(e,t){return new F({...this._def,minSize:{value:e,message:a.errorUtil.toString(t)}})}max(e,t){return new F({...this._def,maxSize:{value:e,message:a.errorUtil.toString(t)}})}size(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}e.ZodSet=F,F.create=(e,t)=>new F({valueType:e,minSize:null,maxSize:null,typeName:se.ZodSet,...c(t)});class q extends d{constructor(){super(...arguments),this.validate=this.implement}_parse(e){const{ctx:a}=this._processInputParams(e);if(a.parsedType!==n.ZodParsedType.function)return(0,r.addIssueToContext)(a,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.function,received:a.parsedType}),r.INVALID;function o(e,n){return(0,r.makeIssue)({data:e,path:a.path,errorMaps:[a.common.contextualErrorMap,a.schemaErrorMap,(0,t.getErrorMap)(),t.defaultErrorMap].filter((e=>!!e)),issueData:{code:s.ZodIssueCode.invalid_arguments,argumentsError:n}})}function i(e,n){return(0,r.makeIssue)({data:e,path:a.path,errorMaps:[a.common.contextualErrorMap,a.schemaErrorMap,(0,t.getErrorMap)(),t.defaultErrorMap].filter((e=>!!e)),issueData:{code:s.ZodIssueCode.invalid_return_type,returnTypeError:n}})}const c={errorMap:a.common.contextualErrorMap},d=a.data;if(this._def.returns instanceof J){const e=this;return(0,r.OK)((async function(...t){const a=new s.ZodError([]),r=await e._def.args.parseAsync(t,c).catch((e=>{throw a.addIssue(o(t,e)),a})),n=await Reflect.apply(d,this,r),u=await e._def.returns._def.type.parseAsync(n,c).catch((e=>{throw a.addIssue(i(n,e)),a}));return u}))}{const e=this;return(0,r.OK)((function(...t){const a=e._def.args.safeParse(t,c);if(!a.success)throw new s.ZodError([o(t,a.error)]);const r=Reflect.apply(d,this,a.data),n=e._def.returns.safeParse(r,c);if(!n.success)throw new s.ZodError([i(r,n.error)]);return n.data}))}}parameters(){return this._def.args}returnType(){return this._def.returns}args(...e){return new q({...this._def,args:L.create(e).rest(E.create())})}returns(e){return new q({...this._def,returns:e})}implement(e){return this.parse(e)}strictImplement(e){return this.parse(e)}static create(e,t,a){return new q({args:e||L.create([]).rest(E.create()),returns:t||E.create(),typeName:se.ZodFunction,...c(a)})}}e.ZodFunction=q;class V extends d{get schema(){return this._def.getter()}_parse(e){const{ctx:t}=this._processInputParams(e);return this._def.getter()._parse({data:t.data,path:t.path,parent:t})}}e.ZodLazy=V,V.create=(e,t)=>new V({getter:e,typeName:se.ZodLazy,...c(t)});class $ extends d{_parse(e){if(e.data!==this._def.value){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{received:t.data,code:s.ZodIssueCode.invalid_literal,expected:this._def.value}),r.INVALID}return{status:"valid",value:e.data}}get value(){return this._def.value}}function K(e,t){return new H({values:e,typeName:se.ZodEnum,...c(t)})}e.ZodLiteral=$,$.create=(e,t)=>new $({value:e,typeName:se.ZodLiteral,...c(t)});class H extends d{_parse(e){if("string"!=typeof e.data){const t=this._getOrReturnCtx(e),a=this._def.values;return(0,r.addIssueToContext)(t,{expected:n.util.joinValues(a),received:t.parsedType,code:s.ZodIssueCode.invalid_type}),r.INVALID}if(-1===this._def.values.indexOf(e.data)){const t=this._getOrReturnCtx(e),a=this._def.values;return(0,r.addIssueToContext)(t,{received:t.data,code:s.ZodIssueCode.invalid_enum_value,options:a}),r.INVALID}return(0,r.OK)(e.data)}get options(){return this._def.values}get enum(){const e={};for(const t of this._def.values)e[t]=t;return e}get Values(){const e={};for(const t of this._def.values)e[t]=t;return e}get Enum(){const e={};for(const t of this._def.values)e[t]=t;return e}extract(e){return H.create(e)}exclude(e){return H.create(this.options.filter((t=>!e.includes(t))))}}e.ZodEnum=H,H.create=K;class W extends d{_parse(e){const t=n.util.getValidEnumValues(this._def.values),a=this._getOrReturnCtx(e);if(a.parsedType!==n.ZodParsedType.string&&a.parsedType!==n.ZodParsedType.number){const e=n.util.objectValues(t);return(0,r.addIssueToContext)(a,{expected:n.util.joinValues(e),received:a.parsedType,code:s.ZodIssueCode.invalid_type}),r.INVALID}if(-1===t.indexOf(e.data)){const e=n.util.objectValues(t);return(0,r.addIssueToContext)(a,{received:a.data,code:s.ZodIssueCode.invalid_enum_value,options:e}),r.INVALID}return(0,r.OK)(e.data)}get enum(){return this._def.values}}e.ZodNativeEnum=W,W.create=(e,t)=>new W({values:e,typeName:se.ZodNativeEnum,...c(t)});class J extends d{unwrap(){return this._def.type}_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==n.ZodParsedType.promise&&!1===t.common.async)return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.promise,received:t.parsedType}),r.INVALID;const a=t.parsedType===n.ZodParsedType.promise?t.data:Promise.resolve(t.data);return(0,r.OK)(a.then((e=>this._def.type.parseAsync(e,{path:t.path,errorMap:t.common.contextualErrorMap}))))}}e.ZodPromise=J,J.create=(e,t)=>new J({type:e,typeName:se.ZodPromise,...c(t)});class G extends d{innerType(){return this._def.schema}sourceType(){return this._def.schema._def.typeName===se.ZodEffects?this._def.schema.sourceType():this._def.schema}_parse(e){const{status:t,ctx:a}=this._processInputParams(e),s=this._def.effect||null,o={addIssue:e=>{(0,r.addIssueToContext)(a,e),e.fatal?t.abort():t.dirty()},get path(){return a.path}};if(o.addIssue=o.addIssue.bind(o),"preprocess"===s.type){const e=s.transform(a.data,o);return a.common.issues.length?{status:"dirty",value:a.data}:a.common.async?Promise.resolve(e).then((e=>this._def.schema._parseAsync({data:e,path:a.path,parent:a}))):this._def.schema._parseSync({data:e,path:a.path,parent:a})}if("refinement"===s.type){const e=e=>{const t=s.refinement(e,o);if(a.common.async)return Promise.resolve(t);if(t instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return e};if(!1===a.common.async){const n=this._def.schema._parseSync({data:a.data,path:a.path,parent:a});return"aborted"===n.status?r.INVALID:("dirty"===n.status&&t.dirty(),e(n.value),{status:t.value,value:n.value})}return this._def.schema._parseAsync({data:a.data,path:a.path,parent:a}).then((a=>"aborted"===a.status?r.INVALID:("dirty"===a.status&&t.dirty(),e(a.value).then((()=>({status:t.value,value:a.value}))))))}if("transform"===s.type){if(!1===a.common.async){const e=this._def.schema._parseSync({data:a.data,path:a.path,parent:a});if(!(0,r.isValid)(e))return e;const n=s.transform(e.value,o);if(n instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:t.value,value:n}}return this._def.schema._parseAsync({data:a.data,path:a.path,parent:a}).then((e=>(0,r.isValid)(e)?Promise.resolve(s.transform(e.value,o)).then((e=>({status:t.value,value:e}))):e))}n.util.assertNever(s)}}e.ZodEffects=G,e.ZodTransformer=G,G.create=(e,t,a)=>new G({schema:e,typeName:se.ZodEffects,effect:t,...c(a)}),G.createWithPreprocess=(e,t,a)=>new G({schema:t,effect:{type:"preprocess",transform:e},typeName:se.ZodEffects,...c(a)});class X extends d{_parse(e){return this._getType(e)===n.ZodParsedType.undefined?(0,r.OK)(void 0):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}e.ZodOptional=X,X.create=(e,t)=>new X({innerType:e,typeName:se.ZodOptional,...c(t)});class Y extends d{_parse(e){return this._getType(e)===n.ZodParsedType.null?(0,r.OK)(null):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}e.ZodNullable=Y,Y.create=(e,t)=>new Y({innerType:e,typeName:se.ZodNullable,...c(t)});class Q extends d{_parse(e){const{ctx:t}=this._processInputParams(e);let a=t.data;return t.parsedType===n.ZodParsedType.undefined&&(a=this._def.defaultValue()),this._def.innerType._parse({data:a,path:t.path,parent:t})}removeDefault(){return this._def.innerType}}e.ZodDefault=Q,Q.create=(e,t)=>new Q({innerType:e,typeName:se.ZodDefault,defaultValue:"function"==typeof t.default?t.default:()=>t.default,...c(t)});class ee extends d{_parse(e){const{ctx:t}=this._processInputParams(e),a={...t,common:{...t.common,issues:[]}},n=this._def.innerType._parse({data:a.data,path:a.path,parent:{...a}});return(0,r.isAsync)(n)?n.then((e=>({status:"valid",value:"valid"===e.status?e.value:this._def.catchValue({get error(){return new s.ZodError(a.common.issues)},input:a.data})}))):{status:"valid",value:"valid"===n.status?n.value:this._def.catchValue({get error(){return new s.ZodError(a.common.issues)},input:a.data})}}removeCatch(){return this._def.innerType}}e.ZodCatch=ee,ee.create=(e,t)=>new ee({innerType:e,typeName:se.ZodCatch,catchValue:"function"==typeof t.catch?t.catch:()=>t.catch,...c(t)});class te extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.nan){const t=this._getOrReturnCtx(e);return(0,r.addIssueToContext)(t,{code:s.ZodIssueCode.invalid_type,expected:n.ZodParsedType.nan,received:t.parsedType}),r.INVALID}return{status:"valid",value:e.data}}}e.ZodNaN=te,te.create=e=>new te({typeName:se.ZodNaN,...c(e)}),e.BRAND=Symbol("zod_brand");class ae extends d{_parse(e){const{ctx:t}=this._processInputParams(e),a=t.data;return this._def.type._parse({data:a,path:t.path,parent:t})}unwrap(){return this._def.type}}e.ZodBranded=ae;class re extends d{_parse(e){const{status:t,ctx:a}=this._processInputParams(e);if(a.common.async){return(async()=>{const e=await this._def.in._parseAsync({data:a.data,path:a.path,parent:a});return"aborted"===e.status?r.INVALID:"dirty"===e.status?(t.dirty(),(0,r.DIRTY)(e.value)):this._def.out._parseAsync({data:e.value,path:a.path,parent:a})})()}{const e=this._def.in._parseSync({data:a.data,path:a.path,parent:a});return"aborted"===e.status?r.INVALID:"dirty"===e.status?(t.dirty(),{status:"dirty",value:e.value}):this._def.out._parseSync({data:e.value,path:a.path,parent:a})}}static create(e,t){return new re({in:e,out:t,typeName:se.ZodPipeline})}}e.ZodPipeline=re;class ne extends d{_parse(e){const t=this._def.innerType._parse(e);return(0,r.isValid)(t)&&(t.value=Object.freeze(t.value)),t}}e.ZodReadonly=ne,ne.create=(e,t)=>new ne({innerType:e,typeName:se.ZodReadonly,...c(t)});var se;e.custom=(e,t={},a)=>e?R.create().superRefine(((r,n)=>{var s,o;if(!e(r)){const e="function"==typeof t?t(r):"string"==typeof t?{message:t}:t,i=null===(o=null!==(s=e.fatal)&&void 0!==s?s:a)||void 0===o||o,c="string"==typeof e?{message:e}:e;n.addIssue({code:"custom",...c,fatal:i})}})):R.create(),e.late={object:O.lazycreate},function(e){e.ZodString="ZodString",e.ZodNumber="ZodNumber",e.ZodNaN="ZodNaN",e.ZodBigInt="ZodBigInt",e.ZodBoolean="ZodBoolean",e.ZodDate="ZodDate",e.ZodSymbol="ZodSymbol",e.ZodUndefined="ZodUndefined",e.ZodNull="ZodNull",e.ZodAny="ZodAny",e.ZodUnknown="ZodUnknown",e.ZodNever="ZodNever",e.ZodVoid="ZodVoid",e.ZodArray="ZodArray",e.ZodObject="ZodObject",e.ZodUnion="ZodUnion",e.ZodDiscriminatedUnion="ZodDiscriminatedUnion",e.ZodIntersection="ZodIntersection",e.ZodTuple="ZodTuple",e.ZodRecord="ZodRecord",e.ZodMap="ZodMap",e.ZodSet="ZodSet",e.ZodFunction="ZodFunction",e.ZodLazy="ZodLazy",e.ZodLiteral="ZodLiteral",e.ZodEnum="ZodEnum",e.ZodEffects="ZodEffects",e.ZodNativeEnum="ZodNativeEnum",e.ZodOptional="ZodOptional",e.ZodNullable="ZodNullable",e.ZodDefault="ZodDefault",e.ZodCatch="ZodCatch",e.ZodPromise="ZodPromise",e.ZodBranded="ZodBranded",e.ZodPipeline="ZodPipeline",e.ZodReadonly="ZodReadonly"}(se=e.ZodFirstPartyTypeKind||(e.ZodFirstPartyTypeKind={}));e.instanceof=(t,a={message:`Input not instance of ${t.name}`})=>(0,e.custom)((e=>e instanceof t),a);const oe=S.create;e.string=oe;const ie=w.create;e.number=ie;const ce=te.create;e.nan=ce;const de=C.create;e.bigint=de;const ue=b.create;e.boolean=ue;const le=x.create;e.date=le;const he=P.create;e.symbol=he;const pe=I.create;e.undefined=pe;const me=k.create;e.null=me;const fe=R.create;e.any=fe;const ge=E.create;e.unknown=ge;const ye=_.create;e.never=ye;const Se=T.create;e.void=Se;const ve=N.create;e.array=ve;const we=O.create;e.object=we;const Ce=O.strictCreate;e.strictObject=Ce;const be=M.create;e.union=be;const xe=B.create;e.discriminatedUnion=xe;const Pe=D.create;e.intersection=Pe;const Ie=L.create;e.tuple=Ie;const ke=Z.create;e.record=ke;const Re=U.create;e.map=Re;const Ee=F.create;e.set=Ee;const _e=q.create;e.function=_e;const Te=V.create;e.lazy=Te;const Ne=$.create;e.literal=Ne;const je=H.create;e.enum=je;const Oe=W.create;e.nativeEnum=Oe;const Me=J.create;e.promise=Me;const Ae=G.create;e.effect=Ae,e.transformer=Ae;const Be=X.create;e.optional=Be;const ze=Y.create;e.nullable=ze;const De=G.createWithPreprocess;e.preprocess=De;const Le=re.create;e.pipeline=Le;e.ostring=()=>oe().optional();e.onumber=()=>ie().optional();e.oboolean=()=>ue().optional(),e.coerce={string:e=>S.create({...e,coerce:!0}),number:e=>w.create({...e,coerce:!0}),boolean:e=>b.create({...e,coerce:!0}),bigint:e=>C.create({...e,coerce:!0}),date:e=>x.create({...e,coerce:!0})},e.NEVER=r.INVALID}(Rn),function(t){var a=e&&e.__createBinding||(Object.create?function(e,t,a,r){void 0===r&&(r=a),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[a]}})}:function(e,t,a,r){void 0===r&&(r=a),e[r]=t[a]}),r=e&&e.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||a(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),r(pn,t),r(Pn,t),r(In,t),r(fn,t),r(Rn,t),r(gn,t)}(hn),function(t){var a=e&&e.__createBinding||(Object.create?function(e,t,a,r){void 0===r&&(r=a),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[a]}})}:function(e,t,a,r){void 0===r&&(r=a),e[r]=t[a]}),r=e&&e.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=e&&e.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&a(t,e,n);return r(t,e),t},s=e&&e.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||a(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),t.z=void 0;const o=n(hn);t.z=o,s(hn,t),t.default=o}(ln);var _n={},Tn=e&&e.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(_n,"__esModule",{value:!0}),_n.Permissions=_n.PermissionsCommandSchema=void 0;const Nn=Tn(ln);var jn;_n.PermissionsCommandSchema=Nn.default.lazy((()=>jn.SetPermissionSchema)),function(e){e.PermissionDescriptorSchema=Nn.default.lazy((()=>Nn.default.object({name:Nn.default.string()})))}(jn||(_n.Permissions=jn={})),function(e){e.PermissionStateSchema=Nn.default.lazy((()=>Nn.default.enum(["granted","denied","prompt"])))}(jn||(_n.Permissions=jn={})),function(e){e.SetPermissionSchema=Nn.default.lazy((()=>Nn.default.object({method:Nn.default.literal("permissions.setPermission"),params:e.SetPermissionParametersSchema})))}(jn||(_n.Permissions=jn={})),function(e){e.SetPermissionParametersSchema=Nn.default.lazy((()=>Nn.default.object({descriptor:e.PermissionDescriptorSchema,state:e.PermissionStateSchema,origin:Nn.default.string()})))}(jn||(_n.Permissions=jn={}));var On={};!function(t){var a=e&&e.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Input=t.InputCommandSchema=t.Log=t.LogEventSchema=t.Storage=t.StorageResultSchema=t.StorageCommandSchema=t.Script=t.ScriptResultSchema=t.ScriptCommandSchema=t.ScriptEventSchema=t.Network=t.NetworkResultSchema=t.NetworkEventSchema=t.NetworkCommandSchema=t.BrowsingContext=t.BrowsingContextResultSchema=t.BrowsingContextEventSchema=t.BrowsingContextCommandSchema=t.Browser=t.BrowserResultSchema=t.BrowserCommandSchema=t.SessionResultSchema=t.Session=t.SessionCommandSchema=t.ErrorCodeSchema=t.JsUintSchema=t.JsIntSchema=t.ExtensibleSchema=t.EmptyResultSchema=t.ErrorResponseSchema=t.MessageSchema=t.EmptyParamsSchema=t.ResultDataSchema=t.CommandDataSchema=t.EventDataSchema=t.CommandResponseSchema=t.CommandSchema=t.EventSchema=void 0;const r=a(ln);var n,s,o,i,c,d,u,l;t.EventSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("event")}).and(t.EventDataSchema).and(t.ExtensibleSchema))),t.CommandSchema=r.default.lazy((()=>r.default.object({id:t.JsUintSchema}).and(t.CommandDataSchema).and(t.ExtensibleSchema))),t.CommandResponseSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("success"),id:t.JsUintSchema,result:t.ResultDataSchema}).and(t.ExtensibleSchema))),t.EventDataSchema=r.default.lazy((()=>r.default.union([t.BrowsingContextEventSchema,t.LogEventSchema,t.NetworkEventSchema,t.ScriptEventSchema]))),t.CommandDataSchema=r.default.lazy((()=>r.default.union([t.BrowserCommandSchema,t.BrowsingContextCommandSchema,t.InputCommandSchema,t.NetworkCommandSchema,t.ScriptCommandSchema,t.SessionCommandSchema,t.StorageCommandSchema]))),t.ResultDataSchema=r.default.lazy((()=>r.default.union([t.BrowsingContextResultSchema,t.EmptyResultSchema,t.NetworkResultSchema,t.ScriptResultSchema,t.SessionResultSchema,t.StorageResultSchema]))),t.EmptyParamsSchema=r.default.lazy((()=>t.ExtensibleSchema)),t.MessageSchema=r.default.lazy((()=>r.default.union([t.CommandResponseSchema,t.ErrorResponseSchema,t.EventSchema]))),t.ErrorResponseSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("error"),id:r.default.union([t.JsUintSchema,r.default.null()]),error:t.ErrorCodeSchema,message:r.default.string(),stacktrace:r.default.string().optional()}).and(t.ExtensibleSchema))),t.EmptyResultSchema=r.default.lazy((()=>t.ExtensibleSchema)),t.ExtensibleSchema=r.default.lazy((()=>r.default.record(r.default.string(),r.default.any()))),t.JsIntSchema=r.default.lazy((()=>r.default.number().int().gte(-9007199254740991).lte(9007199254740991))),t.JsUintSchema=r.default.lazy((()=>r.default.number().int().nonnegative().gte(0).lte(9007199254740991))),t.ErrorCodeSchema=r.default.lazy((()=>r.default.enum(["invalid argument","invalid session id","move target out of bounds","no such alert","no such element","no such frame","no such handle","no such history entry","no such intercept","no such node","no such request","no such script","no such storage partition","no such user context","session not created","unable to capture screen","unable to close browser","unable to set cookie","unable to set file input","underspecified storage partition","unknown command","unknown error","unsupported operation"]))),t.SessionCommandSchema=r.default.lazy((()=>r.default.union([n.EndSchema,n.NewSchema,n.StatusSchema,n.SubscribeSchema,n.UnsubscribeSchema]))),function(e){e.ProxyConfigurationSchema=r.default.lazy((()=>r.default.union([e.AutodetectProxyConfigurationSchema,e.DirectProxyConfigurationSchema,e.ManualProxyConfigurationSchema,e.PacProxyConfigurationSchema,e.SystemProxyConfigurationSchema,r.default.object({})])))}(n||(t.Session=n={})),t.SessionResultSchema=r.default.lazy((()=>r.default.union([n.NewResultSchema,n.StatusResultSchema]))),function(e){e.CapabilitiesRequestSchema=r.default.lazy((()=>r.default.object({alwaysMatch:e.CapabilityRequestSchema.optional(),firstMatch:r.default.array(e.CapabilityRequestSchema).optional()})))}(n||(t.Session=n={})),function(e){e.CapabilityRequestSchema=r.default.lazy((()=>r.default.object({acceptInsecureCerts:r.default.boolean().optional(),browserName:r.default.string().optional(),browserVersion:r.default.string().optional(),platformName:r.default.string().optional(),proxy:e.ProxyConfigurationSchema.optional(),webSocketUrl:r.default.boolean().optional()}).and(t.ExtensibleSchema)))}(n||(t.Session=n={})),function(e){e.AutodetectProxyConfigurationSchema=r.default.lazy((()=>r.default.object({proxyType:r.default.literal("autodetect")}).and(t.ExtensibleSchema)))}(n||(t.Session=n={})),function(e){e.DirectProxyConfigurationSchema=r.default.lazy((()=>r.default.object({proxyType:r.default.literal("direct")}).and(t.ExtensibleSchema)))}(n||(t.Session=n={})),function(e){e.ManualProxyConfigurationSchema=r.default.lazy((()=>r.default.object({proxyType:r.default.literal("manual"),ftpProxy:r.default.string().optional(),httpProxy:r.default.string().optional(),sslProxy:r.default.string().optional()}).and(e.SocksProxyConfigurationSchema.or(r.default.object({}))).and(r.default.object({noProxy:r.default.array(r.default.string()).optional()})).and(t.ExtensibleSchema)))}(n||(t.Session=n={})),function(e){e.SocksProxyConfigurationSchema=r.default.lazy((()=>r.default.object({socksProxy:r.default.string(),socksVersion:r.default.number().int().nonnegative().gte(0).lte(255)})))}(n||(t.Session=n={})),function(e){e.PacProxyConfigurationSchema=r.default.lazy((()=>r.default.object({proxyType:r.default.literal("pac"),proxyAutoconfigUrl:r.default.string()}).and(t.ExtensibleSchema)))}(n||(t.Session=n={})),function(e){e.SystemProxyConfigurationSchema=r.default.lazy((()=>r.default.object({proxyType:r.default.literal("system")}).and(t.ExtensibleSchema)))}(n||(t.Session=n={})),function(e){e.SubscriptionRequestSchema=r.default.lazy((()=>r.default.object({events:r.default.array(r.default.string()),contexts:r.default.array(o.BrowsingContextSchema).optional()})))}(n||(t.Session=n={})),function(e){e.StatusSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("session.status"),params:t.EmptyParamsSchema})))}(n||(t.Session=n={})),function(e){e.StatusResultSchema=r.default.lazy((()=>r.default.object({ready:r.default.boolean(),message:r.default.string()})))}(n||(t.Session=n={})),function(e){e.NewSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("session.new"),params:e.NewParametersSchema})))}(n||(t.Session=n={})),function(e){e.NewParametersSchema=r.default.lazy((()=>r.default.object({capabilities:e.CapabilitiesRequestSchema})))}(n||(t.Session=n={})),function(e){e.NewResultSchema=r.default.lazy((()=>r.default.object({sessionId:r.default.string(),capabilities:r.default.object({acceptInsecureCerts:r.default.boolean(),browserName:r.default.string(),browserVersion:r.default.string(),platformName:r.default.string(),setWindowRect:r.default.boolean(),proxy:e.ProxyConfigurationSchema.optional(),webSocketUrl:r.default.string().optional()}).and(t.ExtensibleSchema)})))}(n||(t.Session=n={})),function(e){e.EndSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("session.end"),params:t.EmptyParamsSchema})))}(n||(t.Session=n={})),function(e){e.SubscribeSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("session.subscribe"),params:e.SubscriptionRequestSchema})))}(n||(t.Session=n={})),function(e){e.UnsubscribeSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("session.unsubscribe"),params:e.SubscriptionRequestSchema})))}(n||(t.Session=n={})),t.BrowserCommandSchema=r.default.lazy((()=>r.default.union([s.CloseSchema,s.CreateUserContextSchema,s.GetUserContextsSchema,s.RemoveUserContextSchema]))),t.BrowserResultSchema=r.default.lazy((()=>r.default.union([s.CreateUserContextResultSchema,s.GetUserContextsResultSchema]))),function(e){e.UserContextSchema=r.default.lazy((()=>r.default.string()))}(s||(t.Browser=s={})),function(e){e.UserContextInfoSchema=r.default.lazy((()=>r.default.object({userContext:e.UserContextSchema})))}(s||(t.Browser=s={})),function(e){e.CloseSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browser.close"),params:t.EmptyParamsSchema})))}(s||(t.Browser=s={})),function(e){e.CreateUserContextSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browser.createUserContext"),params:t.EmptyParamsSchema})))}(s||(t.Browser=s={})),function(e){e.CreateUserContextResultSchema=r.default.lazy((()=>e.UserContextInfoSchema))}(s||(t.Browser=s={})),function(e){e.GetUserContextsSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browser.getUserContexts"),params:t.EmptyParamsSchema})))}(s||(t.Browser=s={})),function(e){e.GetUserContextsResultSchema=r.default.lazy((()=>r.default.object({userContexts:r.default.array(e.UserContextInfoSchema).min(1)})))}(s||(t.Browser=s={})),function(e){e.RemoveUserContextSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browser.removeUserContext"),params:r.default.object({userContext:e.UserContextSchema})})))}(s||(t.Browser=s={})),t.BrowsingContextCommandSchema=r.default.lazy((()=>r.default.union([o.ActivateSchema,o.CaptureScreenshotSchema,o.CloseSchema,o.CreateSchema,o.GetTreeSchema,o.HandleUserPromptSchema,o.LocateNodesSchema,o.NavigateSchema,o.PrintSchema,o.ReloadSchema,o.SetViewportSchema,o.TraverseHistorySchema]))),t.BrowsingContextEventSchema=r.default.lazy((()=>r.default.union([o.ContextCreatedSchema,o.ContextDestroyedSchema,o.DomContentLoadedSchema,o.DownloadWillBeginSchema,o.FragmentNavigatedSchema,o.LoadSchema,o.NavigationAbortedSchema,o.NavigationFailedSchema,o.NavigationStartedSchema,o.UserPromptClosedSchema,o.UserPromptOpenedSchema]))),t.BrowsingContextResultSchema=r.default.lazy((()=>r.default.union([o.CaptureScreenshotResultSchema,o.CreateResultSchema,o.GetTreeResultSchema,o.LocateNodesResultSchema,o.NavigateResultSchema,o.PrintResultSchema,o.TraverseHistoryResultSchema]))),function(e){e.BrowsingContextSchema=r.default.lazy((()=>r.default.string()))}(o||(t.BrowsingContext=o={})),function(e){e.InfoListSchema=r.default.lazy((()=>r.default.array(e.InfoSchema)))}(o||(t.BrowsingContext=o={})),function(e){e.InfoSchema=r.default.lazy((()=>r.default.object({children:r.default.union([e.InfoListSchema,r.default.null()]),context:e.BrowsingContextSchema,url:r.default.string(),userContext:s.UserContextSchema,parent:r.default.union([e.BrowsingContextSchema,r.default.null()]).optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.LocatorSchema=r.default.lazy((()=>r.default.union([e.CssLocatorSchema,e.InnerTextLocatorSchema,e.XPathLocatorSchema])))}(o||(t.BrowsingContext=o={})),function(e){e.CssLocatorSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("css"),value:r.default.string()})))}(o||(t.BrowsingContext=o={})),function(e){e.InnerTextLocatorSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("innerText"),value:r.default.string(),ignoreCase:r.default.boolean().optional(),matchType:r.default.enum(["full","partial"]).optional(),maxDepth:t.JsUintSchema.optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.XPathLocatorSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("xpath"),value:r.default.string()})))}(o||(t.BrowsingContext=o={})),function(e){e.NavigationSchema=r.default.lazy((()=>r.default.string()))}(o||(t.BrowsingContext=o={})),function(e){e.NavigationInfoSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,navigation:r.default.union([e.NavigationSchema,r.default.null()]),timestamp:t.JsUintSchema,url:r.default.string()})))}(o||(t.BrowsingContext=o={})),function(e){e.ReadinessStateSchema=r.default.lazy((()=>r.default.enum(["none","interactive","complete"])))}(o||(t.BrowsingContext=o={})),function(e){e.ActivateSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.activate"),params:e.ActivateParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.ActivateParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.CaptureScreenshotParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,origin:r.default.enum(["viewport","document"]).default("viewport").optional(),format:e.ImageFormatSchema.optional(),clip:e.ClipRectangleSchema.optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.CaptureScreenshotSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.captureScreenshot"),params:e.CaptureScreenshotParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.ImageFormatSchema=r.default.lazy((()=>r.default.object({type:r.default.string(),quality:r.default.number().gte(0).lte(1).optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.ClipRectangleSchema=r.default.lazy((()=>r.default.union([e.BoxClipRectangleSchema,e.ElementClipRectangleSchema])))}(o||(t.BrowsingContext=o={})),function(e){e.ElementClipRectangleSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("element"),element:c.SharedReferenceSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.BoxClipRectangleSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("box"),x:r.default.number(),y:r.default.number(),width:r.default.number(),height:r.default.number()})))}(o||(t.BrowsingContext=o={})),function(e){e.CaptureScreenshotResultSchema=r.default.lazy((()=>r.default.object({data:r.default.string()})))}(o||(t.BrowsingContext=o={})),function(e){e.CloseSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.close"),params:e.CloseParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.CloseParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,promptUnload:r.default.boolean().default(!1).optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.CreateSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.create"),params:e.CreateParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.CreateTypeSchema=r.default.lazy((()=>r.default.enum(["tab","window"])))}(o||(t.BrowsingContext=o={})),function(e){e.CreateParametersSchema=r.default.lazy((()=>r.default.object({type:e.CreateTypeSchema,referenceContext:e.BrowsingContextSchema.optional(),background:r.default.boolean().default(!1).optional(),userContext:r.default.union([s.UserContextSchema,r.default.null()]).optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.CreateResultSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.GetTreeSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.getTree"),params:e.GetTreeParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.GetTreeParametersSchema=r.default.lazy((()=>r.default.object({maxDepth:t.JsUintSchema.optional(),root:e.BrowsingContextSchema.optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.GetTreeResultSchema=r.default.lazy((()=>r.default.object({contexts:e.InfoListSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.HandleUserPromptSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.handleUserPrompt"),params:e.HandleUserPromptParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.HandleUserPromptParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,accept:r.default.boolean().optional(),userText:r.default.string().optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.LocateNodesParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,locator:e.LocatorSchema,maxNodeCount:t.JsUintSchema.gte(1).optional(),ownership:c.ResultOwnershipSchema.optional(),sandbox:r.default.string().optional(),serializationOptions:c.SerializationOptionsSchema.optional(),startNodes:r.default.array(c.SharedReferenceSchema).min(1).optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.LocateNodesSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.locateNodes"),params:e.LocateNodesParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.LocateNodesResultSchema=r.default.lazy((()=>r.default.object({nodes:r.default.array(c.NodeRemoteValueSchema)})))}(o||(t.BrowsingContext=o={})),function(e){e.NavigateSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.navigate"),params:e.NavigateParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.NavigateParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,url:r.default.string(),wait:e.ReadinessStateSchema.optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.NavigateResultSchema=r.default.lazy((()=>r.default.object({navigation:r.default.union([e.NavigationSchema,r.default.null()]),url:r.default.string()})))}(o||(t.BrowsingContext=o={})),function(e){e.PrintSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.print"),params:e.PrintParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.PrintParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,background:r.default.boolean().default(!1).optional(),margin:e.PrintMarginParametersSchema.optional(),orientation:r.default.enum(["portrait","landscape"]).default("portrait").optional(),page:e.PrintPageParametersSchema.optional(),pageRanges:r.default.array(r.default.union([t.JsUintSchema,r.default.string()])).optional(),scale:r.default.number().gte(.1).lte(2).default(1).optional(),shrinkToFit:r.default.boolean().default(!0).optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.PrintMarginParametersSchema=r.default.lazy((()=>r.default.object({bottom:r.default.number().gte(0).default(1).optional(),left:r.default.number().gte(0).default(1).optional(),right:r.default.number().gte(0).default(1).optional(),top:r.default.number().gte(0).default(1).optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.PrintPageParametersSchema=r.default.lazy((()=>r.default.object({height:r.default.number().gte(.0352).default(27.94).optional(),width:r.default.number().gte(.0352).default(21.59).optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.PrintResultSchema=r.default.lazy((()=>r.default.object({data:r.default.string()})))}(o||(t.BrowsingContext=o={})),function(e){e.ReloadSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.reload"),params:e.ReloadParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.ReloadParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,ignoreCache:r.default.boolean().optional(),wait:e.ReadinessStateSchema.optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.SetViewportSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.setViewport"),params:e.SetViewportParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.SetViewportParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,viewport:r.default.union([e.ViewportSchema,r.default.null()]).optional(),devicePixelRatio:r.default.union([r.default.number().gt(0),r.default.null()]).optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.ViewportSchema=r.default.lazy((()=>r.default.object({width:t.JsUintSchema,height:t.JsUintSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.TraverseHistorySchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.traverseHistory"),params:e.TraverseHistoryParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.TraverseHistoryParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,delta:t.JsIntSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.TraverseHistoryResultSchema=r.default.lazy((()=>r.default.object({})))}(o||(t.BrowsingContext=o={})),function(e){e.ContextCreatedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.contextCreated"),params:e.InfoSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.ContextDestroyedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.contextDestroyed"),params:e.InfoSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.NavigationStartedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.navigationStarted"),params:e.NavigationInfoSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.FragmentNavigatedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.fragmentNavigated"),params:e.NavigationInfoSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.DomContentLoadedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.domContentLoaded"),params:e.NavigationInfoSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.LoadSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.load"),params:e.NavigationInfoSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.DownloadWillBeginSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.downloadWillBegin"),params:e.NavigationInfoSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.NavigationAbortedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.navigationAborted"),params:e.NavigationInfoSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.NavigationFailedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.navigationFailed"),params:e.NavigationInfoSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.UserPromptClosedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.userPromptClosed"),params:e.UserPromptClosedParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.UserPromptClosedParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,accepted:r.default.boolean(),userText:r.default.string().optional()})))}(o||(t.BrowsingContext=o={})),function(e){e.UserPromptOpenedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("browsingContext.userPromptOpened"),params:e.UserPromptOpenedParametersSchema})))}(o||(t.BrowsingContext=o={})),function(e){e.UserPromptOpenedParametersSchema=r.default.lazy((()=>r.default.object({context:e.BrowsingContextSchema,type:r.default.enum(["alert","confirm","prompt","beforeunload"]),message:r.default.string(),defaultValue:r.default.string().optional()})))}(o||(t.BrowsingContext=o={})),t.NetworkCommandSchema=r.default.lazy((()=>r.default.union([i.AddInterceptSchema,i.ContinueRequestSchema,i.ContinueResponseSchema,i.ContinueWithAuthSchema,i.FailRequestSchema,i.ProvideResponseSchema,i.RemoveInterceptSchema]))),t.NetworkEventSchema=r.default.lazy((()=>r.default.union([i.AuthRequiredSchema,i.BeforeRequestSentSchema,i.FetchErrorSchema,i.ResponseCompletedSchema,i.ResponseStartedSchema]))),t.NetworkResultSchema=r.default.lazy((()=>i.AddInterceptResultSchema)),function(e){e.AuthChallengeSchema=r.default.lazy((()=>r.default.object({scheme:r.default.string(),realm:r.default.string()})))}(i||(t.Network=i={})),function(e){e.AuthCredentialsSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("password"),username:r.default.string(),password:r.default.string()})))}(i||(t.Network=i={})),function(e){e.BaseParametersSchema=r.default.lazy((()=>r.default.object({context:r.default.union([o.BrowsingContextSchema,r.default.null()]),isBlocked:r.default.boolean(),navigation:r.default.union([o.NavigationSchema,r.default.null()]),redirectCount:t.JsUintSchema,request:e.RequestDataSchema,timestamp:t.JsUintSchema,intercepts:r.default.array(e.InterceptSchema).min(1).optional()})))}(i||(t.Network=i={})),function(e){e.BytesValueSchema=r.default.lazy((()=>r.default.union([e.StringValueSchema,e.Base64ValueSchema])))}(i||(t.Network=i={})),function(e){e.StringValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("string"),value:r.default.string()})))}(i||(t.Network=i={})),function(e){e.Base64ValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("base64"),value:r.default.string()})))}(i||(t.Network=i={})),function(e){e.SameSiteSchema=r.default.lazy((()=>r.default.enum(["strict","lax","none"])))}(i||(t.Network=i={})),function(e){e.CookieSchema=r.default.lazy((()=>r.default.object({name:r.default.string(),value:e.BytesValueSchema,domain:r.default.string(),path:r.default.string(),size:t.JsUintSchema,httpOnly:r.default.boolean(),secure:r.default.boolean(),sameSite:e.SameSiteSchema,expiry:t.JsUintSchema.optional()}).and(t.ExtensibleSchema)))}(i||(t.Network=i={})),function(e){e.CookieHeaderSchema=r.default.lazy((()=>r.default.object({name:r.default.string(),value:e.BytesValueSchema})))}(i||(t.Network=i={})),function(e){e.FetchTimingInfoSchema=r.default.lazy((()=>r.default.object({timeOrigin:r.default.number(),requestTime:r.default.number(),redirectStart:r.default.number(),redirectEnd:r.default.number(),fetchStart:r.default.number(),dnsStart:r.default.number(),dnsEnd:r.default.number(),connectStart:r.default.number(),connectEnd:r.default.number(),tlsStart:r.default.number(),requestStart:r.default.number(),responseStart:r.default.number(),responseEnd:r.default.number()})))}(i||(t.Network=i={})),function(e){e.HeaderSchema=r.default.lazy((()=>r.default.object({name:r.default.string(),value:e.BytesValueSchema})))}(i||(t.Network=i={})),function(e){e.InitiatorSchema=r.default.lazy((()=>r.default.object({type:r.default.enum(["parser","script","preflight","other"]),columnNumber:t.JsUintSchema.optional(),lineNumber:t.JsUintSchema.optional(),stackTrace:c.StackTraceSchema.optional(),request:e.RequestSchema.optional()})))}(i||(t.Network=i={})),function(e){e.InterceptSchema=r.default.lazy((()=>r.default.string()))}(i||(t.Network=i={})),function(e){e.RequestSchema=r.default.lazy((()=>r.default.string()))}(i||(t.Network=i={})),function(e){e.RequestDataSchema=r.default.lazy((()=>r.default.object({request:e.RequestSchema,url:r.default.string(),method:r.default.string(),headers:r.default.array(e.HeaderSchema),cookies:r.default.array(e.CookieSchema),headersSize:t.JsUintSchema,bodySize:r.default.union([t.JsUintSchema,r.default.null()]),timings:e.FetchTimingInfoSchema})))}(i||(t.Network=i={})),function(e){e.ResponseContentSchema=r.default.lazy((()=>r.default.object({size:t.JsUintSchema})))}(i||(t.Network=i={})),function(e){e.ResponseDataSchema=r.default.lazy((()=>r.default.object({url:r.default.string(),protocol:r.default.string(),status:t.JsUintSchema,statusText:r.default.string(),fromCache:r.default.boolean(),headers:r.default.array(e.HeaderSchema),mimeType:r.default.string(),bytesReceived:t.JsUintSchema,headersSize:r.default.union([t.JsUintSchema,r.default.null()]),bodySize:r.default.union([t.JsUintSchema,r.default.null()]),content:e.ResponseContentSchema,authChallenge:e.AuthChallengeSchema.optional()})))}(i||(t.Network=i={})),function(e){e.SetCookieHeaderSchema=r.default.lazy((()=>r.default.object({name:r.default.string(),value:e.BytesValueSchema,domain:r.default.string().optional(),httpOnly:r.default.boolean().optional(),expiry:r.default.string().optional(),maxAge:t.JsIntSchema.optional(),path:r.default.string().optional(),sameSite:e.SameSiteSchema.optional(),secure:r.default.boolean().optional()})))}(i||(t.Network=i={})),function(e){e.UrlPatternSchema=r.default.lazy((()=>r.default.union([e.UrlPatternPatternSchema,e.UrlPatternStringSchema])))}(i||(t.Network=i={})),function(e){e.UrlPatternPatternSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("pattern"),protocol:r.default.string().optional(),hostname:r.default.string().optional(),port:r.default.string().optional(),pathname:r.default.string().optional(),search:r.default.string().optional()})))}(i||(t.Network=i={})),function(e){e.UrlPatternStringSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("string"),pattern:r.default.string()})))}(i||(t.Network=i={})),function(e){e.AddInterceptParametersSchema=r.default.lazy((()=>r.default.object({phases:r.default.array(e.InterceptPhaseSchema).min(1),urlPatterns:r.default.array(e.UrlPatternSchema).optional()})))}(i||(t.Network=i={})),function(e){e.AddInterceptSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.addIntercept"),params:e.AddInterceptParametersSchema})))}(i||(t.Network=i={})),function(e){e.InterceptPhaseSchema=r.default.lazy((()=>r.default.enum(["beforeRequestSent","responseStarted","authRequired"])))}(i||(t.Network=i={})),function(e){e.AddInterceptResultSchema=r.default.lazy((()=>r.default.object({intercept:e.InterceptSchema})))}(i||(t.Network=i={})),function(e){e.ContinueRequestSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.continueRequest"),params:e.ContinueRequestParametersSchema})))}(i||(t.Network=i={})),function(e){e.ContinueRequestParametersSchema=r.default.lazy((()=>r.default.object({request:e.RequestSchema,body:e.BytesValueSchema.optional(),cookies:r.default.array(e.CookieHeaderSchema).optional(),headers:r.default.array(e.HeaderSchema).optional(),method:r.default.string().optional(),url:r.default.string().optional()})))}(i||(t.Network=i={})),function(e){e.ContinueResponseSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.continueResponse"),params:e.ContinueResponseParametersSchema})))}(i||(t.Network=i={})),function(e){e.ContinueResponseParametersSchema=r.default.lazy((()=>r.default.object({request:e.RequestSchema,cookies:r.default.array(e.SetCookieHeaderSchema).optional(),credentials:e.AuthCredentialsSchema.optional(),headers:r.default.array(e.HeaderSchema).optional(),reasonPhrase:r.default.string().optional(),statusCode:t.JsUintSchema.optional()})))}(i||(t.Network=i={})),function(e){e.ContinueWithAuthSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.continueWithAuth"),params:e.ContinueWithAuthParametersSchema})))}(i||(t.Network=i={})),function(e){e.ContinueWithAuthParametersSchema=r.default.lazy((()=>r.default.object({request:e.RequestSchema}).and(r.default.union([e.ContinueWithAuthCredentialsSchema,e.ContinueWithAuthNoCredentialsSchema]))))}(i||(t.Network=i={})),function(e){e.ContinueWithAuthCredentialsSchema=r.default.lazy((()=>r.default.object({action:r.default.literal("provideCredentials"),credentials:e.AuthCredentialsSchema})))}(i||(t.Network=i={})),function(e){e.ContinueWithAuthNoCredentialsSchema=r.default.lazy((()=>r.default.object({action:r.default.enum(["default","cancel"])})))}(i||(t.Network=i={})),function(e){e.FailRequestSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.failRequest"),params:e.FailRequestParametersSchema})))}(i||(t.Network=i={})),function(e){e.FailRequestParametersSchema=r.default.lazy((()=>r.default.object({request:e.RequestSchema})))}(i||(t.Network=i={})),function(e){e.ProvideResponseSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.provideResponse"),params:e.ProvideResponseParametersSchema})))}(i||(t.Network=i={})),function(e){e.ProvideResponseParametersSchema=r.default.lazy((()=>r.default.object({request:e.RequestSchema,body:e.BytesValueSchema.optional(),cookies:r.default.array(e.SetCookieHeaderSchema).optional(),headers:r.default.array(e.HeaderSchema).optional(),reasonPhrase:r.default.string().optional(),statusCode:t.JsUintSchema.optional()})))}(i||(t.Network=i={})),function(e){e.RemoveInterceptSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.removeIntercept"),params:e.RemoveInterceptParametersSchema})))}(i||(t.Network=i={})),function(e){e.RemoveInterceptParametersSchema=r.default.lazy((()=>r.default.object({intercept:e.InterceptSchema})))}(i||(t.Network=i={})),t.ScriptEventSchema=r.default.lazy((()=>r.default.union([c.MessageSchema,c.RealmCreatedSchema,c.RealmDestroyedSchema]))),function(e){e.AuthRequiredParametersSchema=r.default.lazy((()=>e.BaseParametersSchema.and(r.default.object({response:e.ResponseDataSchema}))))}(i||(t.Network=i={})),function(e){e.BeforeRequestSentParametersSchema=r.default.lazy((()=>e.BaseParametersSchema.and(r.default.object({initiator:e.InitiatorSchema}))))}(i||(t.Network=i={})),function(e){e.FetchErrorParametersSchema=r.default.lazy((()=>e.BaseParametersSchema.and(r.default.object({errorText:r.default.string()}))))}(i||(t.Network=i={})),function(e){e.ResponseCompletedParametersSchema=r.default.lazy((()=>e.BaseParametersSchema.and(r.default.object({response:e.ResponseDataSchema}))))}(i||(t.Network=i={})),function(e){e.ResponseStartedParametersSchema=r.default.lazy((()=>e.BaseParametersSchema.and(r.default.object({response:e.ResponseDataSchema}))))}(i||(t.Network=i={})),t.ScriptCommandSchema=r.default.lazy((()=>r.default.union([c.AddPreloadScriptSchema,c.CallFunctionSchema,c.DisownSchema,c.EvaluateSchema,c.GetRealmsSchema,c.RemovePreloadScriptSchema]))),t.ScriptResultSchema=r.default.lazy((()=>r.default.union([c.AddPreloadScriptResultSchema,c.EvaluateResultSchema,c.GetRealmsResultSchema]))),function(e){e.AuthRequiredSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.authRequired"),params:e.AuthRequiredParametersSchema})))}(i||(t.Network=i={})),function(e){e.BeforeRequestSentSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.beforeRequestSent"),params:e.BeforeRequestSentParametersSchema})))}(i||(t.Network=i={})),function(e){e.FetchErrorSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.fetchError"),params:e.FetchErrorParametersSchema})))}(i||(t.Network=i={})),function(e){e.ResponseCompletedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.responseCompleted"),params:e.ResponseCompletedParametersSchema})))}(i||(t.Network=i={})),function(e){e.ResponseStartedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("network.responseStarted"),params:e.ResponseStartedParametersSchema})))}(i||(t.Network=i={})),function(e){e.ChannelSchema=r.default.lazy((()=>r.default.string()))}(c||(t.Script=c={})),function(e){e.EvaluateResultSuccessSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("success"),result:e.RemoteValueSchema,realm:e.RealmSchema})))}(c||(t.Script=c={})),function(e){e.ExceptionDetailsSchema=r.default.lazy((()=>r.default.object({columnNumber:t.JsUintSchema,exception:e.RemoteValueSchema,lineNumber:t.JsUintSchema,stackTrace:e.StackTraceSchema,text:r.default.string()})))}(c||(t.Script=c={})),function(e){e.ChannelValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("channel"),value:e.ChannelPropertiesSchema})))}(c||(t.Script=c={})),function(e){e.ChannelPropertiesSchema=r.default.lazy((()=>r.default.object({channel:e.ChannelSchema,serializationOptions:e.SerializationOptionsSchema.optional(),ownership:e.ResultOwnershipSchema.optional()})))}(c||(t.Script=c={})),function(e){e.EvaluateResultSchema=r.default.lazy((()=>r.default.union([e.EvaluateResultSuccessSchema,e.EvaluateResultExceptionSchema])))}(c||(t.Script=c={})),function(e){e.EvaluateResultExceptionSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("exception"),exceptionDetails:e.ExceptionDetailsSchema,realm:e.RealmSchema})))}(c||(t.Script=c={})),function(e){e.HandleSchema=r.default.lazy((()=>r.default.string()))}(c||(t.Script=c={})),function(e){e.InternalIdSchema=r.default.lazy((()=>r.default.string()))}(c||(t.Script=c={})),function(e){e.ListLocalValueSchema=r.default.lazy((()=>r.default.array(e.LocalValueSchema)))}(c||(t.Script=c={})),function(e){e.LocalValueSchema=r.default.lazy((()=>r.default.union([e.RemoteReferenceSchema,e.PrimitiveProtocolValueSchema,e.ChannelValueSchema,e.ArrayLocalValueSchema,e.DateLocalValueSchema,e.MapLocalValueSchema,e.ObjectLocalValueSchema,e.RegExpLocalValueSchema,e.SetLocalValueSchema])))}(c||(t.Script=c={})),function(e){e.ArrayLocalValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("array"),value:e.ListLocalValueSchema})))}(c||(t.Script=c={})),function(e){e.DateLocalValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("date"),value:r.default.string()})))}(c||(t.Script=c={})),function(e){e.MappingLocalValueSchema=r.default.lazy((()=>r.default.array(r.default.tuple([r.default.union([e.LocalValueSchema,r.default.string()]),e.LocalValueSchema]))))}(c||(t.Script=c={})),function(e){e.MapLocalValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("map"),value:e.MappingLocalValueSchema})))}(c||(t.Script=c={})),function(e){e.ObjectLocalValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("object"),value:e.MappingLocalValueSchema})))}(c||(t.Script=c={})),function(e){e.RegExpValueSchema=r.default.lazy((()=>r.default.object({pattern:r.default.string(),flags:r.default.string().optional()})))}(c||(t.Script=c={})),function(e){e.RegExpLocalValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("regexp"),value:e.RegExpValueSchema})))}(c||(t.Script=c={})),function(e){e.SetLocalValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("set"),value:e.ListLocalValueSchema})))}(c||(t.Script=c={})),function(e){e.PreloadScriptSchema=r.default.lazy((()=>r.default.string()))}(c||(t.Script=c={})),function(e){e.RealmSchema=r.default.lazy((()=>r.default.string()))}(c||(t.Script=c={})),function(e){e.PrimitiveProtocolValueSchema=r.default.lazy((()=>r.default.union([e.UndefinedValueSchema,e.NullValueSchema,e.StringValueSchema,e.NumberValueSchema,e.BooleanValueSchema,e.BigIntValueSchema])))}(c||(t.Script=c={})),function(e){e.UndefinedValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("undefined")})))}(c||(t.Script=c={})),function(e){e.NullValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("null")})))}(c||(t.Script=c={})),function(e){e.StringValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("string"),value:r.default.string()})))}(c||(t.Script=c={})),function(e){e.SpecialNumberSchema=r.default.lazy((()=>r.default.enum(["NaN","-0","Infinity","-Infinity"])))}(c||(t.Script=c={})),function(e){e.NumberValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("number"),value:r.default.union([r.default.number(),e.SpecialNumberSchema])})))}(c||(t.Script=c={})),function(e){e.BooleanValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("boolean"),value:r.default.boolean()})))}(c||(t.Script=c={})),function(e){e.BigIntValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("bigint"),value:r.default.string()})))}(c||(t.Script=c={})),function(e){e.RealmInfoSchema=r.default.lazy((()=>r.default.union([e.WindowRealmInfoSchema,e.DedicatedWorkerRealmInfoSchema,e.SharedWorkerRealmInfoSchema,e.ServiceWorkerRealmInfoSchema,e.WorkerRealmInfoSchema,e.PaintWorkletRealmInfoSchema,e.AudioWorkletRealmInfoSchema,e.WorkletRealmInfoSchema])))}(c||(t.Script=c={})),function(e){e.BaseRealmInfoSchema=r.default.lazy((()=>r.default.object({realm:e.RealmSchema,origin:r.default.string()})))}(c||(t.Script=c={})),function(e){e.WindowRealmInfoSchema=r.default.lazy((()=>e.BaseRealmInfoSchema.and(r.default.object({type:r.default.literal("window"),context:o.BrowsingContextSchema,sandbox:r.default.string().optional()}))))}(c||(t.Script=c={})),function(e){e.DedicatedWorkerRealmInfoSchema=r.default.lazy((()=>e.BaseRealmInfoSchema.and(r.default.object({type:r.default.literal("dedicated-worker"),owners:r.default.tuple([e.RealmSchema])}))))}(c||(t.Script=c={})),function(e){e.SharedWorkerRealmInfoSchema=r.default.lazy((()=>e.BaseRealmInfoSchema.and(r.default.object({type:r.default.literal("shared-worker"),owners:r.default.array(e.RealmSchema).min(1)}))))}(c||(t.Script=c={})),function(e){e.ServiceWorkerRealmInfoSchema=r.default.lazy((()=>e.BaseRealmInfoSchema.and(r.default.object({type:r.default.literal("service-worker"),owners:r.default.array(e.RealmSchema)}))))}(c||(t.Script=c={})),function(e){e.WorkerRealmInfoSchema=r.default.lazy((()=>e.BaseRealmInfoSchema.and(r.default.object({type:r.default.literal("worker")}))))}(c||(t.Script=c={})),function(e){e.PaintWorkletRealmInfoSchema=r.default.lazy((()=>e.BaseRealmInfoSchema.and(r.default.object({type:r.default.literal("paint-worklet")}))))}(c||(t.Script=c={})),function(e){e.AudioWorkletRealmInfoSchema=r.default.lazy((()=>e.BaseRealmInfoSchema.and(r.default.object({type:r.default.literal("audio-worklet")}))))}(c||(t.Script=c={})),function(e){e.WorkletRealmInfoSchema=r.default.lazy((()=>e.BaseRealmInfoSchema.and(r.default.object({type:r.default.literal("worklet")}))))}(c||(t.Script=c={})),function(e){e.RealmTypeSchema=r.default.lazy((()=>r.default.enum(["window","dedicated-worker","shared-worker","service-worker","worker","paint-worklet","audio-worklet","worklet"])))}(c||(t.Script=c={})),function(e){e.ListRemoteValueSchema=r.default.lazy((()=>r.default.array(e.RemoteValueSchema)))}(c||(t.Script=c={})),function(e){e.MappingRemoteValueSchema=r.default.lazy((()=>r.default.array(r.default.tuple([r.default.union([e.RemoteValueSchema,r.default.string()]),e.RemoteValueSchema]))))}(c||(t.Script=c={})),function(e){e.RemoteValueSchema=r.default.lazy((()=>r.default.union([e.PrimitiveProtocolValueSchema,e.SymbolRemoteValueSchema,e.ArrayRemoteValueSchema,e.ObjectRemoteValueSchema,e.FunctionRemoteValueSchema,e.RegExpRemoteValueSchema,e.DateRemoteValueSchema,e.MapRemoteValueSchema,e.SetRemoteValueSchema,e.WeakMapRemoteValueSchema,e.WeakSetRemoteValueSchema,e.IteratorRemoteValueSchema,e.GeneratorRemoteValueSchema,e.ErrorRemoteValueSchema,e.ProxyRemoteValueSchema,e.PromiseRemoteValueSchema,e.TypedArrayRemoteValueSchema,e.ArrayBufferRemoteValueSchema,e.NodeListRemoteValueSchema,e.HtmlCollectionRemoteValueSchema,e.NodeRemoteValueSchema,e.WindowProxyRemoteValueSchema])))}(c||(t.Script=c={})),function(e){e.RemoteReferenceSchema=r.default.lazy((()=>r.default.union([e.SharedReferenceSchema,e.RemoteObjectReferenceSchema])))}(c||(t.Script=c={})),function(e){e.SharedReferenceSchema=r.default.lazy((()=>r.default.object({sharedId:e.SharedIdSchema,handle:e.HandleSchema.optional()}).and(t.ExtensibleSchema)))}(c||(t.Script=c={})),function(e){e.RemoteObjectReferenceSchema=r.default.lazy((()=>r.default.object({handle:e.HandleSchema,sharedId:e.SharedIdSchema.optional()}).and(t.ExtensibleSchema)))}(c||(t.Script=c={})),function(e){e.SymbolRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("symbol"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.ArrayRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("array"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional(),value:e.ListRemoteValueSchema.optional()})))}(c||(t.Script=c={})),function(e){e.ObjectRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("object"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional(),value:e.MappingRemoteValueSchema.optional()})))}(c||(t.Script=c={})),function(e){e.FunctionRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("function"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.RegExpRemoteValueSchema=r.default.lazy((()=>r.default.object({handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()}).and(e.RegExpLocalValueSchema)))}(c||(t.Script=c={})),function(e){e.DateRemoteValueSchema=r.default.lazy((()=>r.default.object({handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()}).and(e.DateLocalValueSchema)))}(c||(t.Script=c={})),function(e){e.MapRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("map"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional(),value:e.MappingRemoteValueSchema.optional()})))}(c||(t.Script=c={})),function(e){e.SetRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("set"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional(),value:e.ListRemoteValueSchema.optional()})))}(c||(t.Script=c={})),function(e){e.WeakMapRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("weakmap"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.WeakSetRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("weakset"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.IteratorRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("iterator"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.GeneratorRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("generator"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.ErrorRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("error"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.ProxyRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("proxy"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.PromiseRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("promise"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.TypedArrayRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("typedarray"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.ArrayBufferRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("arraybuffer"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.NodeListRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("nodelist"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional(),value:e.ListRemoteValueSchema.optional()})))}(c||(t.Script=c={})),function(e){e.HtmlCollectionRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("htmlcollection"),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional(),value:e.ListRemoteValueSchema.optional()})))}(c||(t.Script=c={})),function(e){e.NodeRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("node"),sharedId:e.SharedIdSchema.optional(),handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional(),value:e.NodePropertiesSchema.optional()})))}(c||(t.Script=c={})),function(e){e.NodePropertiesSchema=r.default.lazy((()=>r.default.object({nodeType:t.JsUintSchema,childNodeCount:t.JsUintSchema,attributes:r.default.record(r.default.string(),r.default.string()).optional(),children:r.default.array(e.NodeRemoteValueSchema).optional(),localName:r.default.string().optional(),mode:r.default.enum(["open","closed"]).optional(),namespaceURI:r.default.string().optional(),nodeValue:r.default.string().optional(),shadowRoot:r.default.union([e.NodeRemoteValueSchema,r.default.null()]).optional()})))}(c||(t.Script=c={})),function(e){e.WindowProxyRemoteValueSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("window"),value:e.WindowProxyPropertiesSchema,handle:e.HandleSchema.optional(),internalId:e.InternalIdSchema.optional()})))}(c||(t.Script=c={})),function(e){e.WindowProxyPropertiesSchema=r.default.lazy((()=>r.default.object({context:o.BrowsingContextSchema})))}(c||(t.Script=c={})),function(e){e.ResultOwnershipSchema=r.default.lazy((()=>r.default.enum(["root","none"])))}(c||(t.Script=c={})),function(e){e.SerializationOptionsSchema=r.default.lazy((()=>r.default.object({maxDomDepth:r.default.union([t.JsUintSchema,r.default.null()]).default(0).optional(),maxObjectDepth:r.default.union([t.JsUintSchema,r.default.null()]).default(null).optional(),includeShadowTree:r.default.enum(["none","open","all"]).default("none").optional()})))}(c||(t.Script=c={})),function(e){e.SharedIdSchema=r.default.lazy((()=>r.default.string()))}(c||(t.Script=c={})),function(e){e.StackFrameSchema=r.default.lazy((()=>r.default.object({columnNumber:t.JsUintSchema,functionName:r.default.string(),lineNumber:t.JsUintSchema,url:r.default.string()})))}(c||(t.Script=c={})),function(e){e.StackTraceSchema=r.default.lazy((()=>r.default.object({callFrames:r.default.array(e.StackFrameSchema)})))}(c||(t.Script=c={})),function(e){e.SourceSchema=r.default.lazy((()=>r.default.object({realm:e.RealmSchema,context:o.BrowsingContextSchema.optional()})))}(c||(t.Script=c={})),function(e){e.RealmTargetSchema=r.default.lazy((()=>r.default.object({realm:e.RealmSchema})))}(c||(t.Script=c={})),function(e){e.ContextTargetSchema=r.default.lazy((()=>r.default.object({context:o.BrowsingContextSchema,sandbox:r.default.string().optional()})))}(c||(t.Script=c={})),function(e){e.TargetSchema=r.default.lazy((()=>r.default.union([e.RealmTargetSchema,e.ContextTargetSchema])))}(c||(t.Script=c={})),function(e){e.AddPreloadScriptSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("script.addPreloadScript"),params:e.AddPreloadScriptParametersSchema})))}(c||(t.Script=c={})),function(e){e.AddPreloadScriptParametersSchema=r.default.lazy((()=>r.default.object({functionDeclaration:r.default.string(),arguments:r.default.array(e.ChannelValueSchema).optional(),contexts:r.default.array(o.BrowsingContextSchema).min(1).optional(),sandbox:r.default.string().optional()})))}(c||(t.Script=c={})),function(e){e.AddPreloadScriptResultSchema=r.default.lazy((()=>r.default.object({script:e.PreloadScriptSchema})))}(c||(t.Script=c={})),function(e){e.DisownSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("script.disown"),params:e.DisownParametersSchema})))}(c||(t.Script=c={})),function(e){e.DisownParametersSchema=r.default.lazy((()=>r.default.object({handles:r.default.array(e.HandleSchema),target:e.TargetSchema})))}(c||(t.Script=c={})),function(e){e.CallFunctionParametersSchema=r.default.lazy((()=>r.default.object({functionDeclaration:r.default.string(),awaitPromise:r.default.boolean(),target:e.TargetSchema,arguments:r.default.array(e.LocalValueSchema).optional(),resultOwnership:e.ResultOwnershipSchema.optional(),serializationOptions:e.SerializationOptionsSchema.optional(),this:e.LocalValueSchema.optional(),userActivation:r.default.boolean().default(!1).optional()})))}(c||(t.Script=c={})),function(e){e.CallFunctionSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("script.callFunction"),params:e.CallFunctionParametersSchema})))}(c||(t.Script=c={})),function(e){e.EvaluateSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("script.evaluate"),params:e.EvaluateParametersSchema})))}(c||(t.Script=c={})),function(e){e.EvaluateParametersSchema=r.default.lazy((()=>r.default.object({expression:r.default.string(),target:e.TargetSchema,awaitPromise:r.default.boolean(),resultOwnership:e.ResultOwnershipSchema.optional(),serializationOptions:e.SerializationOptionsSchema.optional(),userActivation:r.default.boolean().default(!1).optional()})))}(c||(t.Script=c={})),function(e){e.GetRealmsSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("script.getRealms"),params:e.GetRealmsParametersSchema})))}(c||(t.Script=c={})),function(e){e.GetRealmsParametersSchema=r.default.lazy((()=>r.default.object({context:o.BrowsingContextSchema.optional(),type:e.RealmTypeSchema.optional()})))}(c||(t.Script=c={})),function(e){e.GetRealmsResultSchema=r.default.lazy((()=>r.default.object({realms:r.default.array(e.RealmInfoSchema)})))}(c||(t.Script=c={})),function(e){e.RemovePreloadScriptSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("script.removePreloadScript"),params:e.RemovePreloadScriptParametersSchema})))}(c||(t.Script=c={})),function(e){e.RemovePreloadScriptParametersSchema=r.default.lazy((()=>r.default.object({script:e.PreloadScriptSchema})))}(c||(t.Script=c={})),function(e){e.MessageParametersSchema=r.default.lazy((()=>r.default.object({channel:e.ChannelSchema,data:e.RemoteValueSchema,source:e.SourceSchema})))}(c||(t.Script=c={})),function(e){e.RealmCreatedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("script.realmCreated"),params:e.RealmInfoSchema})))}(c||(t.Script=c={})),function(e){e.MessageSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("script.message"),params:e.MessageParametersSchema})))}(c||(t.Script=c={})),function(e){e.RealmDestroyedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("script.realmDestroyed"),params:e.RealmDestroyedParametersSchema})))}(c||(t.Script=c={})),function(e){e.RealmDestroyedParametersSchema=r.default.lazy((()=>r.default.object({realm:e.RealmSchema})))}(c||(t.Script=c={})),t.StorageCommandSchema=r.default.lazy((()=>r.default.union([d.DeleteCookiesSchema,d.GetCookiesSchema,d.SetCookieSchema]))),t.StorageResultSchema=r.default.lazy((()=>r.default.union([d.DeleteCookiesResultSchema,d.GetCookiesResultSchema,d.SetCookieResultSchema]))),function(e){e.PartitionKeySchema=r.default.lazy((()=>r.default.object({userContext:r.default.string().optional(),sourceOrigin:r.default.string().optional()}).and(t.ExtensibleSchema)))}(d||(t.Storage=d={})),function(e){e.GetCookiesSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("storage.getCookies"),params:e.GetCookiesParametersSchema})))}(d||(t.Storage=d={})),function(e){e.CookieFilterSchema=r.default.lazy((()=>r.default.object({name:r.default.string().optional(),value:i.BytesValueSchema.optional(),domain:r.default.string().optional(),path:r.default.string().optional(),size:t.JsUintSchema.optional(),httpOnly:r.default.boolean().optional(),secure:r.default.boolean().optional(),sameSite:i.SameSiteSchema.optional(),expiry:t.JsUintSchema.optional()}).and(t.ExtensibleSchema)))}(d||(t.Storage=d={})),function(e){e.BrowsingContextPartitionDescriptorSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("context"),context:o.BrowsingContextSchema})))}(d||(t.Storage=d={})),function(e){e.StorageKeyPartitionDescriptorSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("storageKey"),userContext:r.default.string().optional(),sourceOrigin:r.default.string().optional()}).and(t.ExtensibleSchema)))}(d||(t.Storage=d={})),function(e){e.PartitionDescriptorSchema=r.default.lazy((()=>r.default.union([e.BrowsingContextPartitionDescriptorSchema,e.StorageKeyPartitionDescriptorSchema])))}(d||(t.Storage=d={})),function(e){e.GetCookiesParametersSchema=r.default.lazy((()=>r.default.object({filter:e.CookieFilterSchema.optional(),partition:e.PartitionDescriptorSchema.optional()})))}(d||(t.Storage=d={})),function(e){e.GetCookiesResultSchema=r.default.lazy((()=>r.default.object({cookies:r.default.array(i.CookieSchema),partitionKey:e.PartitionKeySchema})))}(d||(t.Storage=d={})),function(e){e.SetCookieSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("storage.setCookie"),params:e.SetCookieParametersSchema})))}(d||(t.Storage=d={})),function(e){e.PartialCookieSchema=r.default.lazy((()=>r.default.object({name:r.default.string(),value:i.BytesValueSchema,domain:r.default.string(),path:r.default.string().optional(),httpOnly:r.default.boolean().optional(),secure:r.default.boolean().optional(),sameSite:i.SameSiteSchema.optional(),expiry:t.JsUintSchema.optional()}).and(t.ExtensibleSchema)))}(d||(t.Storage=d={})),function(e){e.SetCookieParametersSchema=r.default.lazy((()=>r.default.object({cookie:e.PartialCookieSchema,partition:e.PartitionDescriptorSchema.optional()})))}(d||(t.Storage=d={})),function(e){e.SetCookieResultSchema=r.default.lazy((()=>r.default.object({partitionKey:e.PartitionKeySchema})))}(d||(t.Storage=d={})),function(e){e.DeleteCookiesSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("storage.deleteCookies"),params:e.DeleteCookiesParametersSchema})))}(d||(t.Storage=d={})),function(e){e.DeleteCookiesParametersSchema=r.default.lazy((()=>r.default.object({filter:e.CookieFilterSchema.optional(),partition:e.PartitionDescriptorSchema.optional()})))}(d||(t.Storage=d={})),function(e){e.DeleteCookiesResultSchema=r.default.lazy((()=>r.default.object({partitionKey:e.PartitionKeySchema})))}(d||(t.Storage=d={})),t.LogEventSchema=r.default.lazy((()=>u.EntryAddedSchema)),function(e){e.LevelSchema=r.default.lazy((()=>r.default.enum(["debug","info","warn","error"])))}(u||(t.Log=u={})),function(e){e.EntrySchema=r.default.lazy((()=>r.default.union([e.GenericLogEntrySchema,e.ConsoleLogEntrySchema,e.JavascriptLogEntrySchema])))}(u||(t.Log=u={})),function(e){e.BaseLogEntrySchema=r.default.lazy((()=>r.default.object({level:e.LevelSchema,source:c.SourceSchema,text:r.default.union([r.default.string(),r.default.null()]),timestamp:t.JsUintSchema,stackTrace:c.StackTraceSchema.optional()})))}(u||(t.Log=u={})),function(e){e.GenericLogEntrySchema=r.default.lazy((()=>e.BaseLogEntrySchema.and(r.default.object({type:r.default.string()}))))}(u||(t.Log=u={})),function(e){e.ConsoleLogEntrySchema=r.default.lazy((()=>e.BaseLogEntrySchema.and(r.default.object({type:r.default.literal("console"),method:r.default.string(),args:r.default.array(c.RemoteValueSchema)}))))}(u||(t.Log=u={})),function(e){e.JavascriptLogEntrySchema=r.default.lazy((()=>e.BaseLogEntrySchema.and(r.default.object({type:r.default.literal("javascript")}))))}(u||(t.Log=u={})),function(e){e.EntryAddedSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("log.entryAdded"),params:e.EntrySchema})))}(u||(t.Log=u={})),t.InputCommandSchema=r.default.lazy((()=>r.default.union([l.PerformActionsSchema,l.ReleaseActionsSchema,l.SetFilesSchema]))),function(e){e.ElementOriginSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("element"),element:c.SharedReferenceSchema})))}(l||(t.Input=l={})),function(e){e.PerformActionsParametersSchema=r.default.lazy((()=>r.default.object({context:o.BrowsingContextSchema,actions:r.default.array(e.SourceActionsSchema)})))}(l||(t.Input=l={})),function(e){e.NoneSourceActionsSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("none"),id:r.default.string(),actions:r.default.array(e.NoneSourceActionSchema)})))}(l||(t.Input=l={})),function(e){e.KeySourceActionsSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("key"),id:r.default.string(),actions:r.default.array(e.KeySourceActionSchema)})))}(l||(t.Input=l={})),function(e){e.PointerSourceActionsSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("pointer"),id:r.default.string(),parameters:e.PointerParametersSchema.optional(),actions:r.default.array(e.PointerSourceActionSchema)})))}(l||(t.Input=l={})),function(e){e.PerformActionsSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("input.performActions"),params:e.PerformActionsParametersSchema})))}(l||(t.Input=l={})),function(e){e.SourceActionsSchema=r.default.lazy((()=>r.default.union([e.NoneSourceActionsSchema,e.KeySourceActionsSchema,e.PointerSourceActionsSchema,e.WheelSourceActionsSchema])))}(l||(t.Input=l={})),function(e){e.NoneSourceActionSchema=r.default.lazy((()=>e.PauseActionSchema))}(l||(t.Input=l={})),function(e){e.KeySourceActionSchema=r.default.lazy((()=>r.default.union([e.PauseActionSchema,e.KeyDownActionSchema,e.KeyUpActionSchema])))}(l||(t.Input=l={})),function(e){e.PointerTypeSchema=r.default.lazy((()=>r.default.enum(["mouse","pen","touch"])))}(l||(t.Input=l={})),function(e){e.PointerParametersSchema=r.default.lazy((()=>r.default.object({pointerType:e.PointerTypeSchema.default("mouse").optional()})))}(l||(t.Input=l={})),function(e){e.WheelSourceActionsSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("wheel"),id:r.default.string(),actions:r.default.array(e.WheelSourceActionSchema)})))}(l||(t.Input=l={})),function(e){e.PointerSourceActionSchema=r.default.lazy((()=>r.default.union([e.PauseActionSchema,e.PointerDownActionSchema,e.PointerUpActionSchema,e.PointerMoveActionSchema])))}(l||(t.Input=l={})),function(e){e.WheelSourceActionSchema=r.default.lazy((()=>r.default.union([e.PauseActionSchema,e.WheelScrollActionSchema])))}(l||(t.Input=l={})),function(e){e.PauseActionSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("pause"),duration:t.JsUintSchema.optional()})))}(l||(t.Input=l={})),function(e){e.KeyDownActionSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("keyDown"),value:r.default.string()})))}(l||(t.Input=l={})),function(e){e.KeyUpActionSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("keyUp"),value:r.default.string()})))}(l||(t.Input=l={})),function(e){e.PointerUpActionSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("pointerUp"),button:t.JsUintSchema}).and(e.PointerCommonPropertiesSchema)))}(l||(t.Input=l={})),function(e){e.PointerDownActionSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("pointerDown"),button:t.JsUintSchema}).and(e.PointerCommonPropertiesSchema)))}(l||(t.Input=l={})),function(e){e.PointerMoveActionSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("pointerMove"),x:t.JsIntSchema,y:t.JsIntSchema,duration:t.JsUintSchema.optional(),origin:e.OriginSchema.optional()}).and(e.PointerCommonPropertiesSchema)))}(l||(t.Input=l={})),function(e){e.WheelScrollActionSchema=r.default.lazy((()=>r.default.object({type:r.default.literal("scroll"),x:t.JsIntSchema,y:t.JsIntSchema,deltaX:t.JsIntSchema,deltaY:t.JsIntSchema,duration:t.JsUintSchema.optional(),origin:e.OriginSchema.default("viewport").optional()})))}(l||(t.Input=l={})),function(e){e.PointerCommonPropertiesSchema=r.default.lazy((()=>r.default.object({width:t.JsUintSchema.default(1).optional(),height:t.JsUintSchema.default(1).optional(),pressure:r.default.number().default(0).optional(),tangentialPressure:r.default.number().default(0).optional(),twist:r.default.number().int().nonnegative().gte(0).lte(359).default(0).optional(),altitudeAngle:r.default.number().gte(0).lte(1.5707963267948966).default(0).optional(),azimuthAngle:r.default.number().gte(0).lte(6.283185307179586).default(0).optional()})))}(l||(t.Input=l={})),function(e){e.OriginSchema=r.default.lazy((()=>r.default.union([r.default.literal("viewport"),r.default.literal("pointer"),e.ElementOriginSchema])))}(l||(t.Input=l={})),function(e){e.ReleaseActionsSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("input.releaseActions"),params:e.ReleaseActionsParametersSchema})))}(l||(t.Input=l={})),function(e){e.ReleaseActionsParametersSchema=r.default.lazy((()=>r.default.object({context:o.BrowsingContextSchema})))}(l||(t.Input=l={})),function(e){e.SetFilesSchema=r.default.lazy((()=>r.default.object({method:r.default.literal("input.setFiles"),params:e.SetFilesParametersSchema})))}(l||(t.Input=l={})),function(e){e.SetFilesParametersSchema=r.default.lazy((()=>r.default.object({context:o.BrowsingContextSchema,element:c.SharedReferenceSchema,files:r.default.array(r.default.string())})))}(l||(t.Input=l={}))}(On);var Mn=e&&e.__createBinding||(Object.create?function(e,t,a,r){void 0===r&&(r=a);var n=Object.getOwnPropertyDescriptor(t,a);n&&!("get"in n?!t.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return t[a]}}),Object.defineProperty(e,r,n)}:function(e,t,a,r){void 0===r&&(r=a),e[r]=t[a]}),An=e&&e.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),Bn=e&&e.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var a in e)"default"!==a&&Object.prototype.hasOwnProperty.call(e,a)&&Mn(t,e,a);return An(t,e),t};Object.defineProperty(un,"__esModule",{value:!0}),un.Permissions=un.Cdp=un.Storage=un.Input=un.Session=un.BrowsingContext=un.Script=un.Network=un.parseObject=void 0;const zn=ln,Dn=g,Ln=Bn(_n),Zn=Bn(On);function Un(e,t){const a=t.safeParse(e);if(a.success)return a.data;const r=a.error.errors.map((e=>`${e.message} in ${e.path.map((e=>JSON.stringify(e))).join("/")}.`)).join(" ");throw new Dn.InvalidArgumentException(r)}var Fn,qn,Vn,$n,Kn,Hn,Wn,Jn;un.parseObject=Un,function(e){e.parseAddInterceptParameters=function(e){return Un(e,Zn.Network.AddInterceptParametersSchema)},e.parseContinueRequestParameters=function(e){return Un(e,Zn.Network.ContinueRequestParametersSchema)},e.parseContinueResponseParameters=function(e){return Un(e,Zn.Network.ContinueResponseParametersSchema)},e.parseContinueWithAuthParameters=function(e){return Un(e,Zn.Network.ContinueWithAuthParametersSchema)},e.parseFailRequestParameters=function(e){return Un(e,Zn.Network.FailRequestParametersSchema)},e.parseProvideResponseParameters=function(e){return Un(e,Zn.Network.ProvideResponseParametersSchema)},e.parseRemoveInterceptParameters=function(e){return Un(e,Zn.Network.RemoveInterceptParametersSchema)}}(Fn||(un.Network=Fn={})),function(e){e.parseGetRealmsParams=function(e){return Un(e,Zn.Script.GetRealmsParametersSchema)},e.parseEvaluateParams=function(e){return Un(e,Zn.Script.EvaluateParametersSchema)},e.parseDisownParams=function(e){return Un(e,Zn.Script.DisownParametersSchema)},e.parseAddPreloadScriptParams=function(e){return Un(e,Zn.Script.AddPreloadScriptParametersSchema)},e.parseRemovePreloadScriptParams=function(e){return Un(e,Zn.Script.RemovePreloadScriptParametersSchema)},e.parseCallFunctionParams=function(e){return Un(e,Zn.Script.CallFunctionParametersSchema)}}(qn||(un.Script=qn={})),function(e){e.parseActivateParams=function(e){return Un(e,Zn.BrowsingContext.ActivateParametersSchema)},e.parseGetTreeParams=function(e){return Un(e,Zn.BrowsingContext.GetTreeParametersSchema)},e.parseNavigateParams=function(e){return Un(e,Zn.BrowsingContext.NavigateParametersSchema)},e.parseReloadParams=function(e){return Un(e,Zn.BrowsingContext.ReloadParametersSchema)},e.parseCreateParams=function(e){return Un(e,Zn.BrowsingContext.CreateParametersSchema)},e.parseCloseParams=function(e){return Un(e,Zn.BrowsingContext.CloseParametersSchema)},e.parseCaptureScreenshotParams=function(e){return Un(e,Zn.BrowsingContext.CaptureScreenshotParametersSchema)},e.parsePrintParams=function(e){return Un(e,Zn.BrowsingContext.PrintParametersSchema)},e.parseSetViewportParams=function(e){return Un(e,Zn.BrowsingContext.SetViewportParametersSchema)},e.parseTraverseHistoryParams=function(e){return Un(e,Zn.BrowsingContext.TraverseHistoryParametersSchema)},e.parseHandleUserPromptParameters=function(e){return Un(e,Zn.BrowsingContext.HandleUserPromptParametersSchema)}}(Vn||(un.BrowsingContext=Vn={})),function(e){e.parseSubscribeParams=function(e){return Un(e,Zn.Session.SubscriptionRequestSchema)}}($n||(un.Session=$n={})),function(e){e.parsePerformActionsParams=function(e){return Un(e,Zn.Input.PerformActionsParametersSchema)},e.parseReleaseActionsParams=function(e){return Un(e,Zn.Input.ReleaseActionsParametersSchema)},e.parseSetFilesParams=function(e){return Un(e,Zn.Input.SetFilesParametersSchema)}}(Kn||(un.Input=Kn={})),function(e){e.parseGetCookiesParams=function(e){return Un(e,Zn.Storage.GetCookiesParametersSchema)},e.parseSetCookieParams=function(e){return Un(e,Zn.Storage.SetCookieParametersSchema)},e.parseDeleteCookiesParams=function(e){return Un(e,Zn.Storage.DeleteCookiesParametersSchema)}}(Hn||(un.Storage=Hn={})),function(e){const t=zn.z.object({method:zn.z.string(),params:zn.z.object({}).passthrough().optional(),session:zn.z.string().optional()}),a=zn.z.object({context:Zn.BrowsingContext.BrowsingContextSchema});e.parseSendCommandRequest=function(e){return Un(e,t)},e.parseGetSessionRequest=function(e){return Un(e,a)}}(Wn||(un.Cdp=Wn={})),function(e){e.parseSetPermissionsParams=function(e){return Un(e,Ln.Permissions.SetPermissionParametersSchema)}}(Jn||(un.Permissions=Jn={}));var Gn=e&&e.__createBinding||(Object.create?function(e,t,a,r){void 0===r&&(r=a);var n=Object.getOwnPropertyDescriptor(t,a);n&&!("get"in n?!t.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return t[a]}}),Object.defineProperty(e,r,n)}:function(e,t,a,r){void 0===r&&(r=a),e[r]=t[a]}),Xn=e&&e.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),Yn=e&&e.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var a in e)"default"!==a&&Object.prototype.hasOwnProperty.call(e,a)&&Gn(t,e,a);return Xn(t,e),t};Object.defineProperty(dn,"__esModule",{value:!0}),dn.BidiParser=void 0;const Qn=Yn(un);dn.BidiParser=class{parseActivateParams(e){return Qn.BrowsingContext.parseActivateParams(e)}parseCaptureScreenshotParams(e){return Qn.BrowsingContext.parseCaptureScreenshotParams(e)}parseCloseParams(e){return Qn.BrowsingContext.parseCloseParams(e)}parseCreateParams(e){return Qn.BrowsingContext.parseCreateParams(e)}parseGetTreeParams(e){return Qn.BrowsingContext.parseGetTreeParams(e)}parseHandleUserPromptParams(e){return Qn.BrowsingContext.parseHandleUserPromptParameters(e)}parseNavigateParams(e){return Qn.BrowsingContext.parseNavigateParams(e)}parsePrintParams(e){return Qn.BrowsingContext.parsePrintParams(e)}parseReloadParams(e){return Qn.BrowsingContext.parseReloadParams(e)}parseSetViewportParams(e){return Qn.BrowsingContext.parseSetViewportParams(e)}parseTraverseHistoryParams(e){return Qn.BrowsingContext.parseTraverseHistoryParams(e)}parseGetSessionParams(e){return Qn.Cdp.parseGetSessionRequest(e)}parseSendCommandParams(e){return Qn.Cdp.parseSendCommandRequest(e)}parsePerformActionsParams(e){return Qn.Input.parsePerformActionsParams(e)}parseReleaseActionsParams(e){return Qn.Input.parseReleaseActionsParams(e)}parseSetFilesParams(e){return Qn.Input.parseSetFilesParams(e)}parseAddInterceptParams(e){return Qn.Network.parseAddInterceptParameters(e)}parseContinueRequestParams(e){return Qn.Network.parseContinueRequestParameters(e)}parseContinueResponseParams(e){return Qn.Network.parseContinueResponseParameters(e)}parseContinueWithAuthParams(e){return Qn.Network.parseContinueWithAuthParameters(e)}parseFailRequestParams(e){return Qn.Network.parseFailRequestParameters(e)}parseProvideResponseParams(e){return Qn.Network.parseProvideResponseParameters(e)}parseRemoveInterceptParams(e){return Qn.Network.parseRemoveInterceptParameters(e)}parseSetPermissionsParams(e){return Qn.Permissions.parseSetPermissionsParams(e)}parseAddPreloadScriptParams(e){return Qn.Script.parseAddPreloadScriptParams(e)}parseCallFunctionParams(e){return Qn.Script.parseCallFunctionParams(e)}parseDisownParams(e){return Qn.Script.parseDisownParams(e)}parseEvaluateParams(e){return Qn.Script.parseEvaluateParams(e)}parseGetRealmsParams(e){return Qn.Script.parseGetRealmsParams(e)}parseRemovePreloadScriptParams(e){return Qn.Script.parseRemovePreloadScriptParams(e)}parseSubscribeParams(e){return Qn.Session.parseSubscribeParams(e)}parseDeleteCookiesParams(e){return Qn.Storage.parseDeleteCookiesParams(e)}parseGetCookiesParams(e){return Qn.Storage.parseGetCookiesParams(e)}parseSetCookieParams(e){return Qn.Storage.parseSetCookieParams(e)}};var es={};Object.defineProperty(es,"__esModule",{value:!0}),es.log=es.generatePage=void 0;const ts=l;function as(e){const t=e.split(":")[0],a=`${t}_log`,r=document.getElementById(a);if(r)return r;const n=document.getElementById("details"),s=document.createElement("div");s.className="divider",n.appendChild(s);const o=document.createElement("div");return o.className="item",o.innerHTML=`<h3>${t}</h3><div id="${a}" class="log"></div>`,n.appendChild(o),document.getElementById(a)}function rs(e){return"object"==typeof e?JSON.stringify(e,null,2):e}es.generatePage=function(){globalThis.document.documentElement&&(globalThis.document.documentElement.innerHTML='<!DOCTYPE html><title>BiDi-CDP Mapper</title><style>body{font-family: Roboto, serif; font-size: 13px; color: #202124;}.log{padding: 12px; font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; font-size: 11px; line-height: 180%; background: #f1f3f4; border-radius: 4px;}.pre{overflow-wrap: break-word; padding: 10px;}.card{margin: 60px auto; padding: 2px 0; max-width: 900px; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15), 0 1px 6px rgba(0, 0, 0, 0.2); border-radius: 8px;}.divider{height: 1px; background: #f0f0f0;}.item{padding: 16px 20px;}</style><div class="card"><div class="item"><h1>BiDi-CDP Mapper is controlling this tab</h1><p>Closing or reloading it will stop the BiDi process. <a target="_blank" title="BiDi-CDP Mapper GitHub Repository" href="https://github.com/GoogleChromeLabs/chromium-bidi">Details.</a></p></div><div class="divider"></div><details id="details"><summary class="item">Debug information</summary></details></div>',as(ts.LogType.debugInfo),as(ts.LogType.bidi),as(ts.LogType.cdp))},es.log=function(t,...a){if(!globalThis.document.documentElement)return;t.startsWith(ts.LogType.bidi)||e.window?.sendDebugMessage?.(JSON.stringify({logType:t,messages:a}));const r=as(t),n=document.createElement("div");n.className="pre",n.textContent=[t,...a].map(rs).join(" "),r.appendChild(n)};var ns={};Object.defineProperty(ns,"__esModule",{value:!0}),ns.WindowCdpTransport=ns.WindowBidiTransport=void 0;const ss=l,os=es;class is{static LOGGER_PREFIX_RECV=`${ss.LogType.bidi}:RECV \u25c2`;static LOGGER_PREFIX_SEND=`${ss.LogType.bidi}:SEND \u25b8`;#sr=null;constructor(){window.onBidiMessage=e=>{(0,os.log)(is.LOGGER_PREFIX_RECV,e);try{const t=is.#ir(e);this.#sr?.call(null,t)}catch(t){const a=t instanceof Error?t:new Error(t);this.#cr(e,"invalid argument",a,null)}}}setOnMessage(e){this.#sr=e}sendMessage(e){(0,os.log)(is.LOGGER_PREFIX_SEND,e);const t=JSON.stringify(e);window.sendBidiResponse(t)}close(){this.#sr=null,window.onBidiMessage=null}#cr(e,t,a,r){const n=is.#dr(e,t,a);r?this.sendMessage({...n,channel:r}):this.sendMessage(n)}static#ur(e){return null===e?"null":Array.isArray(e)?"array":typeof e}static#dr(e,t,a){let r;try{const t=JSON.parse(e);"object"===is.#ur(t)&&"id"in t&&(r=t.id)}catch{}return{type:"error",id:r,error:t,message:a.message}}static#ir(e){let t;try{t=JSON.parse(e)}catch{throw new Error("Cannot parse data as JSON")}const a=is.#ur(t);if("object"!==a)throw new Error(`Expected JSON object but got ${a}`);const{id:r,method:n,params:s}=t,o=is.#ur(r);if("number"!==o||!Number.isInteger(r)||r<0)throw new Error(`Expected unsigned integer but got ${o}`);const i=is.#ur(n);if("string"!==i)throw new Error(`Expected string method but got ${i}`);const c=is.#ur(s);if("object"!==c)throw new Error(`Expected object params but got ${c}`);let d=t.channel;if(void 0!==d){const e=is.#ur(d);if("string"!==e)throw new Error(`Expected string channel but got ${e}`);""===d&&(d=void 0)}return{id:r,method:n,params:s,channel:d}}}ns.WindowBidiTransport=is;ns.WindowCdpTransport=class{#sr=null;constructor(){window.cdp.onmessage=e=>{this.#sr?.call(null,e)}}setOnMessage(e){this.#sr=e}sendMessage(e){window.cdp.send(e)}close(){this.#sr=null,window.cdp.onmessage=null}},
+/**
 	 * Copyright 2021 Google LLC.
 	 * Copyright (c) Microsoft Corporation.
 	 *
@@ -16048,46 +17,5 @@
 	 *
 	 * @license
 	 */
-	Object.defineProperty(bidiTab, "__esModule", { value: true });
-	const BidiMapper_js_1 = BidiMapper;
-	const CdpConnection_js_1 = CdpConnection;
-	const log_js_1 = log$1;
-	const BidiParser_js_1 = BidiParser$1;
-	const mapperTabPage_js_1 = mapperTabPage;
-	const Transport_js_1 = Transport;
-	(0, mapperTabPage_js_1.generatePage)();
-	const mapperTabToServerTransport = new Transport_js_1.WindowBidiTransport();
-	const cdpTransport = new Transport_js_1.WindowCdpTransport();
-	/**
-	 * A CdpTransport implementation that uses the window.cdp bindings
-	 * injected by Target.exposeDevToolsProtocol.
-	 */
-	const cdpConnection = new CdpConnection_js_1.MapperCdpConnection(cdpTransport, mapperTabPage_js_1.log);
-	/**
-	 * Launches the BiDi mapper instance.
-	 * @param {string} selfTargetId
-	 * @param options Mapper options. E.g. `acceptInsecureCerts`.
-	 */
-	async function runMapperInstance(selfTargetId, options) {
-	    // eslint-disable-next-line no-console
-	    console.log('Launching Mapper instance with selfTargetId:', selfTargetId);
-	    const bidiServer = await BidiMapper_js_1.BidiServer.createAndStart(mapperTabToServerTransport, cdpConnection, 
-	    /**
-	     * Create a Browser CDP Session per Mapper instance.
-	     */
-	    await cdpConnection.createBrowserSession(), selfTargetId, options, new BidiParser_js_1.BidiParser(), mapperTabPage_js_1.log);
-	    (0, mapperTabPage_js_1.log)(log_js_1.LogType.debugInfo, 'Mapper instance has been launched');
-	    return bidiServer;
-	}
-	/**
-	 * Set `window.runMapper` to a function which launches the BiDi mapper instance.
-	 * @param selfTargetId Needed to filter out info related to BiDi target.
-	 * @param options Mapper options. E.g. `acceptInsecureCerts`. */
-	window.runMapperInstance = async (selfTargetId, options) => {
-	    await runMapperInstance(selfTargetId, options);
-	};
-
-	return bidiTab;
-
-})();
+Object.defineProperty(a,"__esModule",{value:!0});const cs=r,ds=en,us=l,ls=dn,hs=es,ps=ns;(0,hs.generatePage)();const ms=new ps.WindowBidiTransport,fs=new ps.WindowCdpTransport,gs=new ds.MapperCdpConnection(fs,hs.log);return window.runMapperInstance=async(e,t)=>{await async function(e,t){console.log("Launching Mapper instance with selfTargetId:",e);const a=await cs.BidiServer.createAndStart(ms,gs,await gs.createBrowserSession(),e,t,new ls.BidiParser,hs.log);return(0,hs.log)(us.LogType.debugInfo,"Mapper instance has been launched"),a}(e,t)},a}();
 //# sourceMappingURL=mapperTab.js.map
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 739be81..0f390bf 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -274,6 +274,10 @@
     sources += [ "chromeos/diagnostics/cros_diagnostics.mojom" ]
   }
 
+  if (use_blink_extensions_webview) {
+    sources += [ "webview/webview_media_integrity.mojom" ]
+  }
+
   public_deps = [
     ":android_mojo_bindings",
     ":authenticator_test_mojo_bindings",
diff --git a/third_party/blink/public/mojom/renderer_preferences.mojom b/third_party/blink/public/mojom/renderer_preferences.mojom
index 6eccc5a..0b1d850 100644
--- a/third_party/blink/public/mojom/renderer_preferences.mojom
+++ b/third_party/blink/public/mojom/renderer_preferences.mojom
@@ -65,8 +65,8 @@
   bool browser_handles_all_top_level_requests = false;
 
   // Cursor blink rate.
-  // Currently only changed from default on Linux.  Uses |gtk-cursor-blink|
-  // from GtkSettings.
+  // On Linux, uses |gtk-cursor-blink| from GtkSettings.
+  // On platforms with views toolkit, uses the system value from ui::NativeTheme.
   // Note: Null |caret_blink_interval| should be interpreted as the default
   // value kDefaultCaretBlinkIntervalInMilliseconds.
   mojo_base.mojom.TimeDelta? caret_blink_interval;
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index f0bad193..09c0ef7e 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -4285,6 +4285,10 @@
   kWebPrintJobAttributesFunction = 4915,
   kWebPrintJobCancelFunction = 4916,
   kWebPrintJobOnJobStateChangeEvent = 4917,
+  kFledgeAuctionReportBuyers = 4918,
+  kFledgeAuctionReportBuyerDebugModeConfig = 4919,
+
+  kFedCmButtonMode = 4920,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/mojom/webview/OWNERS b/third_party/blink/public/mojom/webview/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/third_party/blink/public/mojom/webview/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/mojom/webview/webview_media_integrity.mojom b/third_party/blink/public/mojom/webview/webview_media_integrity.mojom
new file mode 100644
index 0000000..358860f
--- /dev/null
+++ b/third_party/blink/public/mojom/webview/webview_media_integrity.mojom
@@ -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.
+
+module blink.mojom;
+
+// These error codes map to the MediaIntegrityErrorName enum in
+// media_integrity_error.idl, which is the JavaScript visible version of these
+// errors.
+//
+// Precise details for these errors (beyond the error code) are not conveyed
+// back to the renderer (and thus not the calling script either).
+enum WebViewMediaIntegrityErrorCode {
+  // There was an error. The operation may be retried.
+  kInternalError,
+  // There was an error, and the operation should not be retried.
+  //
+  // If there is no underlying implementation for token providers
+  // available on this build of WebView or there is a missing dependency,
+  // this error will be produced.
+  kNonRecoverableError,
+  // The embedding app has disabled Android WebView Media Integrity APIs.
+  //
+  // The application may enable or disable the APIs for specific origins
+  // dynamically at runtime, so API accessibility may change during the lifetime
+  // of a frame.
+  kApiDisabledByApplication,
+  // Invalid argument may arise if the content binding supplied for a token
+  // request is considered invalid by the underlying provider (such as by being
+  // too long).
+  kInvalidArgument,
+  // The token provider is no longer valid; for example, because the provider
+  // expired. The calling script may ask for a new token provider.
+  kTokenProviderInvalid,
+};
+
+// Either a token on success or an error code on failure.
+// See WebViewMediaIntegrityProvider.RequestToken for further details.
+union WebViewMediaIntegrityTokenResponse {
+  // Error code in case of a failure.
+  WebViewMediaIntegrityErrorCode error_code;
+  // A token to be returned verbatim to the calling script.
+  string token;
+};
+
+// Main entry point into the Android WebView Media Integrity API.
+// Implementations live in the browser process and are bound to individual
+// RenderFrameHosts. Remote lives in the renderer and is associated with the
+// JavaScript-visible WebView (window.android.webview) object.
+interface WebViewMediaIntegrityService {
+  // The limiting factor is JavaScript Number.MAX_SAFE_INTEGER (2**53 - 1). This
+  // isn't a limitation for the browser, but it's something which we don't
+  // expect the renderer to ever send, as it should be rejecting such numbers
+  // supplied by JavaScript scripts before getting to IPC.
+  const uint64 kMaxCloudProjectNumber = 9007199254740991;
+
+  // Attempts to acquire a token provider. If successful, the provider
+  // implementation is bound to the supplied provider_receiver and a null error
+  // code response is sent back. If there is a failure, a non-null error code
+  // will be sent back.
+  //
+  // The Android application embedding WebView may disable getting token providers
+  // through configuration, which will produce a kApiDisabledByApplication error.
+  //
+  // cloudProjectNumber has a maximum legal value of kMaxCloudProjectNumber.
+  // Messages with numbers larger than this are considered bad messages.
+  //
+  // Triggered by a (JavaScript) call to
+  // window.android.webview.getExperimentalMediaIntegrityTokenProvider(...)
+  //
+  // Common failure cases include:
+  // - The build of WebView doesn't have a provider implementation.
+  // - The system or some other dependency doesn't implement the underlying
+  //   token provisioning functionality, or is temporarily unavailable.
+  // - The embedding application has explicitly disabled the Android WebView
+  //   Media Integrity APIs.
+  // - An unknown or inappropriate cloud project number is supplied.
+  // - Some underlying provider implementation-specific failure.
+  GetIntegrityProvider(
+      pending_receiver<WebViewMediaIntegrityProvider> provider_receiver,
+      uint64 cloud_project_number)
+      => (WebViewMediaIntegrityErrorCode? error);
+};
+
+// Provides tokens upon request. Implementation lives in the browser
+// process. The remote lives in the renderer and is associated with a
+// MediaIntegrityTokenProvider object held in JavaScript.
+interface WebViewMediaIntegrityProvider {
+  // Requests a token, embedding an optional content_binding into the token.
+  //
+  // The underlying provider implementation may impose restrictions on the
+  // content_binding string (such as length), which are not defined here, though
+  // content_binding is effectively opaque data that the underlying token
+  // provider may include verbatim (in encrypted form) into the token. It is
+  // never parsed by the browser or renderer. However, as it is supplied by page
+  // scripts in UTF-16, and Mojo will perform intermediate conversions to UTF-8,
+  // any invalid Unicode (unpaired surrogates) will be mangled into \uFFFD
+  // characters. This situation is not unambiguosly visible to the browser
+  // implementation and is NOT considered a bad message.
+  //
+  // The token in the response provided by the browser should, in practice,
+  // contain only base64 characters, though this data is also opaque (not
+  // parsed) as far as the browser/renderer is concerned.
+  //
+  // Triggered by a (JavaScript) call to
+  // MediaIntegrityTokenProvider.requestToken(...)
+  //
+  // Common failure cases include:
+  // - The provider rejected the request because content_binding was invalid
+  //   (such as by being too long).
+  // - The provider rejected the request due to media integrity concerns.
+  // - Some underlying provider implementation-specific failure.
+  // - The token provider expired.
+  //
+  // Note that once a token provider is acquired, it remains usable even if the
+  // application later disables the API for the origin. (Token requests should
+  // never fail with kApiDisabledByApplication.)
+  RequestToken(string? content_binding)
+      => (WebViewMediaIntegrityTokenResponse response);
+};
diff --git a/third_party/blink/public/web/modules/media/audio/audio_device_factory.h b/third_party/blink/public/web/modules/media/audio/audio_device_factory.h
index b03b900..50c5cc77 100644
--- a/third_party/blink/public/web/modules/media/audio/audio_device_factory.h
+++ b/third_party/blink/public/web/modules/media/audio/audio_device_factory.h
@@ -57,8 +57,10 @@
   //
   // `source_type` represents the type of entity producing audio.
   // `frame_token` refers to the local RenderFrame containing the entity
-  // producing the audio. It is used to create a sink and for sharing a mixer
-  // instance among other streams with the same `frame_token`.
+  // producing the audio. It is used to create output sinks.
+  // `main_frame_token` refers to the local or remote main frame at the root of
+  // the tree containing the RenderFrame referenced by `frame_token` and is used
+  // for sharing the underlying audio output device.
   // `params` contains the device id that should be used for audio output.
   //
   // Note: These sinks do not support the blocking GetOutputDeviceInfo() API and
@@ -67,6 +69,7 @@
   virtual scoped_refptr<media::SwitchableAudioRendererSink> NewMixableSink(
       blink::WebAudioDeviceSourceType source_type,
       const blink::LocalFrameToken& frame_token,
+      const blink::FrameToken& main_frame_token,
       const media::AudioSinkParameters& params);
 
   // A helper to get device info in the absence of AudioOutputDevice.
diff --git a/third_party/blink/public/web/web_document.h b/third_party/blink/public/web/web_document.h
index a52f557..712b253 100644
--- a/third_party/blink/public/web/web_document.h
+++ b/third_party/blink/public/web/web_document.h
@@ -167,9 +167,6 @@
   // Returns true if the document has a Document Picture-in-Picture window.
   bool HasDocumentPictureInPictureWindow() const;
 
-  // Return true if  accessibility processing has been enabled.
-  bool IsAccessibilityEnabled();
-
   // Adds `callback` to the post-prerendering activation steps.
   // https://wicg.github.io/nav-speculation/prerendering.html#document-post-prerendering-activation-steps-list
   void AddPostPrerenderingActivationStep(base::OnceClosure callback);
diff --git a/third_party/blink/renderer/bindings/core/v8/script_iterator.h b/third_party/blink/renderer/bindings/core/v8/script_iterator.h
index 02ed13c5..4777b0d 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_iterator.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_iterator.h
@@ -35,18 +35,18 @@
 //     return;
 //   if (!script_iterator.IsNull()) {
 //     while (script_iterator.Next(execution_context, exception_state)) {
-//       // V8 may have thrown an exception.
-//       if (exception_state.HadException())
-//         return;
+//       // When `Next()` puts an exception on the stack, it always returns
+//       // false, thus breaking out of this loop.
+//       DCHECK(!exception_state.HadException());
 //       v8::Local<v8::Value> value =
 //           script_iterator.GetValue().ToLocalChecked();
-//       // Do something with |value|.
+//       // Do something with `value`.
 //     }
 //   }
-//   // If the very first call to Next() throws, the loop above will not be
-//   // entered, so we need to catch any exceptions here.
-//   if (exception_state.HadException())
+//   // See documentation above.
+//   if (exception_state.HadException()) {
 //     return;
+//   }
 class CORE_EXPORT ScriptIterator {
   STACK_ALLOCATED();
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise.h b/third_party/blink/renderer/bindings/core/v8/script_promise.h
index c64fea7..2bd21ff 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_promise.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise.h
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.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/wtf/allocator/allocator.h"
@@ -45,7 +46,6 @@
 namespace blink {
 
 class DOMException;
-class ExceptionState;
 class ScriptFunction;
 
 template <typename IDLResolvedType>
@@ -228,6 +228,15 @@
     resolver.Reject(value);
     return promise;
   }
+
+  static ScriptPromiseTyped<IDLResolvedType> Reject(
+      ScriptState* script_state,
+      ExceptionState& exception_state) {
+    DCHECK(exception_state.HadException());
+    auto promise = Reject(script_state, exception_state.GetException());
+    exception_state.ClearException();
+    return promise;
+  }
 };
 
 // Defined in to_v8_traits.h due to circular dependency.
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 8c784a9..86a0461 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -286,8 +286,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_channel_merger_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_channel_splitter_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_channel_splitter_options.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_chapter_information.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_chapter_information.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_chapter_information_init.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_chapter_information_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_client_query_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_client_query_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_clipboard_permission_descriptor.cc",
@@ -1898,6 +1898,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_channel_merger_node.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_channel_splitter_node.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_channel_splitter_node.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_chapter_information.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_chapter_information.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_client.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_client.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_clients.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 71d3b86..d384fc0 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -427,6 +427,7 @@
   "//third_party/blink/renderer/modules/mediarecorder/media_recorder.idl",
   "//third_party/blink/renderer/modules/mediarecorder/media_recorder_options.idl",
   "//third_party/blink/renderer/modules/mediasession/chapter_information.idl",
+  "//third_party/blink/renderer/modules/mediasession/chapter_information_init.idl",
   "//third_party/blink/renderer/modules/mediasession/media_image.idl",
   "//third_party/blink/renderer/modules/mediasession/media_metadata.idl",
   "//third_party/blink/renderer/modules/mediasession/media_metadata_init.idl",
diff --git a/third_party/blink/renderer/core/css/css_custom_font_data.h b/third_party/blink/renderer/core/css/css_custom_font_data.h
index eb66109f..3ca295e6 100644
--- a/third_party/blink/renderer/core/css/css_custom_font_data.h
+++ b/third_party/blink/renderer/core/css/css_custom_font_data.h
@@ -60,7 +60,6 @@
 
   bool IsLoading() const override { return is_loading_; }
   bool IsLoadingFallback() const override { return true; }
-  void ClearFontFaceSource() override { font_face_source_ = nullptr; }
 
   bool IsPendingDataUrl() const override {
     return font_face_source_ && font_face_source_->IsPendingDataUrl();
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 6118f32..58554867 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
@@ -2447,12 +2447,20 @@
         case CSSValueID::kContextFill:
           // context-fill cannot be use as a uri fallback
           DCHECK(!paint.resource);
-          paint.type = SVGPaintType::kContextFill;
+          if (RuntimeEnabledFeatures::SvgContextPaintEnabled()) {
+            paint.type = SVGPaintType::kContextFill;
+          } else {
+            local_identifier_value = nullptr;
+          }
           break;
         case CSSValueID::kContextStroke:
           // context-stroke cannot be use as a uri fallback
           DCHECK(!paint.resource);
-          paint.type = SVGPaintType::kContextStroke;
+          if (RuntimeEnabledFeatures::SvgContextPaintEnabled()) {
+            paint.type = SVGPaintType::kContextStroke;
+          } else {
+            local_identifier_value = nullptr;
+          }
           break;
         default:
           // For all other keywords, try to parse as a color.
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index b95699e8..d512299 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -2083,27 +2083,6 @@
   EXPECT_EQ(properties[1].types_.origin, CascadeOrigin::kAuthor);
 }
 
-// TODO(crbug.com/1095765): We should have a WPT for this test case, and the
-// Blink web test runner can now test @page rules in WPT.
-TEST_P(ParameterizedStyleResolverTest, CascadeLayersAndPageRules) {
-  GetDocument().documentElement()->setInnerHTML(R"HTML(
-    <style>
-    @page { margin-top: 100px; }
-    @layer {
-      @page { margin-top: 50px; }
-    }
-    </style>
-  )HTML");
-
-  GetDocument().GetFrame()->StartPrinting(gfx::SizeF(800, 600));
-  GetDocument().View()->UpdateLifecyclePhasesForPrinting();
-
-  WebPrintPageDescription description = GetDocument().GetPageDescription(0);
-
-  // The layered declaraion should win the cascading.
-  EXPECT_EQ(100, description.margin_top);
-}
-
 TEST_P(ParameterizedStyleResolverTest, BodyPropagationLayoutImageContain) {
   GetDocument().documentElement()->setAttribute(
       html_names::kStyleAttr,
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 2fa1463..d9d66c30 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1843,9 +1843,6 @@
 
   ComputedAccessibleNode* GetOrCreateComputedAccessibleNode(AXID ax_id);
 
-  // Return true if any accessibility contexts have been enabled.
-  bool IsAccessibilityEnabled() const { return !ax_contexts_.empty(); }
-
   void DispatchHandleLoadStart();
   void DispatchHandleLoadComplete();
 
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
index 7c05980..1853b3d 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
+++ b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
@@ -597,7 +597,8 @@
   }
   if (shadow_root->serializable() &&
       RuntimeEnabledFeatures::DeclarativeShadowDOMSerializableEnabled()) {
-    template_element->SetBooleanAttribute(html_names::kSerializableAttr, true);
+    template_element->SetBooleanAttribute(
+        html_names::kShadowrootserializableAttr, true);
   }
   if (shadow_root->clonable() &&
       RuntimeEnabledFeatures::ShadowRootClonableEnabled()) {
diff --git a/third_party/blink/renderer/core/exported/web_document.cc b/third_party/blink/renderer/core/exported/web_document.cc
index c063db4..46010457 100644
--- a/third_party/blink/renderer/core/exported/web_document.cc
+++ b/third_party/blink/renderer/core/exported/web_document.cc
@@ -326,10 +326,6 @@
   return ConstUnwrap<Document>()->HasDocumentPictureInPictureWindow();
 }
 
-bool WebDocument::IsAccessibilityEnabled() {
-  return ConstUnwrap<Document>()->IsAccessibilityEnabled();
-}
-
 void WebDocument::AddPostPrerenderingActivationStep(
     base::OnceClosure callback) {
   return Unwrap<Document>()->AddPostPrerenderingActivationStep(
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 0889137d..f1719e6e6 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -1011,18 +1011,17 @@
           loader_->local_frame_->DomWindow(),
           mojom::blink::WebFeature::kAttributionReportingCrossAppWeb);
 
-      std::vector<attribution_reporting::OsRegistrationItem>
-          registration_items =
-              attribution_reporting::ParseOsSourceOrTriggerHeader(
-                  StringUTF8Adaptor(headers.os_source).AsStringPiece());
-      if (registration_items.empty()) {
+      auto registration_items =
+          attribution_reporting::ParseOsSourceOrTriggerHeader(
+              StringUTF8Adaptor(headers.os_source).AsStringPiece());
+      if (!registration_items.has_value()) {
         LogAuditIssueAndMaybeReportHeaderError(
             headers, registration_info.report_header_errors,
             attribution_reporting::mojom::RegistrationHeaderType::kOsSource,
             std::move(reporting_origin));
         return;
       }
-      data_host_->OsSourceDataAvailable(std::move(registration_items));
+      data_host_->OsSourceDataAvailable(std::move(registration_items.value()));
       ++num_registrations_;
     }
   }
@@ -1080,18 +1079,17 @@
           loader_->local_frame_->DomWindow(),
           mojom::blink::WebFeature::kAttributionReportingCrossAppWeb);
 
-      std::vector<attribution_reporting::OsRegistrationItem>
-          registration_items =
-              attribution_reporting::ParseOsSourceOrTriggerHeader(
-                  StringUTF8Adaptor(headers.os_trigger).AsStringPiece());
-      if (registration_items.empty()) {
+      auto registration_items =
+          attribution_reporting::ParseOsSourceOrTriggerHeader(
+              StringUTF8Adaptor(headers.os_trigger).AsStringPiece());
+      if (!registration_items.has_value()) {
         LogAuditIssueAndMaybeReportHeaderError(
             headers, registration_info.report_header_errors,
             attribution_reporting::mojom::RegistrationHeaderType::kOsTrigger,
             std::move(reporting_origin));
         return;
       }
-      data_host_->OsTriggerDataAvailable(std::move(registration_items));
+      data_host_->OsTriggerDataAvailable(std::move(registration_items.value()));
       ++num_registrations_;
       break;
     }
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 86d6296..f2b986db 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1972,7 +1972,6 @@
 }
 
 void LocalDOMWindow::queueMicrotask(V8VoidFunction* callback) {
-  SetCurrentTaskAsCallbackParent(callback);
   GetAgent()->event_loop()->EnqueueMicrotask(
       WTF::BindOnce(&V8VoidFunction::InvokeAndReportException,
                     WrapPersistent(callback), nullptr));
diff --git a/third_party/blink/renderer/core/html/html_attribute_names.json5 b/third_party/blink/renderer/core/html/html_attribute_names.json5
index f6d12e0..58c3d1d7 100644
--- a/third_party/blink/renderer/core/html/html_attribute_names.json5
+++ b/third_party/blink/renderer/core/html/html_attribute_names.json5
@@ -320,11 +320,11 @@
     "scrolling",
     "select",
     "selected",
-    "serializable",
     "shadowroot",
     "shadowrootmode",
     "shadowrootclonable",
     "shadowrootdelegatesfocus",
+    "shadowrootserializable",
     "shape",
     "sharedstoragewritable",
     "size",
diff --git a/third_party/blink/renderer/core/html/html_template_element.idl b/third_party/blink/renderer/core/html/html_template_element.idl
index fc60569b..1aac910 100644
--- a/third_party/blink/renderer/core/html/html_template_element.idl
+++ b/third_party/blink/renderer/core/html/html_template_element.idl
@@ -45,18 +45,16 @@
     readonly attribute ShadowRoot? shadowRoot;
 
     // These are the declarative Shadow DOM IDL attributes, which reflect the
-    // equivalent content attributes.
+    // equivalent content attributes. Note that these are just the reflections
+    // of HTMLTemplateElement's attribute, which isn't a common use case: in
+    // normal declarative shadow DOM usage, the template element is "converted"
+    // to a shadow root; since the template no longer exists, these reflections
+    // don't have anything to reflect.
     [Reflect,ReflectOnly=("open","closed")] attribute DOMString shadowRootMode;
     [CEReactions,Reflect,RuntimeEnabled=TemplateDelegatesFocusReflection] attribute boolean shadowRootDelegatesFocus;
     [CEReactions,Reflect,RuntimeEnabled=ShadowRootClonable] attribute boolean shadowRootClonable;
+    [Reflect, RuntimeEnabled=ElementGetHTML] attribute boolean shadowRootSerializable;
 
     // Used by the DOM Parts API
     [Reflect, RuntimeEnabled=DOMPartsAPI] attribute boolean parseparts;
-
-    // Declarative shadow dom serializable attribute, which controls whether the
-    // shadow root will be serialized by getHTML({includeShadowRoots:true}).
-    // Note that this is just the reflection of HTMLTemplateElement's attribute,
-    // which isn't a common usage - in normal declarative shadow DOM usage, the
-    // template element is "converted" to a shadow root.
-    [Reflect, RuntimeEnabled=ElementGetHTML] attribute boolean serializable;
 };
diff --git a/third_party/blink/renderer/core/html/parser/html_construction_site.cc b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
index 0e8606f..d121d86 100644
--- a/third_party/blink/renderer/core/html/parser/html_construction_site.cc
+++ b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
@@ -937,7 +937,8 @@
     auto slot_assignment_mode = SlotAssignmentMode::kNamed;
     bool serializable =
         RuntimeEnabledFeatures::DeclarativeShadowDOMSerializableEnabled() &&
-        template_stack_item->GetAttributeItem(html_names::kSerializableAttr);
+        template_stack_item->GetAttributeItem(
+            html_names::kShadowrootserializableAttr);
     bool clonable;
     if (RuntimeEnabledFeatures::ShadowRootClonableEnabled()) {
       clonable = template_stack_item->GetAttributeItem(
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
index 13f5625..cec9a7e 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
@@ -1030,7 +1030,8 @@
         mojom::blink::ConsoleMessageSource::kOther,
         mojom::blink::ConsoleMessageLevel::kWarning,
         "Found declarative shadowrootmode attribute on a template, but "
-        "declarative Shadow DOM has not been enabled by includeShadowRoots."));
+        "declarative Shadow DOM is not being parsed. Use setHTMLUnsafe() "
+        "or parseHTMLUnsafe() instead."));
   } else {
     document.AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
         mojom::blink::ConsoleMessageSource::kOther,
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 ab9b158..f542ddd 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
@@ -933,34 +933,31 @@
                 border_padding_in_child_writing_mode);
       }
 
-      LayoutUnit specified_size_suggestion = LayoutUnit::Max();
-      const Length& specified_length_in_main_axis =
-          is_horizontal_flow_ ? child_style.Width() : child_style.Height();
-      // If the item’s computed main size property is definite, then the
-      // specified size suggestion is that size.
-      if (MainAxisIsInlineAxis(child)) {
+      const LayoutUnit specified_size_suggestion = ([&]() -> LayoutUnit {
+        const Length& specified_length_in_main_axis =
+            is_horizontal_flow_ ? child_style.Width() : child_style.Height();
         // TODO(https://crbug.com/313072): This (and surrounding) tests
         // should be HasAuto rather than IsAuto to account for
         // calc-size().
-        if (!specified_length_in_main_axis.IsAuto() &&
-            !InlineLengthUnresolvable(flex_basis_space,
-                                      specified_length_in_main_axis)) {
-          // Note: we may have already resolved specified_length_in_main_axis
-          // when calculating flex basis. Reusing that in the current code
-          // structure is a lot of work, so just recalculate here.
-          specified_size_suggestion = ResolveMainInlineLength(
-              flex_basis_space, child_style,
-              border_padding_in_child_writing_mode, MinMaxSizesFunc,
-              specified_length_in_main_axis, /* auto_length */ nullptr);
+        if (specified_length_in_main_axis.IsAuto()) {
+          return LayoutUnit::Max();
         }
-      } else if (!BlockLengthUnresolvable(flex_basis_space,
-                                          specified_length_in_main_axis)) {
-        specified_size_suggestion = ResolveMainBlockLength(
-            flex_basis_space, child_style, border_padding_in_child_writing_mode,
-            specified_length_in_main_axis, /* auto_length */ nullptr,
-            IntrinsicBlockSizeFunc);
-        DCHECK_NE(specified_size_suggestion, kIndefiniteSize);
-      }
+        const LayoutUnit resolved_size =
+            MainAxisIsInlineAxis(child)
+                ? ResolveMainInlineLength(
+                      flex_basis_space, child_style,
+                      border_padding_in_child_writing_mode, MinMaxSizesFunc,
+                      specified_length_in_main_axis, /* auto_length */ nullptr)
+                : ResolveMainBlockLength(flex_basis_space, child_style,
+                                         border_padding_in_child_writing_mode,
+                                         specified_length_in_main_axis,
+                                         /* auto_length */ nullptr,
+                                         IntrinsicBlockSizeFunc);
+
+        // Coerce an indefinite size to LayoutUnit::Max().
+        return resolved_size == kIndefiniteSize ? LayoutUnit::Max()
+                                                : resolved_size;
+      })();
 
       LayoutUnit transferred_size_suggestion = LayoutUnit::Max();
       if (specified_size_suggestion == LayoutUnit::Max() &&
diff --git a/third_party/blink/renderer/core/paint/svg_object_painter.cc b/third_party/blink/renderer/core/paint/svg_object_painter.cc
index 11d1b37b..e1fe03be 100644
--- a/third_party/blink/renderer/core/paint/svg_object_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_object_painter.cc
@@ -96,10 +96,12 @@
     const SVGPaint& initial_paint) {
   switch (initial_paint.type) {
     case SVGPaintType::kContextFill:
+      DCHECK(RuntimeEnabledFeatures::SvgContextPaintEnabled());
       return context_paints_
                  ? context_paints_->fill
                  : SvgContextPaints::ContextPaint(layout_object_, SVGPaint());
     case SVGPaintType::kContextStroke:
+      DCHECK(RuntimeEnabledFeatures::SvgContextPaintEnabled());
       return context_paints_
                  ? context_paints_->stroke
                  : SvgContextPaints::ContextPaint(layout_object_, SVGPaint());
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
index 316d703..537c45c 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
@@ -209,8 +209,6 @@
 
 std::optional<scheduler::TaskAttributionId>
 SoftNavigationHeuristics::GetUserInteractionAncestorTaskIfAny() {
-  using IterationStatus = scheduler::TaskAttributionTracker::IterationStatus;
-
   if (potential_soft_navigation_tasks_.empty()) {
     return std::nullopt;
   }
@@ -226,16 +224,9 @@
       return cached_result->value;
     }
     std::optional<scheduler::TaskAttributionId> ancestor_task_id;
-    // Check if any of `potential_soft_navigation_tasks_` is an ancestor of
-    // `task`.
-    tracker->ForEachAncestor(
-        *task, [&](const scheduler::TaskAttributionInfo& ancestor) {
-          if (potential_soft_navigation_tasks_.Contains(&ancestor)) {
-            ancestor_task_id = ancestor.Id();
-            return IterationStatus::kStop;
-          }
-          return IterationStatus::kContinue;
-        });
+    if (potential_soft_navigation_tasks_.Contains(task)) {
+      ancestor_task_id = task->Id();
+    }
     soft_navigation_descendant_cache_.insert(task->Id().value(),
                                              ancestor_task_id);
     return ancestor_task_id;
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc b/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc
index 299922b9..575307b 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc
@@ -63,7 +63,7 @@
   // NextId() required so that the first task ID is non-zero (because we hash on
   // key).
   auto* task = MakeGarbageCollected<scheduler::TaskAttributionInfo>(
-      scheduler::TaskAttributionId().NextId(), nullptr);
+      scheduler::TaskAttributionId().NextId());
   test_heuristics->OnCreateTaskScope(*task);
   ASSERT_TRUE(test_heuristics->GetInitialInteractionEncounteredForTest());
 }
@@ -183,8 +183,8 @@
       heuristics->CreateEventScope(
           SoftNavigationHeuristics::EventScope::Type::kClick,
           /*is_new_interaction=*/true));
-  auto* task1 = MakeGarbageCollected<scheduler::TaskAttributionInfo>(
-      current_task_id, nullptr);
+  auto* task1 =
+      MakeGarbageCollected<scheduler::TaskAttributionInfo>(current_task_id);
   heuristics->OnCreateTaskScope(*task1);
 
   scheduler::TaskAttributionIdType interaction_id1 =
@@ -198,8 +198,8 @@
       heuristics->CreateEventScope(
           SoftNavigationHeuristics::EventScope::Type::kNavigate,
           /*is_new_interaction=*/true));
-  auto* task2 = MakeGarbageCollected<scheduler::TaskAttributionInfo>(
-      current_task_id, nullptr);
+  auto* task2 =
+      MakeGarbageCollected<scheduler::TaskAttributionInfo>(current_task_id);
   heuristics->OnCreateTaskScope(*task2);
 
   scheduler::TaskAttributionIdType interaction_id2 =
diff --git a/third_party/blink/renderer/core/xml/dom_parser.cc b/third_party/blink/renderer/core/xml/dom_parser.cc
index c41848c4..69f2388 100644
--- a/third_party/blink/renderer/core/xml/dom_parser.cc
+++ b/third_party/blink/renderer/core/xml/dom_parser.cc
@@ -23,6 +23,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/document_init.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
@@ -38,6 +39,7 @@
                       .WithAgent(*window_->GetAgent())
                       .CreateDocument();
   bool include_shadow_roots =
+      RuntimeEnabledFeatures::DOMParserIncludeShadowRootsEnabled() &&
       options->hasIncludeShadowRoots() && options->includeShadowRoots();
   doc->setAllowDeclarativeShadowRoots(include_shadow_roots);
   doc->CountUse(mojom::blink::WebFeature::kParseFromString);
diff --git a/third_party/blink/renderer/core/xml/dom_parser.idl b/third_party/blink/renderer/core/xml/dom_parser.idl
index 47b5fee..f53d8b7 100644
--- a/third_party/blink/renderer/core/xml/dom_parser.idl
+++ b/third_party/blink/renderer/core/xml/dom_parser.idl
@@ -31,5 +31,8 @@
     Exposed=Window
 ] interface DOMParser {
     [CallWith=ScriptState] constructor();
+
+    // TODO(crbug.com/329330085): remove the ParseFromStringOptions options
+    // entirely, once it has been disabled via DOMParserIncludeShadowRoots.
     [NewObject] Document parseFromString(HTMLString str, SupportedType type, optional ParseFromStringOptions options = {});
 };
diff --git a/third_party/blink/renderer/core/xml/parse_from_string_options.idl b/third_party/blink/renderer/core/xml/parse_from_string_options.idl
index 86f52d50..3a57e67 100644
--- a/third_party/blink/renderer/core/xml/parse_from_string_options.idl
+++ b/third_party/blink/renderer/core/xml/parse_from_string_options.idl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(crbug.com/329330085): remove the ParseFromStringOptions options
+// entirely, once it has been disabled via DOMParserIncludeShadowRoots.
+
 dictionary ParseFromStringOptions {
   boolean includeShadowRoots = false;
 };
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h
index 94260cd..e259776dc 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h
@@ -50,6 +50,7 @@
 #include "third_party/blink/renderer/platform/network/http_header_map.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
diff --git a/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.cc b/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.cc
index 6a04af3..35de2ed 100644
--- a/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.cc
+++ b/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.h"
 
+#include "third_party/blink/public/mojom/webview/webview_media_integrity.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/extensions_webview/v8/v8_media_integrity_error_options.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 
@@ -23,6 +24,25 @@
     case V8MediaIntegrityErrorName::Enum::kTokenProviderInvalid:
       return "Token provider invalid.";
   }
+  NOTREACHED_NORETURN();
+}
+
+V8MediaIntegrityErrorName::Enum MojomToV8Enum(
+    mojom::blink::WebViewMediaIntegrityErrorCode error) {
+  switch (error) {
+    case mojom::blink::WebViewMediaIntegrityErrorCode::kInternalError:
+      return V8MediaIntegrityErrorName::Enum::kInternalError;
+    case mojom::blink::WebViewMediaIntegrityErrorCode::kNonRecoverableError:
+      return V8MediaIntegrityErrorName::Enum::kNonRecoverableError;
+    case mojom::blink::WebViewMediaIntegrityErrorCode::
+        kApiDisabledByApplication:
+      return V8MediaIntegrityErrorName::Enum::kAPIDisabledByApplication;
+    case mojom::blink::WebViewMediaIntegrityErrorCode::kInvalidArgument:
+      return V8MediaIntegrityErrorName::Enum::kInvalidArgument;
+    case mojom::blink::WebViewMediaIntegrityErrorCode::kTokenProviderInvalid:
+      return V8MediaIntegrityErrorName::Enum::kTokenProviderInvalid;
+  }
+  NOTREACHED_NORETURN();
 }
 }  // namespace
 
@@ -41,6 +61,14 @@
       GetErrorMessageForName(name), V8MediaIntegrityErrorName(name));
 }
 
+// static
+MediaIntegrityError* MediaIntegrityError::CreateFromMojomEnum(
+    mojom::blink::WebViewMediaIntegrityErrorCode error) {
+  V8MediaIntegrityErrorName::Enum name = MojomToV8Enum(error);
+  return MakeGarbageCollected<MediaIntegrityError>(
+      GetErrorMessageForName(name), V8MediaIntegrityErrorName(name));
+}
+
 MediaIntegrityError::MediaIntegrityError(String message,
                                          V8MediaIntegrityErrorName name)
     : DOMException(DOMExceptionCode::kOperationError, message),
diff --git a/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.h b/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.h
index 6ea9603..d4e6329 100644
--- a/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.h
+++ b/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_EXTENSIONS_WEBVIEW_MEDIA_INTEGRITY_MEDIA_INTEGRITY_ERROR_H_
 #define THIRD_PARTY_BLINK_RENDERER_EXTENSIONS_WEBVIEW_MEDIA_INTEGRITY_MEDIA_INTEGRITY_ERROR_H_
 
+#include "third_party/blink/public/mojom/webview/webview_media_integrity.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/extensions_webview/v8/v8_media_integrity_error_name.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -27,6 +28,9 @@
   static MediaIntegrityError* CreateForName(
       V8MediaIntegrityErrorName::Enum name);
 
+  static MediaIntegrityError* CreateFromMojomEnum(
+      mojom::blink::WebViewMediaIntegrityErrorCode error);
+
   // Use one of the Create() methods instead. This constructor has to be public
   // so that it can be used with MakeGarbageCollected<> inside the Create
   // methods.
diff --git a/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.cc b/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.cc
index dcd898bf..81960630 100644
--- a/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.cc
+++ b/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.h"
 
+#include "third_party/blink/public/mojom/webview/webview_media_integrity.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/extensions_webview/v8/v8_media_integrity_error_name.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -11,34 +12,93 @@
 #include "third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace {
+const char kInvalidContext[] = "Invalid context";
+}  // namespace
 
 namespace blink {
 
 MediaIntegrityTokenProvider::MediaIntegrityTokenProvider(
     ExecutionContext* context,
+    mojo::PendingRemote<mojom::blink::WebViewMediaIntegrityProvider>
+        provider_pending_remote,
     uint64_t cloud_project_number)
-    : cloud_project_number_(cloud_project_number) {}
+    : provider_remote_(context), cloud_project_number_(cloud_project_number) {
+  provider_remote_.Bind(std::move(provider_pending_remote),
+                        context->GetTaskRunner(TaskType::kInternalDefault));
+  provider_remote_.set_disconnect_handler(
+      WTF::BindOnce(&MediaIntegrityTokenProvider::OnProviderConnectionError,
+                    WrapWeakPersistent(this)));
+}
 
-ScriptPromise MediaIntegrityTokenProvider::requestToken(
+void MediaIntegrityTokenProvider::OnProviderConnectionError() {
+  provider_remote_.reset();
+  for (auto& resolver : token_resolvers_) {
+    resolver->Reject(MediaIntegrityError::CreateForName(
+        V8MediaIntegrityErrorName::Enum::kTokenProviderInvalid));
+  }
+  token_resolvers_.clear();
+}
+
+ScriptPromiseTyped<IDLString> MediaIntegrityTokenProvider::requestToken(
     ScriptState* script_state,
-    const String& content_binding,
+    const String& opt_content_binding,
     ExceptionState& exception_state) {
   if (!script_state->ContextIsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Invalid context");
-    return ScriptPromise();
+                                      kInvalidContext);
+    return ScriptPromiseTyped<IDLString>();
   }
-  // TODO(crbug.com/327186031): Add real implementation.
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
-  resolver->Reject(MediaIntegrityError::CreateForName(
-      V8MediaIntegrityErrorName::Enum::kNonRecoverableError));
+  ScriptPromiseResolverTyped<IDLString>* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLString>>(
+          script_state, exception_state.GetContext());
+  ScriptPromiseTyped<IDLString> promise = resolver->Promise();
+
+  if (!provider_remote_.is_bound()) {
+    // We cannot reconnect ourselves. The caller must request a new provider.
+    resolver->Reject(MediaIntegrityError::CreateForName(
+        V8MediaIntegrityErrorName::Enum::kTokenProviderInvalid));
+    return promise;
+  }
+
+  token_resolvers_.insert(resolver);
+  provider_remote_->RequestToken(
+      opt_content_binding,
+      WTF::BindOnce(&MediaIntegrityTokenProvider::OnRequestTokenResponse,
+                    WrapPersistent(this), WrapPersistent(script_state),
+                    WrapPersistent(resolver)));
   return promise;
 }
 
+void MediaIntegrityTokenProvider::OnRequestTokenResponse(
+    ScriptState* script_state,
+    ScriptPromiseResolverTyped<IDLString>* resolver,
+    const mojom::blink::WebViewMediaIntegrityTokenResponsePtr response) {
+  token_resolvers_.erase(resolver);
+
+  if (!script_state->ContextIsValid()) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError, kInvalidContext));
+    return;
+  }
+
+  if (response->is_token()) {
+    resolver->Resolve(response->get_token());
+  } else {
+    const mojom::blink::WebViewMediaIntegrityErrorCode error_code =
+        response->get_error_code();
+    resolver->Reject(MediaIntegrityError::CreateFromMojomEnum(error_code));
+  }
+}
+
 void MediaIntegrityTokenProvider::Trace(Visitor* visitor) const {
+  visitor->Trace(token_resolvers_);
+  visitor->Trace(provider_remote_);
   ScriptWrappable::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.h b/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.h
index 6c62a6d4..b81f890 100644
--- a/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.h
+++ b/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.h
@@ -5,11 +5,14 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_EXTENSIONS_WEBVIEW_MEDIA_INTEGRITY_MEDIA_INTEGRITY_TOKEN_PROVIDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_EXTENSIONS_WEBVIEW_MEDIA_INTEGRITY_MEDIA_INTEGRITY_TOKEN_PROVIDER_H_
 
+#include "third_party/blink/public/mojom/webview/webview_media_integrity.mojom-blink.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/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/extensions/webview/extensions_webview_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 
 namespace blink {
 
@@ -18,18 +21,29 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  MediaIntegrityTokenProvider(ExecutionContext* execution_context,
-                              uint64_t cloud_project_number);
+  MediaIntegrityTokenProvider(
+      ExecutionContext* execution_context,
+      mojo::PendingRemote<mojom::blink::WebViewMediaIntegrityProvider>
+          provider_pending_remote,
+      uint64_t cloud_project_number);
 
   uint64_t cloudProjectNumber() { return cloud_project_number_; }
 
-  ScriptPromise requestToken(ScriptState* script_state,
-                             const String& opt_content_binding,
-                             ExceptionState& exception_state);
+  ScriptPromiseTyped<IDLString> requestToken(ScriptState* script_state,
+                                             const String& opt_content_binding,
+                                             ExceptionState& exception_state);
 
   void Trace(Visitor*) const override;
 
  private:
+  void OnProviderConnectionError();
+  void OnRequestTokenResponse(
+      ScriptState* script_state,
+      ScriptPromiseResolverTyped<IDLString>* resolver,
+      mojom::blink::WebViewMediaIntegrityTokenResponsePtr response);
+
+  HeapHashSet<Member<ScriptPromiseResolverTyped<IDLString>>> token_resolvers_;
+  HeapMojoRemote<mojom::blink::WebViewMediaIntegrityProvider> provider_remote_;
   const uint64_t cloud_project_number_;
 };
 
diff --git a/third_party/blink/renderer/extensions/webview/web_view.cc b/third_party/blink/renderer/extensions/webview/web_view.cc
index c5ec261..18b1675 100644
--- a/third_party/blink/renderer/extensions/webview/web_view.cc
+++ b/third_party/blink/renderer/extensions/webview/web_view.cc
@@ -4,13 +4,24 @@
 
 #include "third_party/blink/renderer/extensions/webview/web_view.h"
 
+#include "base/task/single_thread_task_runner.h"
+#include "third_party/blink/public/mojom/webview/webview_media_integrity.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/extensions_webview/v8/v8_get_media_integrity_token_provider_params.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.h"
 #include "third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace {
+const char kInvalidContext[] = "Invalid context";
+}  // namespace
 
 namespace blink {
 
@@ -31,32 +42,117 @@
 
 WebView::WebView(ExecutionContext& execution_context)
     : Supplement<ExecutionContext>(execution_context),
-      ExecutionContextClient(&execution_context) {}
+      ExecutionContextClient(&execution_context),
+      media_integrity_service_remote_(&execution_context) {}
 
-ScriptPromise WebView::getExperimentalMediaIntegrityTokenProvider(
+void WebView::EnsureServiceConnection(ExecutionContext* execution_context) {
+  if (media_integrity_service_remote_.is_bound()) {
+    return;
+  }
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      execution_context->GetTaskRunner(TaskType::kInternalDefault);
+  execution_context->GetBrowserInterfaceBroker().GetInterface(
+      media_integrity_service_remote_.BindNewPipeAndPassReceiver(task_runner));
+  media_integrity_service_remote_.set_disconnect_handler(WTF::BindOnce(
+      &WebView::OnServiceConnectionError, WrapWeakPersistent(this)));
+}
+
+void WebView::OnServiceConnectionError() {
+  media_integrity_service_remote_.reset();
+  for (auto& resolver : provider_resolvers_) {
+    resolver->Reject(MediaIntegrityError::CreateForName(
+        V8MediaIntegrityErrorName::Enum::kInternalError));
+  }
+  provider_resolvers_.clear();
+}
+
+ScriptPromiseTyped<MediaIntegrityTokenProvider>
+WebView::getExperimentalMediaIntegrityTokenProvider(
     ScriptState* script_state,
     GetMediaIntegrityTokenProviderParams* params,
     ExceptionState& exception_state) {
   if (!script_state->ContextIsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Invalid context");
-    return ScriptPromise();
+                                      kInvalidContext);
+    return ScriptPromiseTyped<MediaIntegrityTokenProvider>();
   }
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
 
-  // TODO(crbug.com/327186031): Communicate with browser to instantiate a proper
-  // token provider and bind the handle here.
-  MediaIntegrityTokenProvider* provider =
-      MakeGarbageCollected<MediaIntegrityTokenProvider>(
-          ExecutionContext::From(script_state), params->cloudProjectNumber());
+  ScriptPromiseResolverTyped<MediaIntegrityTokenProvider>* resolver =
+      MakeGarbageCollected<
+          ScriptPromiseResolverTyped<MediaIntegrityTokenProvider>>(
+          script_state, exception_state.GetContext());
+  ScriptPromiseTyped<MediaIntegrityTokenProvider> promise = resolver->Promise();
 
-  resolver->Resolve(provider);
+  if (!params->hasCloudProjectNumber()) {
+    resolver->Reject(MediaIntegrityError::CreateForName(
+        V8MediaIntegrityErrorName::Enum::kInvalidArgument));
+    return promise;
+  }
+
+  const uint64_t cloud_project_number = params->cloudProjectNumber();
+
+  // This is checked in the browser also, but the browser will consider it a bad
+  // message (and has the right to ignore or kill the renderer). We want to
+  // report an error to the script instead.
+  if (cloud_project_number >
+      mojom::blink::WebViewMediaIntegrityService::kMaxCloudProjectNumber) {
+    resolver->Reject(MediaIntegrityError::CreateForName(
+        V8MediaIntegrityErrorName::Enum::kInvalidArgument));
+    return promise;
+  }
+
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  EnsureServiceConnection(execution_context);
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      execution_context->GetTaskRunner(TaskType::kInternalDefault);
+  mojo::PendingRemote<mojom::blink::WebViewMediaIntegrityProvider>
+      provider_pending_remote;
+  mojo::PendingReceiver<mojom::blink::WebViewMediaIntegrityProvider>
+      provider_pending_receiver =
+          provider_pending_remote.InitWithNewPipeAndPassReceiver();
+
+  provider_resolvers_.insert(resolver);
+  media_integrity_service_remote_->GetIntegrityProvider(
+      std::move(provider_pending_receiver), cloud_project_number,
+      WTF::BindOnce(&WebView::OnGetIntegrityProviderResponse,
+                    WrapPersistent(this), WrapPersistent(script_state),
+                    std::move(provider_pending_remote), cloud_project_number,
+                    WrapPersistent(resolver)));
+
   return promise;
 }
 
+void WebView::OnGetIntegrityProviderResponse(
+    ScriptState* script_state,
+    mojo::PendingRemote<mojom::blink::WebViewMediaIntegrityProvider>
+        provider_pending_remote,
+    const uint64_t cloud_project_number,
+    ScriptPromiseResolverTyped<MediaIntegrityTokenProvider>* resolver,
+    const std::optional<mojom::blink::WebViewMediaIntegrityErrorCode> error) {
+  provider_resolvers_.erase(resolver);
+
+  if (!script_state->ContextIsValid()) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError, kInvalidContext));
+    return;
+  }
+
+  if (error.has_value()) {
+    resolver->Reject(MediaIntegrityError::CreateFromMojomEnum(*error));
+    return;
+  }
+
+  MediaIntegrityTokenProvider* provider =
+      MakeGarbageCollected<MediaIntegrityTokenProvider>(
+          ExecutionContext::From(script_state),
+          std::move(provider_pending_remote), cloud_project_number);
+
+  resolver->Resolve(provider);
+}
+
 void WebView::Trace(Visitor* visitor) const {
+  visitor->Trace(provider_resolvers_);
+  visitor->Trace(media_integrity_service_remote_);
   Supplement<ExecutionContext>::Trace(visitor);
   ExecutionContextClient::Trace(visitor);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/extensions/webview/web_view.h b/third_party/blink/renderer/extensions/webview/web_view.h
index ce84b80..015d6d5 100644
--- a/third_party/blink/renderer/extensions/webview/web_view.h
+++ b/third_party/blink/renderer/extensions/webview/web_view.h
@@ -5,11 +5,14 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_EXTENSIONS_WEBVIEW_WEB_VIEW_H_
 #define THIRD_PARTY_BLINK_RENDERER_EXTENSIONS_WEBVIEW_WEB_VIEW_H_
 
+#include "third_party/blink/public/mojom/webview/webview_media_integrity.mojom-blink.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/extensions_webview/v8/v8_get_media_integrity_token_provider_params.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/extensions/webview/extensions_webview_export.h"
+#include "third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
@@ -26,12 +29,29 @@
 
   explicit WebView(ExecutionContext&);
 
-  ScriptPromise getExperimentalMediaIntegrityTokenProvider(
+  ScriptPromiseTyped<MediaIntegrityTokenProvider>
+  getExperimentalMediaIntegrityTokenProvider(
       ScriptState* script_state,
       GetMediaIntegrityTokenProviderParams* params,
       ExceptionState& exception_state);
 
   void Trace(Visitor*) const override;
+
+ private:
+  void EnsureServiceConnection(ExecutionContext* execution_context);
+  void OnServiceConnectionError();
+  void OnGetIntegrityProviderResponse(
+      ScriptState* script_state,
+      mojo::PendingRemote<mojom::blink::WebViewMediaIntegrityProvider>
+          provider_pending_remote,
+      uint64_t cloud_project_number,
+      ScriptPromiseResolverTyped<MediaIntegrityTokenProvider>* resolver,
+      std::optional<mojom::blink::WebViewMediaIntegrityErrorCode> error);
+
+  HeapHashSet<Member<ScriptPromiseResolverTyped<MediaIntegrityTokenProvider>>>
+      provider_resolvers_;
+  HeapMojoRemote<mojom::blink::WebViewMediaIntegrityService>
+      media_integrity_service_remote_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/extensions/webview/web_view.idl b/third_party/blink/renderer/extensions/webview/web_view.idl
index f4980145..9f85d72 100644
--- a/third_party/blink/renderer/extensions/webview/web_view.idl
+++ b/third_party/blink/renderer/extensions/webview/web_view.idl
@@ -4,7 +4,7 @@
 
 // Input dictionary for the getExperimentalMediaIntegrityTokenProvider call.
 dictionary GetMediaIntegrityTokenProviderParams {
-    unsigned long long cloudProjectNumber;
+    [EnforceRange] unsigned long long cloudProjectNumber;
 };
 
 // WebView extension APIs.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 6d5c15cb..704986a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -5445,7 +5445,7 @@
   AXRelatedObjectVector local_related_objects;
 
   // 5.1/5.5 Text inputs, Other labelable Elements
-  // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
+  // If you change this logic, update AXNodeObject::IsNameFromLabelElement, too.
   auto* html_element = DynamicTo<HTMLElement>(GetNode());
   if (html_element && html_element->IsLabelable()) {
     name_from = ax::mojom::blink::NameFrom::kRelatedElement;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 778c2c7..b80b3ea 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -2489,8 +2489,7 @@
     if (IsA<HTMLSelectElement>(listbox_candidate->GetNode()))
       return nullptr;
     // Require an ARIA role on the next sibling.
-    if (listbox_candidate->AriaRoleAttribute() !=
-        ax::mojom::blink::Role::kListBox) {
+    if (!ui::IsComboBoxContainer(listbox_candidate->AriaRoleAttribute())) {
       return nullptr;
     }
     // Naming a listbox within a composite combobox widget is not part of a
@@ -2501,7 +2500,7 @@
   }
 
   if (!listbox_candidate ||
-      listbox_candidate->RoleValue() != ax::mojom::blink::Role::kListBox) {
+      !ui::IsComboBoxContainer(listbox_candidate->RoleValue())) {
     return nullptr;
   }
 
@@ -4734,14 +4733,13 @@
   }
 
   // Step 2A from: http://www.w3.org/TR/accname-aam-1.1
-  // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
   if (IsHiddenForTextAlternativeCalculation(aria_label_or_description_root)) {
     *found_text_alternative = true;
     return String();
   }
 
   // Step 2B from: http://www.w3.org/TR/accname-aam-1.1
-  // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
+  // If you change this logic, update AXObject::IsNameFromAriaAttributet, too.
   if (!aria_label_or_description_root && !already_visited) {
     name_from = ax::mojom::blink::NameFrom::kRelatedElement;
 
@@ -4793,7 +4791,7 @@
   }
 
   // Step 2C from: http://www.w3.org/TR/accname-aam-1.1
-  // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
+  // If you change this logic, update AXObject::IsNameFromAriaAttribute, too.
   name_from = ax::mojom::blink::NameFrom::kAttribute;
   if (name_sources) {
     name_sources->push_back(
@@ -4802,7 +4800,7 @@
   }
   const AtomicString& aria_label =
       GetAOMPropertyOrARIAAttribute(AOMStringProperty::kLabel);
-  if (!aria_label.empty()) {
+  if (!aria_label.GetString().ContainsOnlyWhitespaceOrEmpty()) {
     text_alternative = aria_label;
 
     if (name_sources) {
@@ -4964,8 +4962,9 @@
 
   const AtomicString& aria_label = AccessibleNode::GetPropertyOrARIAAttribute(
       element, AOMStringProperty::kLabel);
-  if (!aria_label.empty())
+  if (!aria_label.GetString().ContainsOnlyWhitespaceOrEmpty()) {
     return true;
+  }
 
   return false;
 }
@@ -7620,10 +7619,23 @@
         return true;
       AXObject* ancestor = ParentObjectUnignored();
       while (ancestor) {
+        // If an ancestor has aria-activedescendant consider it focusable.
         if (ancestor->GetAOMPropertyOrARIAAttribute(
                 AOMRelationProperty::kActiveDescendant)) {
           return true;
         }
+        // If in a grid/treegrid that's after a combobox textfield using
+        // aria-activedescendant, then consider the row focusable.
+        if (ancestor->RoleValue() == ax::mojom::blink::Role::kGrid ||
+            ancestor->RoleValue() == ax::mojom::blink::Role::kTreeGrid) {
+          if (AXObject* ax_prev = ancestor->PreviousSiblingIncludingIgnored()) {
+            if (ax_prev->GetControlsListboxForTextfieldCombobox() == ancestor &&
+                ax_prev->GetAOMPropertyOrARIAAttribute(
+                    AOMRelationProperty::kActiveDescendant)) {
+              return true;
+            }
+          }
+        }
         if (ancestor->RoleValue() !=
                 ax::mojom::blink::Role::kGenericContainer &&
             ancestor->RoleValue() != ax::mojom::blink::Role::kNone &&
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index b536773..4fdf548 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -2814,7 +2814,7 @@
   if (AXObject* ax_ancestor_for_notification = InvalidateChildren(obj)) {
     if (ax_ancestor_for_notification->GetNode() &&
         nodes_with_pending_children_changed_.Contains(
-            ax_ancestor_for_notification->GetNode()->GetDomNodeId())) {
+            ax_ancestor_for_notification->AXObjectID())) {
       return;
     }
     ChildrenChangedWithCleanLayout(ax_ancestor_for_notification->GetNode(),
@@ -2832,7 +2832,7 @@
     CHECK(!IsFrozen());
     if (ax_ancestor_for_notification->GetNode() &&
         !nodes_with_pending_children_changed_
-             .insert(ax_ancestor_for_notification->GetNode()->GetDomNodeId())
+             .insert(ax_ancestor_for_notification->AXObjectID())
              .is_new_entry) {
       return nullptr;
     }
@@ -3403,11 +3403,6 @@
     Document& document) {
   TRACE_EVENT0("accessibility", "ProcessDeferredAccessibilityEvents");
 
-  DCHECK(GetDocument().IsAccessibilityEnabled())
-      << "ProcessDeferredAccessibilityEvents should not perform work when "
-         "accessibility is not enabled."
-      << "\n* IsPopup? " << IsPopup(document);
-
   SCOPED_UMA_HISTOGRAM_TIMER(
       "Accessibility.Performance.ProcessDeferredAccessibilityEvents");
 
@@ -5374,6 +5369,10 @@
                                  DocumentLifecycle::kLayoutClean);
   DUMP_WILL_BE_CHECK(HasDirtyObjects());
 
+  DCHECK_GE(dirty_objects_.size(), pending_events_.size())
+      << "There should be at least as many updates as events, because events "
+         "always mark a node dirty.";
+
   EnsureSerializer();
 
   ui::AXNodeData::AXNodeDataSize node_data_size;
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
index a392a95..6d49fc8 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
@@ -68,6 +68,7 @@
 #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/frame/navigator.h"
+#include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/html/fenced_frame/fenced_frame_config.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
@@ -2070,6 +2071,7 @@
 // TODO(caraitto): Consider validating keys -- no bucket base + offset
 // conflicts, no overflow, etc.
 bool CopyAuctionReportBuyerKeysFromIdlToMojo(
+    ExecutionContext& execution_context,
     ExceptionState& exception_state,
     const AuctionAdConfig& input,
     mojom::blink::AuctionAdConfig& output) {
@@ -2077,6 +2079,9 @@
     return true;
   }
 
+  UseCounter::Count(execution_context,
+                    blink::WebFeature::kFledgeAuctionReportBuyers);
+
   output.auction_ad_config_non_shared_params->auction_report_buyer_keys
       .emplace();
   for (const BigInt& value : input.auctionReportBuyerKeys()) {
@@ -2094,6 +2099,7 @@
 }
 
 bool CopyAuctionReportBuyersFromIdlToMojo(
+    ExecutionContext& execution_context,
     ExceptionState& exception_state,
     const AuctionAdConfig& input,
     mojom::blink::AuctionAdConfig& output) {
@@ -2101,6 +2107,9 @@
     return true;
   }
 
+  UseCounter::Count(execution_context,
+                    blink::WebFeature::kFledgeAuctionReportBuyers);
+
   output.auction_ad_config_non_shared_params->auction_report_buyers.emplace();
   for (const auto& [report_type_string, report_config] :
        input.auctionReportBuyers()) {
@@ -2138,6 +2147,7 @@
 }
 
 bool CopyAuctionReportBuyerDebugModeConfigFromIdlToMojo(
+    ExecutionContext& execution_context,
     ExceptionState& exception_state,
     const AuctionAdConfig& input,
     mojom::blink::AuctionAdConfig& output) {
@@ -2148,6 +2158,10 @@
     return true;
   }
 
+  UseCounter::Count(
+      execution_context,
+      blink::WebFeature::kFledgeAuctionReportBuyerDebugModeConfig);
+
   const AuctionReportBuyerDebugModeConfig* debug_mode_config =
       input.auctionReportBuyerDebugModeConfig();
   bool enabled = debug_mode_config->enabled();
@@ -2365,7 +2379,7 @@
     bool is_top_level,
     uint32_t nested_pos,
     ScriptState& script_state,
-    const ExecutionContext& context,
+    ExecutionContext& context,
     ExceptionState& exception_state,
     const ResourceFetcher& resource_fetcher,
     const AuctionAdConfig& config) {
@@ -2405,12 +2419,12 @@
                                             config, *mojo_config) ||
       !CopyPerBuyerPrioritySignalsFromIdlToMojo(exception_state, config,
                                                 *mojo_config) ||
-      !CopyAuctionReportBuyerKeysFromIdlToMojo(exception_state, config,
+      !CopyAuctionReportBuyerKeysFromIdlToMojo(context, exception_state, config,
                                                *mojo_config) ||
-      !CopyAuctionReportBuyersFromIdlToMojo(exception_state, config,
+      !CopyAuctionReportBuyersFromIdlToMojo(context, exception_state, config,
                                             *mojo_config) ||
       !CopyAuctionReportBuyerDebugModeConfigFromIdlToMojo(
-          exception_state, config, *mojo_config) ||
+          context, exception_state, config, *mojo_config) ||
       !CopyRequiredSellerSignalsFromIdlToMojo(context, exception_state, config,
                                               *mojo_config) ||
       !CopyRequestedSizeFromIdlToMojo(context, exception_state, config,
diff --git a/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc b/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
index 988195f..08a7a30d 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
@@ -2106,7 +2106,8 @@
   base::UmaHistogramEnumeration("Blink.FedCm.RpContext", rp_context);
 
   mojom::blink::RpMode rp_mode = mojom::blink::RpMode::kWidget;
-  if (blink::RuntimeEnabledFeatures::FedCmButtonModeEnabled()) {
+  if (blink::RuntimeEnabledFeatures::FedCmButtonModeEnabled(
+          resolver->GetExecutionContext())) {
     // TODO(crbug.com/1429083): add use counters for rp mode.
     rp_mode = mojo::ConvertTo<mojom::blink::RpMode>(identity_options.mode());
   }
diff --git a/third_party/blink/renderer/modules/credentialmanagement/identity_credential_request_options.idl b/third_party/blink/renderer/modules/credentialmanagement/identity_credential_request_options.idl
index 89266e87..2739342a 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/identity_credential_request_options.idl
+++ b/third_party/blink/renderer/modules/credentialmanagement/identity_credential_request_options.idl
@@ -20,7 +20,7 @@
     required sequence<IdentityProviderRequestOptions> providers;
     IdentityCredentialRequestOptionsContext context = "signin";
     // Allows an RP to select between the two UX modes of operation: button flows or widgets.
-    [RuntimeEnabled=FedCmButtonMode] IdentityCredentialRequestOptionsMode mode = "widget";
+    [RuntimeEnabled=FedCmButtonMode, MeasureAs=FedCmButtonMode] IdentityCredentialRequestOptionsMode mode = "widget";
     // A querying language that allows an RP to ask what it wants from the IdPs.
     [RuntimeEnabled=FedCmSelectiveDisclosure] IdentityStandardClaims claims;
 };
diff --git a/third_party/blink/renderer/modules/media/audio/audio_device_factory.cc b/third_party/blink/renderer/modules/media/audio/audio_device_factory.cc
index 726763c..1b4fbe0 100644
--- a/third_party/blink/renderer/modules/media/audio/audio_device_factory.cc
+++ b/third_party/blink/renderer/modules/media/audio/audio_device_factory.cc
@@ -135,6 +135,7 @@
 scoped_refptr<media::SwitchableAudioRendererSink>
 AudioDeviceFactory::NewMixableSink(blink::WebAudioDeviceSourceType source_type,
                                    const blink::LocalFrameToken& frame_token,
+                                   const blink::FrameToken& main_frame_token,
                                    const media::AudioSinkParameters& params) {
   DCHECK(IsMixable(source_type));
   DCHECK(IsMainThread()) << __func__ << "() is called on a wrong thread.";
@@ -151,7 +152,7 @@
         std::make_unique<AudioRendererMixerManager>(std::move(create_sink_cb));
   }
   return mixer_manager_->CreateInput(
-      frame_token, params.session_id, params.device_id,
+      frame_token, main_frame_token, params.session_id, params.device_id,
       AudioDeviceFactory::GetSourceLatencyType(source_type));
 }
 
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input.cc b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input.cc
index 043e9f4a..4202126 100644
--- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input.cc
+++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input.cc
@@ -22,10 +22,12 @@
 AudioRendererMixerInput::AudioRendererMixerInput(
     AudioRendererMixerPool* mixer_pool,
     const LocalFrameToken& source_frame_token,
+    const FrameToken& main_frame_token,
     std::string_view device_id,
     media::AudioLatency::Type latency)
     : mixer_pool_(mixer_pool),
       source_frame_token_(source_frame_token),
+      main_frame_token_(main_frame_token),
       device_id_(device_id),
       latency_(latency) {
   DCHECK(mixer_pool_);
@@ -73,13 +75,16 @@
   DCHECK(!started_);
   DCHECK(!mixer_);
   DCHECK(callback_);  // Initialized.
-
   DCHECK(sink_);
-  DCHECK(device_info_);
-  DCHECK_EQ(device_info_->device_status(), media::OUTPUT_DEVICE_STATUS_OK);
+
+  // It's important that `sink` has already been authorized to ensure we don't
+  // allow sharing between RenderFrames not authorized for sending audio to a
+  // given device.
+  CHECK(device_info_);
+  CHECK_EQ(device_info_->device_status(), media::OUTPUT_DEVICE_STATUS_OK);
 
   started_ = true;
-  mixer_ = mixer_pool_->GetMixer(source_frame_token_, params_, latency_,
+  mixer_ = mixer_pool_->GetMixer(main_frame_token_, params_, latency_,
                                  *device_info_, std::move(sink_));
 
   // Note: OnRenderError() may be called immediately after this call returns.
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input.h b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input.h
index 846e4579a..844d422 100644
--- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input.h
+++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input.h
@@ -38,8 +38,17 @@
     : public media::SwitchableAudioRendererSink,
       public media::AudioConverter::InputCallback {
  public:
+  // `mixer_pool` is used to request sinks and mixer instances.
+  // `source_frame_token` refers to the local RenderFrame containing the entity
+  // producing the audio. It is used to request output sinks.
+  // `main_frame_token` refers to the local or remote main frame at the root of
+  // the tree containing the RenderFrame referenced by `source_frame_token` and
+  // is used for sharing the underlying audio output device.
+  // `device_id` is the name of the output device which should be used.
+  // `latency` is used to configure buffer size for the output device.
   AudioRendererMixerInput(AudioRendererMixerPool* mixer_pool,
                           const LocalFrameToken& source_frame_token,
+                          const FrameToken& main_frame_token,
                           std::string_view device_id,
                           media::AudioLatency::Type latency);
 
@@ -119,6 +128,8 @@
   int remaining_fade_in_frames_ = 0;
 
   const LocalFrameToken source_frame_token_;
+  const FrameToken main_frame_token_;
+
   std::string device_id_;  // ID of hardware device to use
   const media::AudioLatency::Type latency_;
 
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input_test.cc b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input_test.cc
index ffd0819..442746e 100644
--- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input_test.cc
+++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_input_test.cc
@@ -51,14 +51,14 @@
 
   void CreateMixerInput(const std::string& device_id) {
     mixer_input_ = base::MakeRefCounted<AudioRendererMixerInput>(
-        this, LocalFrameToken(), device_id,
+        this, LocalFrameToken(), FrameToken(), device_id,
         media::AudioLatency::Type::kPlayback);
     mixer_input_->GetOutputDeviceInfoAsync(base::DoNothing());
     task_environment_.RunUntilIdle();
   }
 
   AudioRendererMixer* GetMixer(
-      const LocalFrameToken&,
+      const FrameToken&,
       const media::AudioParameters& params,
       media::AudioLatency::Type,
       const media::OutputDeviceInfo& sink_info,
@@ -340,7 +340,7 @@
 TEST_F(AudioRendererMixerInputTest, SwitchOutputDeviceBeforeGODIA) {
   mixer_input_->Stop();
   mixer_input_ = base::MakeRefCounted<AudioRendererMixerInput>(
-      this, LocalFrameToken(), kDefaultDeviceId,
+      this, LocalFrameToken(), FrameToken(), kDefaultDeviceId,
       media::AudioLatency::Type::kPlayback);
 
   base::RunLoop run_loop;
@@ -358,7 +358,7 @@
 TEST_F(AudioRendererMixerInputTest, SwitchOutputDeviceDuringGODIA) {
   mixer_input_->Stop();
   mixer_input_ = base::MakeRefCounted<AudioRendererMixerInput>(
-      this, LocalFrameToken(), kDefaultDeviceId,
+      this, LocalFrameToken(), FrameToken(), kDefaultDeviceId,
       media::AudioLatency::Type::kPlayback);
 
   mixer_input_->GetOutputDeviceInfoAsync(
@@ -389,7 +389,7 @@
 TEST_F(AudioRendererMixerInputTest, GODIADuringSwitchOutputDevice) {
   mixer_input_->Stop();
   mixer_input_ = base::MakeRefCounted<AudioRendererMixerInput>(
-      this, LocalFrameToken(), kDefaultDeviceId,
+      this, LocalFrameToken(), FrameToken(), kDefaultDeviceId,
       media::AudioLatency::Type::kPlayback);
 
   mixer_input_->SwitchOutputDevice(
@@ -421,7 +421,7 @@
 TEST_F(AudioRendererMixerInputTest, GODIADuringSwitchOutputDeviceWhichFails) {
   mixer_input_->Stop();
   mixer_input_ = base::MakeRefCounted<AudioRendererMixerInput>(
-      this, LocalFrameToken(), kDefaultDeviceId,
+      this, LocalFrameToken(), FrameToken(), kDefaultDeviceId,
       media::AudioLatency::Type::kPlayback);
 
   mixer_input_->SwitchOutputDevice(
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.cc b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.cc
index cf50184..acd27b37 100644
--- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.cc
+++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.cc
@@ -118,6 +118,7 @@
 
 scoped_refptr<AudioRendererMixerInput> AudioRendererMixerManager::CreateInput(
     const LocalFrameToken& source_frame_token,
+    const FrameToken& main_frame_token,
     const base::UnguessableToken& session_id,
     std::string_view device_id,
     media::AudioLatency::Type latency) {
@@ -128,21 +129,25 @@
   // TODO(crbug.com/41405939): `session_id` is always empty, delete since
   // NewAudioRenderingMixingStrategy didn't ship.
   DCHECK(session_id.is_empty());
-  return base::MakeRefCounted<AudioRendererMixerInput>(this, source_frame_token,
-                                                       device_id, latency);
+  return base::MakeRefCounted<AudioRendererMixerInput>(
+      this, source_frame_token, main_frame_token, device_id, latency);
 }
 
 AudioRendererMixer* AudioRendererMixerManager::GetMixer(
-    const LocalFrameToken& source_frame_token,
+    const FrameToken& main_frame_token,
     const media::AudioParameters& input_params,
     media::AudioLatency::Type latency,
     const media::OutputDeviceInfo& sink_info,
     scoped_refptr<media::AudioRendererSink> sink) {
   // Ownership of the sink must be given to GetMixer().
   DCHECK(sink->HasOneRef());
-  DCHECK_EQ(sink_info.device_status(), media::OUTPUT_DEVICE_STATUS_OK);
 
-  const MixerKey key(source_frame_token, input_params, latency,
+  // It's important that `sink` has already been authorized to ensure we don't
+  // allow sharing between RenderFrames not authorized for sending audio to a
+  // given device.
+  CHECK_EQ(sink_info.device_status(), media::OUTPUT_DEVICE_STATUS_OK);
+
+  const MixerKey key(main_frame_token, input_params, latency,
                      sink_info.device_id());
   base::AutoLock auto_lock(mixers_lock_);
 
@@ -227,11 +232,11 @@
 }
 
 AudioRendererMixerManager::MixerKey::MixerKey(
-    const LocalFrameToken& source_frame_token,
+    const FrameToken& main_frame_token,
     const media::AudioParameters& params,
     media::AudioLatency::Type latency,
     std::string_view device_id)
-    : source_frame_token(source_frame_token),
+    : main_frame_token(main_frame_token),
       params(params),
       latency(latency),
       device_id(device_id) {}
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h
index 7e1ea12..3ad02978 100644
--- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h
+++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager.h
@@ -58,14 +58,22 @@
 
   // Creates an AudioRendererMixerInput with the proper callbacks necessary to
   // retrieve an AudioRendererMixer instance from AudioRendererMixerManager.
+  //
   // `source_frame_token` refers to the RenderFrame containing the entity
   // rendering the audio.  Caller must ensure AudioRendererMixerManager outlives
-  // the returned input. `device_id` and `session_id` identify the output
-  // device to use. If `device_id` is empty and `session_id` is nonzero,
-  // output device associated with the opened input device designated by
-  // `session_id` is used. Otherwise, `session_id` is ignored.
+  // the returned input.
+  //
+  // `main_frame_token` refers to the local or remote main frame at the root of
+  // the tree containing the RenderFrame referenced by `source_frame_token` and
+  // is used for sharing the underlying audio output device.
+  //
+  // `device_id` and `session_id` identify the output device to use. If
+  // `device_id` is empty and `session_id` is nonzero, output device associated
+  // with the opened input device designated by `session_id` is used. Otherwise,
+  // `session_id` is ignored.
   scoped_refptr<AudioRendererMixerInput> CreateInput(
       const LocalFrameToken& source_frame_token,
+      const FrameToken& main_frame_token,
       const base::UnguessableToken& session_id,
       std::string_view device_id,
       media::AudioLatency::Type latency);
@@ -73,7 +81,7 @@
   // media::AudioRendererMixerPool implementation. The rest of the
   // implementation is kept private (see comment below).
   AudioRendererMixer* GetMixer(
-      const LocalFrameToken& source_frame_token,
+      const FrameToken& main_frame_token,
       const media::AudioParameters& input_params,
       media::AudioLatency::Type latency,
       const media::OutputDeviceInfo& sink_info,
@@ -89,13 +97,13 @@
   // Define a key so that only those AudioRendererMixerInputs from the same
   // RenderView, AudioParameters and output device can be mixed together.
   struct MixerKey {
-    MixerKey(const blink::LocalFrameToken& source_frame_token,
+    MixerKey(const blink::FrameToken& main_frame_token,
              const media::AudioParameters& params,
              media::AudioLatency::Type latency,
              std::string_view device_id);
     MixerKey(const MixerKey& other);
     ~MixerKey();
-    blink::LocalFrameToken source_frame_token;
+    blink::FrameToken main_frame_token;
     media::AudioParameters params;
     media::AudioLatency::Type latency;
     std::string device_id;
@@ -105,8 +113,13 @@
   // mixers where only irrelevant keys mismatch.
   struct MixerKeyCompare {
     bool operator()(const MixerKey& a, const MixerKey& b) const {
-      if (a.source_frame_token != b.source_frame_token) {
-        return a.source_frame_token < b.source_frame_token;
+      // `main_frame_token` allows sharing of output devices across multiple
+      // RenderFrames. We don't need to check the `device_id` to allow this
+      // sharing since an AudioRendererMixerInput can't join this sharing
+      // without having already completed authorization for `device_id` through
+      // GetOutputDeviceInfo(). See AudioRendererMixerInput::Start().
+      if (a.main_frame_token != b.main_frame_token) {
+        return a.main_frame_token < b.main_frame_token;
       }
       if (a.params.channels() != b.params.channels()) {
         return a.params.channels() < b.params.channels();
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc
index ef6dd2e8..05f8bfcb 100644
--- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc
+++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc
@@ -25,21 +25,22 @@
 namespace blink {
 
 namespace {
-const int kSampleRate = 48000;
-const int kBufferSize = 8192;
-const int kHardwareSampleRate = 44100;
-const int kHardwareBufferSize = 128;
+constexpr int kSampleRate = 48000;
+constexpr int kBufferSize = 8192;
+constexpr int kHardwareSampleRate = 44100;
+constexpr int kHardwareBufferSize = 128;
 constexpr media::ChannelLayout kChannelLayout = media::CHANNEL_LAYOUT_STEREO;
 constexpr media::ChannelLayout kAnotherChannelLayout =
     media::CHANNEL_LAYOUT_2_1;
 const char* const kDefaultDeviceId =
     media::AudioDeviceDescription::kDefaultDeviceId;
-const char kAnotherDeviceId[] = "another-device-id";
-const char kMatchedDeviceId[] = "matched-device-id";
-const char kNonexistentDeviceId[] = "nonexistent-device-id";
+constexpr char kAnotherDeviceId[] = "another-device-id";
+constexpr char kMatchedDeviceId[] = "matched-device-id";
+constexpr char kNonexistentDeviceId[] = "nonexistent-device-id";
 
-const LocalFrameToken kFrameToken;
-const LocalFrameToken kAnotherFrameToken;
+const LocalFrameToken kLocalFrameToken;
+const FrameToken kFrameToken;
+const FrameToken kAnotherFrameToken;
 }  // namespace
 
 using media::AudioLatency;
@@ -82,6 +83,7 @@
 
   enum class SinkUseState { kExistingSink, kNewSink };
   AudioRendererMixer* GetMixer(const LocalFrameToken& source_frame_token,
+                               const FrameToken& main_frame_token,
                                const media::AudioParameters& params,
                                AudioLatency::Type latency,
                                const std::string& device_id,
@@ -93,7 +95,7 @@
     if (sink_state == SinkUseState::kNewSink) {
       EXPECT_CALL(*sink, Start()).Times(1);
     }
-    return manager_->GetMixer(source_frame_token, params, latency, device_info,
+    return manager_->GetMixer(main_frame_token, params, latency, device_info,
                               std::move(sink));
   }
 
@@ -103,13 +105,14 @@
 
   scoped_refptr<AudioRendererMixerInput> CreateInputHelper(
       const LocalFrameToken& source_frame_token,
+      const FrameToken& main_frame_token,
       const base::UnguessableToken& session_id,
       const std::string& device_id,
       media::AudioLatency::Type latency,
       const media::AudioParameters params,
       media::AudioRendererSink::RenderCallback* callback) {
-    auto input = manager_->CreateInput(source_frame_token, session_id,
-                                       device_id, latency);
+    auto input = manager_->CreateInput(source_frame_token, main_frame_token,
+                                       session_id, device_id, latency);
     input->GetOutputDeviceInfoAsync(
         base::DoNothing());  // Primes input, needed for tests.
     base::RunLoop().RunUntilIdle();
@@ -171,16 +174,16 @@
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate,
       kBufferSize);
 
-  AudioRendererMixer* mixer1 =
-      GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer1 = GetMixer(
+      kLocalFrameToken, kFrameToken, params1, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer1);
   EXPECT_EQ(1u, mixer_count());
 
   // The same parameters should return the same mixer1.
-  EXPECT_EQ(mixer1,
-            GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback,
-                     kDefaultDeviceId, SinkUseState::kExistingSink));
+  EXPECT_EQ(mixer1, GetMixer(kLocalFrameToken, kFrameToken, params1,
+                             AudioLatency::Type::kPlayback, kDefaultDeviceId,
+                             SinkUseState::kExistingSink));
   EXPECT_EQ(1u, mixer_count());
 
   // Return the extra mixer we just acquired.
@@ -191,9 +194,9 @@
       AudioParameters::AUDIO_PCM_LINEAR,
       media::ChannelLayoutConfig::FromLayout<kAnotherChannelLayout>(),
       kSampleRate * 2, kBufferSize * 2);
-  AudioRendererMixer* mixer2 =
-      GetMixer(kFrameToken, params2, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer2 = GetMixer(
+      kLocalFrameToken, kFrameToken, params2, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer2);
   EXPECT_EQ(2u, mixer_count());
 
@@ -219,16 +222,16 @@
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate,
       kBufferSize);
 
-  AudioRendererMixer* mixer1 =
-      GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer1 = GetMixer(
+      kLocalFrameToken, kFrameToken, params1, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer1);
   EXPECT_EQ(1u, mixer_count());
 
   // The same parameters should return the same mixer1.
-  EXPECT_EQ(mixer1,
-            GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback,
-                     kDefaultDeviceId, SinkUseState::kExistingSink));
+  EXPECT_EQ(mixer1, GetMixer(kLocalFrameToken, kFrameToken, params1,
+                             AudioLatency::Type::kPlayback, kDefaultDeviceId,
+                             SinkUseState::kExistingSink));
   EXPECT_EQ(1u, mixer_count());
 
   // Trigger an error in mixer1.
@@ -243,9 +246,9 @@
   // Using the same params should create a new mixer due to the error.
   mock_sink_ = CreateNormalSink();
   local_sink = mock_sink_.get();
-  AudioRendererMixer* mixer2 =
-      GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer2 = GetMixer(
+      kLocalFrameToken, kFrameToken, params1, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer2);
   EXPECT_EQ(1u, mixer_count());
   EXPECT_EQ(1u, dead_mixer_count());
@@ -255,9 +258,9 @@
   local_sink->callback()->OnRenderError();
 
   // Ensure we end up with two dead mixers and not just one in this case.
-  AudioRendererMixer* mixer3 =
-      GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer3 = GetMixer(
+      kLocalFrameToken, kFrameToken, params1, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer3);
   EXPECT_EQ(1u, mixer_count());
   EXPECT_EQ(2u, dead_mixer_count());
@@ -281,9 +284,9 @@
       AudioParameters::AUDIO_PCM_LINEAR,
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate,
       kBufferSize);
-  AudioRendererMixer* mixer1 =
-      GetMixer(kFrameToken, params1, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer1 = GetMixer(
+      kLocalFrameToken, kFrameToken, params1, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer1);
   EXPECT_EQ(1u, mixer_count());
 
@@ -293,9 +296,9 @@
       AudioParameters::AUDIO_PCM_LOW_LATENCY,
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate * 2,
       kBufferSize * 2);
-  AudioRendererMixer* mixer2 =
-      GetMixer(kFrameToken, params2, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kExistingSink);
+  AudioRendererMixer* mixer2 = GetMixer(
+      kLocalFrameToken, kFrameToken, params2, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kExistingSink);
   EXPECT_EQ(mixer1, mixer2);
   EXPECT_EQ(1u, mixer_count());
   ReturnMixer(mixer2);
@@ -307,9 +310,9 @@
       media::ChannelLayoutConfig::FromLayout<kAnotherChannelLayout>(),
       kSampleRate, kBufferSize);
   ASSERT_NE(params3.channel_layout(), params1.channel_layout());
-  AudioRendererMixer* mixer3 =
-      GetMixer(kFrameToken, params3, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer3 = GetMixer(
+      kLocalFrameToken, kFrameToken, params3, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   EXPECT_NE(mixer1, mixer3);
   EXPECT_EQ(2u, mixer_count());
   ReturnMixer(mixer3);
@@ -335,33 +338,50 @@
   media::FakeAudioRenderCallback callback(0, kSampleRate);
   mock_sink_ = CreateNormalSink();
   EXPECT_CALL(*mock_sink_, Start()).Times(1);
-  auto input =
-      CreateInputHelper(kFrameToken, base::UnguessableToken(), kDefaultDeviceId,
-                        AudioLatency::Type::kPlayback, params, &callback);
+  auto input = CreateInputHelper(
+      kLocalFrameToken, kFrameToken, base::UnguessableToken(), kDefaultDeviceId,
+      AudioLatency::Type::kPlayback, params, &callback);
   EXPECT_EQ(0u, mixer_count());
-  media::FakeAudioRenderCallback another_callback(1, kSampleRate);
+  ASSERT_EQ(mock_sink_, nullptr);  // Sink is consumed by CreateInputHelper.
 
-  EXPECT_FALSE(!!mock_sink_);
-  mock_sink_ = CreateNormalSink();
-  EXPECT_CALL(*mock_sink_, Start()).Times(1);
+  // Despite being from another local frame, this input has the same main frame
+  // so should share the previously created mixer.
+  media::FakeAudioRenderCallback another_callback(1, kSampleRate);
   auto another_input = CreateInputHelper(
-      kAnotherFrameToken, base::UnguessableToken(), kDefaultDeviceId,
-      AudioLatency::Type::kPlayback, params, &another_callback);
+      LocalFrameToken(), kFrameToken, base::UnguessableToken(),
+      kDefaultDeviceId, AudioLatency::Type::kPlayback, params,
+      &another_callback);
   EXPECT_EQ(0u, mixer_count());
 
+  // Since this input uses a another main frame token, it should not be shared.
+  media::FakeAudioRenderCallback another_callback2(1, kSampleRate);
+  mock_sink_ = CreateNormalSink(kDefaultDeviceId);
+  EXPECT_CALL(*mock_sink_, Start()).Times(1);
+  auto another_input2 = CreateInputHelper(
+      kLocalFrameToken, kAnotherFrameToken, base::UnguessableToken(),
+      kDefaultDeviceId, AudioLatency::Type::kPlayback, params,
+      &another_callback2);
+  EXPECT_EQ(0u, mixer_count());
+  ASSERT_EQ(mock_sink_, nullptr);  // Sink is consumed by CreateInputHelper.
+
   // Implicitly test that AudioRendererMixerInput was provided with the expected
   // callbacks needed to acquire an AudioRendererMixer and return it.
   input->Start();
   EXPECT_EQ(1u, mixer_count());
   another_input->Start();
+  EXPECT_EQ(1u, mixer_count());
+  another_input2->Start();
   EXPECT_EQ(2u, mixer_count());
 
   // Destroying the inputs should destroy the mixers.
   input->Stop();
   input = nullptr;
-  EXPECT_EQ(1u, mixer_count());
+  EXPECT_EQ(2u, mixer_count());
   another_input->Stop();
   another_input = nullptr;
+  EXPECT_EQ(1u, mixer_count());
+  another_input2->Stop();
+  another_input2 = nullptr;
   EXPECT_EQ(0u, mixer_count());
 }
 
@@ -381,26 +401,26 @@
 
   // Empty device id, zero session id;
   auto input_to_default_device = CreateInputHelper(
-      kFrameToken, base::UnguessableToken(),  // session_id
+      kLocalFrameToken, kFrameToken, base::UnguessableToken(),  // session_id
       std::string(), AudioLatency::Type::kPlayback, params, &callback);
   EXPECT_EQ(0u, mixer_count());
 
   // Specific device id, zero session id;
   auto input_to_another_device = CreateInputHelper(
-      kFrameToken, base::UnguessableToken(),  // session_id
+      kLocalFrameToken, kFrameToken, base::UnguessableToken(),  // session_id
       kMatchedDeviceId, AudioLatency::Type::kPlayback, params, &callback);
   EXPECT_EQ(0u, mixer_count());
 
   // Specific device id, non-zero session id (to be ignored);
   auto input_to_matched_device = CreateInputHelper(
-      kFrameToken,
+      kLocalFrameToken, kFrameToken,
       base::UnguessableToken::Create(),  // session id
       kAnotherDeviceId, AudioLatency::Type::kPlayback, params, &callback);
   EXPECT_EQ(0u, mixer_count());
 
   // Empty device id, non-zero session id;
   auto input_to_matched_device_with_session_id = CreateInputHelper(
-      kFrameToken,
+      kLocalFrameToken, kFrameToken,
       base::UnguessableToken::Create(),  // session id
       std::string(), AudioLatency::Type::kPlayback, params, &callback);
   EXPECT_EQ(0u, mixer_count());
@@ -444,15 +464,15 @@
       AudioParameters::AUDIO_PCM_LINEAR,
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate,
       kBufferSize);
-  AudioRendererMixer* mixer1 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer1 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer1);
   EXPECT_EQ(1u, mixer_count());
 
-  AudioRendererMixer* mixer2 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kAnotherDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer2 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kAnotherDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer2);
   EXPECT_EQ(2u, mixer_count());
   EXPECT_NE(mixer1, mixer2);
@@ -472,15 +492,15 @@
       AudioParameters::AUDIO_PCM_LINEAR,
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate,
       kBufferSize);
-  AudioRendererMixer* mixer1 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer1 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer1);
   EXPECT_EQ(1u, mixer_count());
 
-  AudioRendererMixer* mixer2 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               std::string(), SinkUseState::kExistingSink);
+  AudioRendererMixer* mixer2 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      std::string(), SinkUseState::kExistingSink);
   ASSERT_TRUE(mixer2);
   EXPECT_EQ(1u, mixer_count());
   EXPECT_EQ(mixer1, mixer2);
@@ -501,9 +521,9 @@
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate,
       kBufferSize);
 
-  auto sink =
-      GetSink(kFrameToken, media::AudioSinkParameters(base::UnguessableToken(),
-                                                      kNonexistentDeviceId));
+  auto sink = GetSink(kLocalFrameToken,
+                      media::AudioSinkParameters(base::UnguessableToken(),
+                                                 kNonexistentDeviceId));
   auto device_info = sink->GetOutputDeviceInfo();
 
   EXPECT_EQ(media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND,
@@ -520,41 +540,41 @@
       AudioParameters::AUDIO_PCM_LINEAR,
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate,
       kBufferSize);
-  AudioRendererMixer* mixer1 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer1 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer1);
   EXPECT_EQ(1u, mixer_count());
 
-  AudioRendererMixer* mixer2 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kExistingSink);
+  AudioRendererMixer* mixer2 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kExistingSink);
   ASSERT_TRUE(mixer2);
   EXPECT_EQ(mixer1, mixer2);  // Same latency => same mixer.
   EXPECT_EQ(1u, mixer_count());
 
   AudioRendererMixer* mixer3 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kRtc, kDefaultDeviceId,
-               SinkUseState::kNewSink);
+      GetMixer(kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kRtc,
+               kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer3);
   EXPECT_NE(mixer1, mixer3);
   EXPECT_EQ(2u, mixer_count());  // Another latency => another mixer.
 
   AudioRendererMixer* mixer4 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kRtc, kDefaultDeviceId,
-               SinkUseState::kExistingSink);
+      GetMixer(kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kRtc,
+               kDefaultDeviceId, SinkUseState::kExistingSink);
   EXPECT_EQ(mixer3, mixer4);
   EXPECT_EQ(2u, mixer_count());  // Same latency => same mixer.
 
-  AudioRendererMixer* mixer5 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kInteractive,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer5 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kInteractive,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer5);
   EXPECT_EQ(3u, mixer_count());  // Another latency => another mixer.
 
-  AudioRendererMixer* mixer6 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kInteractive,
-               kDefaultDeviceId, SinkUseState::kExistingSink);
+  AudioRendererMixer* mixer6 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kInteractive,
+      kDefaultDeviceId, SinkUseState::kExistingSink);
   EXPECT_EQ(mixer5, mixer6);
   EXPECT_EQ(3u, mixer_count());  // Same latency => same mixer.
 
@@ -582,43 +602,43 @@
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), kSampleRate,
       kBufferSize);
   params.set_effects(1);
-  AudioRendererMixer* mixer1 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer1 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer1);
   EXPECT_EQ(1u, mixer_count());
 
-  AudioRendererMixer* mixer2 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kExistingSink);
+  AudioRendererMixer* mixer2 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kExistingSink);
   ASSERT_TRUE(mixer2);
   EXPECT_EQ(mixer1, mixer2);  // Same effects => same mixer.
   EXPECT_EQ(1u, mixer_count());
 
   params.set_effects(2);
-  AudioRendererMixer* mixer3 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer3 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer3);
   EXPECT_NE(mixer1, mixer3);
   EXPECT_EQ(2u, mixer_count());  // Another effects => another mixer.
 
-  AudioRendererMixer* mixer4 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kExistingSink);
+  AudioRendererMixer* mixer4 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kExistingSink);
   EXPECT_EQ(mixer3, mixer4);
   EXPECT_EQ(2u, mixer_count());  // Same effects => same mixer.
 
   params.set_effects(3);
-  AudioRendererMixer* mixer5 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer5 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
   ASSERT_TRUE(mixer5);
   EXPECT_EQ(3u, mixer_count());  // Another effects => another mixer.
 
-  AudioRendererMixer* mixer6 =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kExistingSink);
+  AudioRendererMixer* mixer6 = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kExistingSink);
   EXPECT_EQ(mixer5, mixer6);
   EXPECT_EQ(3u, mixer_count());  // Same effects => same mixer.
 
@@ -655,8 +675,8 @@
   params.set_latency_tag(AudioLatency::Type::kPlayback);
 
   AudioRendererMixer* mixer =
-      GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId,
-               SinkUseState::kNewSink);
+      GetMixer(kLocalFrameToken, kFrameToken, params, params.latency_tag(),
+               kDefaultDeviceId, SinkUseState::kNewSink);
 
   if (AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())) {
     // Expecting input sample rate
@@ -703,8 +723,8 @@
   params.set_latency_tag(AudioLatency::Type::kPlayback);
 
   AudioRendererMixer* mixer =
-      GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId,
-               SinkUseState::kNewSink);
+      GetMixer(kLocalFrameToken, kFrameToken, params, params.latency_tag(),
+               kDefaultDeviceId, SinkUseState::kNewSink);
 
   // 20 ms at 44100 is 882 frames per buffer.
   if (AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())) {
@@ -741,9 +761,9 @@
       AudioParameters::AUDIO_PCM_LINEAR,
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), 32000, 512);
 
-  AudioRendererMixer* mixer =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kPlayback,
-               kDefaultDeviceId, SinkUseState::kNewSink);
+  AudioRendererMixer* mixer = GetMixer(
+      kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kPlayback,
+      kDefaultDeviceId, SinkUseState::kNewSink);
 
   // Expecting input sample rate
   EXPECT_EQ(32000, mixer->get_output_params_for_testing().sample_rate());
@@ -781,8 +801,8 @@
   params.set_latency_tag(AudioLatency::Type::kRtc);
 
   AudioRendererMixer* mixer =
-      GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId,
-               SinkUseState::kNewSink);
+      GetMixer(kLocalFrameToken, kFrameToken, params, params.latency_tag(),
+               kDefaultDeviceId, SinkUseState::kNewSink);
 
   int output_sample_rate =
       AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())
@@ -824,8 +844,8 @@
       media::ChannelLayoutConfig::FromLayout<kChannelLayout>(), 32000, 512);
 
   AudioRendererMixer* mixer =
-      GetMixer(kFrameToken, params, AudioLatency::Type::kRtc, kDefaultDeviceId,
-               SinkUseState::kNewSink);
+      GetMixer(kLocalFrameToken, kFrameToken, params, AudioLatency::Type::kRtc,
+               kDefaultDeviceId, SinkUseState::kNewSink);
 
   // Expecting input sample rate.
   EXPECT_EQ(32000, mixer->get_output_params_for_testing().sample_rate());
@@ -856,8 +876,8 @@
   params.set_latency_tag(AudioLatency::Type::kInteractive);
 
   AudioRendererMixer* mixer =
-      GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId,
-               SinkUseState::kNewSink);
+      GetMixer(kLocalFrameToken, kFrameToken, params, params.latency_tag(),
+               kDefaultDeviceId, SinkUseState::kNewSink);
 
   if (AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())) {
     // Expecting input sample rate.
@@ -890,8 +910,8 @@
   params.set_latency_tag(AudioLatency::Type::kPlayback);
 
   AudioRendererMixer* mixer =
-      GetMixer(kFrameToken, params, params.latency_tag(), kDefaultDeviceId,
-               SinkUseState::kNewSink);
+      GetMixer(kLocalFrameToken, kFrameToken, params, params.latency_tag(),
+               kDefaultDeviceId, SinkUseState::kNewSink);
 
   // Output parameters should be the same as input properties for bitstream
   // formats.
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_pool.h b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_pool.h
index ee5bce6..2879650b 100644
--- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_pool.h
+++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_pool.h
@@ -39,8 +39,11 @@
   // discard if an existing mixer can be reused. Clients must have called
   // GetOutputDeviceInfoAsync() on `sink` to get `sink_info`, and it must have
   // a device_status() == OUTPUT_DEVICE_STATUS_OK.
+  //
+  // `main_frame_token` is used to determine when mixers can be shared among
+  // multiple AudioRenderMixerInput instances.
   virtual AudioRendererMixer* GetMixer(
-      const LocalFrameToken& source_frame_token,
+      const FrameToken& main_frame_token,
       const media::AudioParameters& input_params,
       media::AudioLatency::Type latency,
       const media::OutputDeviceInfo& sink_info,
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_test.cc b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_test.cc
index c01ba019..e3a49c76 100644
--- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_test.cc
+++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_test.cc
@@ -95,7 +95,7 @@
   AudioRendererMixerTest(const AudioRendererMixerTest&) = delete;
   AudioRendererMixerTest& operator=(const AudioRendererMixerTest&) = delete;
 
-  AudioRendererMixer* GetMixer(const LocalFrameToken&,
+  AudioRendererMixer* GetMixer(const FrameToken&,
                                const media::AudioParameters&,
                                media::AudioLatency::Type,
                                const media::OutputDeviceInfo&,
@@ -397,7 +397,7 @@
 
   scoped_refptr<AudioRendererMixerInput> CreateMixerInput() {
     auto input = base::MakeRefCounted<AudioRendererMixerInput>(
-        this, LocalFrameToken(),
+        this, LocalFrameToken(), FrameToken(),
         // default device ID.
         std::string(), media::AudioLatency::Type::kPlayback);
     input->GetOutputDeviceInfoAsync(
diff --git a/third_party/blink/renderer/modules/mediarecorder/BUILD.gn b/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
index f05ad3e..f30411e 100644
--- a/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediarecorder/BUILD.gn
@@ -42,6 +42,7 @@
   deps = [
     "//build:chromeos_buildflags",
     "//media",
+    "//media/gpu:gpu",
     "//media/mojo/clients",
     "//media/mojo/mojom",
     "//third_party/blink/renderer/modules/mediastream",
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
index f3209db7..910e7ae 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
@@ -557,4 +557,8 @@
   ExecutionContextLifecycleObserver::Trace(visitor);
 }
 
+void MediaRecorder::UpdateAudioBitrate(uint32_t bits_per_second) {
+  audio_bits_per_second_ = bits_per_second;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder.h b/third_party/blink/renderer/modules/mediarecorder/media_recorder.h
index 1b3351e..9a72be6a 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder.h
@@ -95,6 +95,8 @@
 
   void Trace(Visitor* visitor) const override;
 
+  void UpdateAudioBitrate(uint32_t bits_per_second);
+
  private:
   void CreateBlobEvent(Blob* blob, double timecode);
 
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
index 2dfc82c..96f81631 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
@@ -49,6 +49,10 @@
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
+#if BUILDFLAG(IS_WIN)
+#include "media/gpu/windows/mf_audio_encoder.h"
+#endif
+
 using base::TimeTicks;
 
 namespace blink {
@@ -480,6 +484,16 @@
         audio_codec, use_video_tracks, use_audio_tracks,
         std::make_unique<media::Mp4MuxerDelegate>(write_callback),
         optional_timeslice);
+
+#if BUILDFLAG(IS_WIN)
+    // Windows OS uses MediaFoundation for MP4 muxing, which requires the
+    // specific audio bit rate for AAC encoding.
+    if (audio_bits_per_second_ != 0u) {
+      audio_bits_per_second_ =
+          media::MFAudioEncoder::ClampAccCodecBitrate(audio_bits_per_second_);
+      recorder_->UpdateAudioBitrate(audio_bits_per_second_);
+    }
+#endif
   } else {
     muxer = std::make_unique<media::WebmMuxer>(
         audio_codec, use_video_tracks, use_audio_tracks,
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
index c2185f6..0aca164d 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
@@ -58,8 +58,12 @@
 using ::testing::TestWithParam;
 using ::testing::ValuesIn;
 
-#if BUILDFLAG(IS_WIN) || \
-    (BUILDFLAG(IS_MAC) && BUILDFLAG(USE_PROPRIETARY_CODECS))
+#if BUILDFLAG(IS_WIN)
+#include "media/gpu/windows/mf_audio_encoder.h"
+#define HAS_AAC_ENCODER 1
+#endif
+
+#if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_PROPRIETARY_CODECS)
 #define HAS_AAC_ENCODER 1
 #endif
 
@@ -203,8 +207,10 @@
   void OnEncodedAudioForTesting(const media::AudioParameters& params,
                                 std::string encoded_data,
                                 base::TimeTicks timestamp) {
+    media::AudioEncoder::CodecDescription codec_description = {99};
     media_recorder_handler_->OnEncodedAudio(params, std::move(encoded_data),
-                                            std::nullopt, timestamp);
+                                            std::move(codec_description),
+                                            timestamp);
   }
 
   void OnAudioBusForTesting(const media::AudioBus& audio_bus) {
@@ -1226,13 +1232,11 @@
     {false, "video/x-matroska", "avc1.640029"},
     {false, "video/x-matroska", "avc1.640034"},
     {true, "video/x-matroska", "avc1.64000c,pcm"},
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
     {false, "video/mp4", "avc1.42000c"},  // H264PROFILE_BASELINE
     {false, "video/mp4", "avc1.4d000c"},  // H264PROFILE_MAIN
     {false, "video/mp4", "avc1.64000c"},  // H264PROFILE_HIGH
     {false, "video/mp4", "avc1.640029"},
     {false, "video/mp4", "avc1.640034"},
-#endif
 };
 
 class MediaRecorderHandlerH264ProfileTest
@@ -1277,7 +1281,51 @@
                          MediaRecorderHandlerH264ProfileTest,
                          ValuesIn(kH264ProfileTestParams));
 
-#endif
+#if BUILDFLAG(IS_WIN)
+class MediaRecorderHandlerWinAacCodecTest : public TestWithParam<unsigned int>,
+                                            public MediaRecorderHandlerFixture {
+ public:
+  MediaRecorderHandlerWinAacCodecTest()
+      : MediaRecorderHandlerFixture(false, true) {
+    scoped_feature_list_.InitAndEnableFeature(kMediaRecorderEnableMp4Muxer);
+  }
+
+  MediaRecorderHandlerWinAacCodecTest(
+      const MediaRecorderHandlerWinAacCodecTest&) = delete;
+  MediaRecorderHandlerWinAacCodecTest& operator=(
+      const MediaRecorderHandlerWinAacCodecTest&) = delete;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_P(MediaRecorderHandlerWinAacCodecTest, AudioBitsPerSeconds) {
+  AddTracks();
+
+  V8TestingScope scope;
+  auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
+
+  const String mime_type("audio/mp4");
+  const String codecs("mp4a.40.2");
+  EXPECT_TRUE(media_recorder_handler_->Initialize(
+      recorder, registry_.test_stream(), mime_type, codecs,
+      AudioTrackRecorder::BitrateMode::kVariable));
+  media_recorder_handler_->Start(0, "", GetParam(), 0);
+
+  EXPECT_EQ(media::MFAudioEncoder::ClampAccCodecBitrate(GetParam()),
+            recorder->audioBitsPerSecond());
+
+  media_recorder_handler_->Stop();
+  media_recorder_handler_ = nullptr;
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         MediaRecorderHandlerWinAacCodecTest,
+                         ValuesIn({5000u, 96000u, 128000u, 160000u, 192000u,
+                                   256000u, 300000u}));
+
+#endif  // BUILDFLAG(IS_WIN)
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 struct MediaRecorderPassthroughTestParams {
   const char* mime_type;
diff --git a/third_party/blink/renderer/modules/mediasession/BUILD.gn b/third_party/blink/renderer/modules/mediasession/BUILD.gn
index fbcfd83d9..f3e47c2 100644
--- a/third_party/blink/renderer/modules/mediasession/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediasession/BUILD.gn
@@ -6,6 +6,8 @@
 
 blink_modules_sources("mediasession") {
   sources = [
+    "chapter_information.cc",
+    "chapter_information.h",
     "media_metadata.cc",
     "media_metadata.h",
     "media_metadata_sanitizer.cc",
@@ -14,5 +16,7 @@
     "media_session.h",
     "media_session_type_converters.cc",
     "media_session_type_converters.h",
+    "media_session_utils.cc",
+    "media_session_utils.h",
   ]
 }
diff --git a/third_party/blink/renderer/modules/mediasession/chapter_information.cc b/third_party/blink/renderer/modules/mediasession/chapter_information.cc
new file mode 100644
index 0000000..ca62c37f
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediasession/chapter_information.cc
@@ -0,0 +1,94 @@
+// 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 "third_party/blink/renderer/modules/mediasession/chapter_information.h"
+
+#include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_chapter_information_init.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_metadata_init.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/modules/mediasession/media_session.h"
+#include "third_party/blink/renderer/modules/mediasession/media_session_utils.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+
+namespace blink {
+
+// static
+ChapterInformation* ChapterInformation::From(
+    ScriptState* script_state,
+    const ChapterInformationInit* chapter,
+    ExceptionState& exception_state) {
+  return MakeGarbageCollected<ChapterInformation>(
+      script_state, chapter->title(), chapter->startTime(), chapter->artwork(),
+      exception_state);
+}
+
+ChapterInformation* ChapterInformation::Create(
+    ScriptState* script_state,
+    const String& title,
+    const double& start_time,
+    const HeapVector<Member<MediaImage>>& artwork,
+    ExceptionState& exception_state) {
+  return MakeGarbageCollected<ChapterInformation>(
+      script_state, title, start_time, artwork, exception_state);
+}
+
+ChapterInformation::ChapterInformation(
+    ScriptState* script_state,
+    const String& title,
+    const double& start_time,
+    const HeapVector<Member<MediaImage>>& artwork,
+    ExceptionState& exception_state)
+    : title_(title), start_time_(start_time) {
+  SetArtworkInternal(script_state, artwork, exception_state);
+}
+
+String ChapterInformation::title() const {
+  return title_;
+}
+
+double ChapterInformation::startTime() const {
+  return start_time_;
+}
+
+const HeapVector<Member<MediaImage>>& ChapterInformation::artwork() const {
+  return artwork_;
+}
+
+v8::LocalVector<v8::Value> ChapterInformation::artwork(
+    ScriptState* script_state) const {
+  v8::LocalVector<v8::Value> result(script_state->GetIsolate(),
+                                    artwork_.size());
+
+  for (wtf_size_t i = 0; i < artwork_.size(); ++i) {
+    result[i] =
+        FreezeV8Object(ToV8Traits<MediaImage>::ToV8(script_state, artwork_[i]),
+                       script_state->GetIsolate());
+  }
+
+  return result;
+}
+
+void ChapterInformation::Trace(Visitor* visitor) const {
+  visitor->Trace(artwork_);
+  ScriptWrappable::Trace(visitor);
+}
+
+void ChapterInformation::SetArtworkInternal(
+    ScriptState* script_state,
+    const HeapVector<Member<MediaImage>>& artwork,
+    ExceptionState& exception_state) {
+  HeapVector<Member<MediaImage>> processed_artwork =
+      media_session_utils::ProcessArtworkVector(script_state, artwork,
+                                                exception_state);
+  if (processed_artwork.empty()) {
+    return;
+  }
+  artwork_.swap(processed_artwork);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediasession/chapter_information.h b/third_party/blink/renderer/modules/mediasession/chapter_information.h
new file mode 100644
index 0000000..088bbba
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediasession/chapter_information.h
@@ -0,0 +1,68 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_CHAPTER_INFORMATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_CHAPTER_INFORMATION_H_
+
+#include "third_party/blink/renderer/bindings/modules/v8/v8_chapter_information_init.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_image.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.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/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class ChapterInformationInit;
+class ExceptionState;
+class ScriptState;
+
+// Implementation of `ChapterInformation` interface from the Media Session API.
+class MODULES_EXPORT ChapterInformation final : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static ChapterInformation* From(ScriptState* script_state,
+                                  const ChapterInformationInit* chapter,
+                                  ExceptionState& exception_state);
+
+  static ChapterInformation* Create(
+      ScriptState* script_state,
+      const String& title,
+      const double& start_time,
+      const HeapVector<Member<MediaImage>>& artwork,
+      ExceptionState& exception_state);
+
+  ChapterInformation(ScriptState* script_state,
+                     const String& title,
+                     const double& start_time,
+                     const HeapVector<Member<MediaImage>>& artwork,
+                     ExceptionState& exception_state);
+
+  String title() const;
+  double startTime() const;
+  v8::LocalVector<v8::Value> artwork(ScriptState*) const;
+
+  // Internal use only, returns a reference to m_artwork instead of a Frozen
+  // copy of a `MediaImage` array.
+  const HeapVector<Member<MediaImage>>& artwork() const;
+
+  void Trace(Visitor*) const override;
+
+ private:
+  // Make an internal copy of the MediaImage vector with some internal steps
+  // such as parsing of the src property.
+  void SetArtworkInternal(ScriptState*,
+                          const HeapVector<Member<MediaImage>>&,
+                          ExceptionState&);
+
+  const String title_;
+  const double start_time_;
+  HeapVector<Member<MediaImage>> artwork_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_CHAPTER_INFORMATION_H_
diff --git a/third_party/blink/renderer/modules/mediasession/chapter_information.idl b/third_party/blink/renderer/modules/mediasession/chapter_information.idl
index 6bd102f..2b13591 100644
--- a/third_party/blink/renderer/modules/mediasession/chapter_information.idl
+++ b/third_party/blink/renderer/modules/mediasession/chapter_information.idl
@@ -4,8 +4,11 @@
 
 // https://wicg.github.io/mediasession/#dictdef-chapterinformation
 
-dictionary ChapterInformation {
-  DOMString title = "";
-  double startTime = 0;
-  sequence<MediaImage> artwork;
+[
+    Exposed=Window,
+    RuntimeEnabled=MediaSessionChapterInformation
+] interface ChapterInformation {
+  readonly attribute DOMString title;
+  readonly attribute double startTime;
+  [SameObject] readonly attribute FrozenArray<MediaImage> artwork;
 };
diff --git a/third_party/blink/renderer/modules/mediasession/chapter_information_init.idl b/third_party/blink/renderer/modules/mediasession/chapter_information_init.idl
new file mode 100644
index 0000000..ffa9741
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediasession/chapter_information_init.idl
@@ -0,0 +1,11 @@
+// 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.
+
+// https://wicg.github.io/mediasession/#dictdef-chapterinformation
+
+dictionary ChapterInformationInit {
+  DOMString title = "";
+  double startTime = 0;
+  sequence<MediaImage> artwork = [];
+};
diff --git a/third_party/blink/renderer/modules/mediasession/media_metadata.cc b/third_party/blink/renderer/modules/mediasession/media_metadata.cc
index 5d49905..12ceea8d 100644
--- a/third_party/blink/renderer/modules/mediasession/media_metadata.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_metadata.cc
@@ -8,40 +8,17 @@
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_chapter_information.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_chapter_information_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_media_metadata_init.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/modules/mediasession/chapter_information.h"
 #include "third_party/blink/renderer/modules/mediasession/media_session.h"
+#include "third_party/blink/renderer/modules/mediasession/media_session_utils.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 
 namespace blink {
 
-namespace {
-
-// Processes the `MediaImage` with a parsed url in the artwork list. Returns an
-// empty list when any exception happens.
-HeapVector<Member<MediaImage>> ProcessArtworkVector(
-    ScriptState* script_state,
-    const HeapVector<Member<MediaImage>>& artwork,
-    ExceptionState& exception_state) {
-  HeapVector<Member<MediaImage>> processed_artwork(artwork);
-
-  for (MediaImage* image : processed_artwork) {
-    KURL url = ExecutionContext::From(script_state)->CompleteURL(image->src());
-    if (!url.IsValid()) {
-      exception_state.ThrowTypeError("'" + image->src() +
-                                     "' can't be resolved to a valid URL.");
-      return {};
-    }
-    image->setSrc(url);
-  }
-
-  DCHECK(!exception_state.HadException());
-  return processed_artwork;
-}
-
-}  // namespace
-
 // static
 MediaMetadata* MediaMetadata::Create(ScriptState* script_state,
                                      const MediaMetadataInit* metadata,
@@ -61,6 +38,8 @@
   artist_ = metadata->artist();
   album_ = metadata->album();
   SetArtworkInternal(script_state, metadata->artwork(), exception_state);
+  SetChapterInfoFromInit(script_state, metadata->chapterInfo(),
+                         exception_state);
 }
 
 String MediaMetadata::title() const {
@@ -104,9 +83,9 @@
                                     chapterInfo_.size());
 
   for (wtf_size_t i = 0; i < chapterInfo_.size(); ++i) {
-    result[i] = FreezeV8Object(
-        ToV8Traits<ChapterInformation>::ToV8(script_state, chapterInfo_[i]),
-        script_state->GetIsolate());
+    result[i] = FreezeV8Object(ToV8Traits<blink::ChapterInformation>::ToV8(
+                                   script_state, chapterInfo_[i]),
+                               script_state->GetIsolate());
   }
 
   return result;
@@ -134,24 +113,6 @@
   NotifySessionAsync();
 }
 
-void MediaMetadata::setChapterInfo(
-    ScriptState* script_state,
-    const HeapVector<Member<ChapterInformation>>& chapter_info,
-    ExceptionState& exception_state) {
-  HeapVector<Member<ChapterInformation>> processed_chapters(chapter_info);
-
-  for (ChapterInformation* chapter : processed_chapters) {
-    HeapVector<Member<MediaImage>> processed_artwork =
-        ProcessArtworkVector(script_state, chapter->artwork(), exception_state);
-    if (processed_artwork.empty()) {
-      return;
-    }
-    chapter->setArtwork(processed_artwork);
-  }
-  chapterInfo_.swap(processed_chapters);
-  NotifySessionAsync();
-}
-
 void MediaMetadata::SetSession(MediaSession* session) {
   session_ = session;
 }
@@ -173,13 +134,27 @@
     const HeapVector<Member<MediaImage>>& artwork,
     ExceptionState& exception_state) {
   HeapVector<Member<MediaImage>> processed_artwork =
-      ProcessArtworkVector(script_state, artwork, exception_state);
+      media_session_utils::ProcessArtworkVector(script_state, artwork,
+                                                exception_state);
   if (processed_artwork.empty()) {
     return;
   }
   artwork_.swap(processed_artwork);
 }
 
+void MediaMetadata::SetChapterInfoFromInit(
+    ScriptState* script_state,
+    const HeapVector<Member<ChapterInformationInit>>& chapter_info,
+    ExceptionState& exception_state) {
+  HeapVector<Member<ChapterInformation>> processed_chapters;
+  for (ChapterInformationInit* init_chapter : chapter_info) {
+    auto* chapter =
+        ChapterInformation::From(script_state, init_chapter, exception_state);
+    processed_chapters.push_back(chapter);
+  }
+  chapterInfo_.swap(processed_chapters);
+}
+
 void MediaMetadata::Trace(Visitor* visitor) const {
   visitor->Trace(artwork_);
   visitor->Trace(chapterInfo_);
diff --git a/third_party/blink/renderer/modules/mediasession/media_metadata.h b/third_party/blink/renderer/modules/mediasession/media_metadata.h
index 52b33437..99a87a25 100644
--- a/third_party/blink/renderer/modules/mediasession/media_metadata.h
+++ b/third_party/blink/renderer/modules/mediasession/media_metadata.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_MEDIA_METADATA_H_
 
 #include "third_party/blink/renderer/bindings/modules/v8/v8_chapter_information.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_chapter_information_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_media_image.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -16,6 +17,7 @@
 
 namespace blink {
 
+class ChapterInformation;
 class ExceptionState;
 class MediaMetadataInit;
 class MediaSession;
@@ -54,9 +56,6 @@
   void setArtwork(ScriptState*,
                   const HeapVector<Member<MediaImage>>&,
                   ExceptionState&);
-  void setChapterInfo(ScriptState*,
-                      const HeapVector<Member<ChapterInformation>>&,
-                      ExceptionState&);
 
   // Called by MediaSession to associate or de-associate itself.
   void SetSession(MediaSession*);
@@ -79,6 +78,11 @@
                           const HeapVector<Member<MediaImage>>&,
                           ExceptionState&);
 
+  // Set the `ChapterInfo` from `ChapterInformationInit` list.
+  void SetChapterInfoFromInit(ScriptState*,
+                              const HeapVector<Member<ChapterInformationInit>>&,
+                              ExceptionState&);
+
   String title_;
   String artist_;
   String album_;
diff --git a/third_party/blink/renderer/modules/mediasession/media_metadata.idl b/third_party/blink/renderer/modules/mediasession/media_metadata.idl
index d97f048b..64a02110 100644
--- a/third_party/blink/renderer/modules/mediasession/media_metadata.idl
+++ b/third_party/blink/renderer/modules/mediasession/media_metadata.idl
@@ -13,5 +13,5 @@
     attribute DOMString artist;
     attribute DOMString album;
     [CallWith=ScriptState, RaisesException=Setter] attribute FrozenArray<MediaImage> artwork;
-    [RuntimeEnabled=MediaSessionChapterInformation, CallWith=ScriptState, RaisesException=Setter] attribute FrozenArray<ChapterInformation> chapterInfo;
+    [RuntimeEnabled=MediaSessionChapterInformation, CallWith=ScriptState, RaisesException=Setter, SameObject] readonly attribute FrozenArray<ChapterInformation> chapterInfo;
 };
diff --git a/third_party/blink/renderer/modules/mediasession/media_metadata_init.idl b/third_party/blink/renderer/modules/mediasession/media_metadata_init.idl
index cbeff95..a783234f 100644
--- a/third_party/blink/renderer/modules/mediasession/media_metadata_init.idl
+++ b/third_party/blink/renderer/modules/mediasession/media_metadata_init.idl
@@ -9,5 +9,5 @@
     DOMString artist = "";
     DOMString album = "";
     sequence<MediaImage> artwork = [];
-    sequence<ChapterInformation> chapterInfo = [];
+    sequence<ChapterInformationInit> chapterInfo = [];
 };
diff --git a/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc b/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc
index c0709ed..e8e6686 100644
--- a/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_media_image.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/modules/mediasession/chapter_information.h"
 #include "third_party/blink/renderer/modules/mediasession/media_metadata.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_operators.h"
diff --git a/third_party/blink/renderer/modules/mediasession/media_session_utils.cc b/third_party/blink/renderer/modules/mediasession/media_session_utils.cc
new file mode 100644
index 0000000..02b90e09
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediasession/media_session_utils.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 "third_party/blink/renderer/modules/mediasession/media_session_utils.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_image.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink::media_session_utils {
+
+HeapVector<Member<MediaImage>> ProcessArtworkVector(
+    ScriptState* script_state,
+    const HeapVector<Member<MediaImage>>& artwork,
+    ExceptionState& exception_state) {
+  HeapVector<Member<MediaImage>> processed_artwork(artwork);
+
+  for (MediaImage* image : processed_artwork) {
+    KURL url = ExecutionContext::From(script_state)->CompleteURL(image->src());
+    if (!url.IsValid()) {
+      exception_state.ThrowTypeError("'" + image->src() +
+                                     "' can't be resolved to a valid URL.");
+      return {};
+    }
+    image->setSrc(url);
+  }
+
+  DCHECK(!exception_state.HadException());
+  return processed_artwork;
+}
+
+}  // namespace blink::media_session_utils
diff --git a/third_party/blink/renderer/modules/mediasession/media_session_utils.h b/third_party/blink/renderer/modules/mediasession/media_session_utils.h
new file mode 100644
index 0000000..076b410
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediasession/media_session_utils.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_MEDIA_SESSION_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_MEDIA_SESSION_UTILS_H_
+
+#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_image.h"
+
+namespace blink::media_session_utils {
+
+// Processes the `MediaImage` with a parsed url in the artwork list. Returns an
+// empty list when any exception happens.
+HeapVector<Member<MediaImage>> ProcessArtworkVector(
+    ScriptState* script_state,
+    const HeapVector<Member<MediaImage>>& artwork,
+    ExceptionState& exception_state);
+
+}  // namespace blink::media_session_utils
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_MEDIA_SESSION_UTILS_H_
diff --git a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
index 48caf40..f519293 100644
--- a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
+++ b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
@@ -176,7 +176,7 @@
   return promise;
 }
 
-ScriptPromise RemotePlayback::cancelWatchAvailability(
+ScriptPromiseTyped<IDLUndefined> RemotePlayback::cancelWatchAvailability(
     ScriptState* script_state,
     int id,
     ExceptionState& exception_state) {
@@ -185,24 +185,20 @@
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
         "disableRemotePlayback attribute is present.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (!CancelWatchAvailabilityInternal(id)) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotFoundError,
         "A callback with the given id is not found.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
-  resolver->Resolve();
-  return promise;
+  return ToResolvedUndefinedPromise(script_state);
 }
 
-ScriptPromise RemotePlayback::cancelWatchAvailability(
+ScriptPromiseTyped<IDLUndefined> RemotePlayback::cancelWatchAvailability(
     ScriptState* script_state,
     ExceptionState& exception_state) {
   if (media_element_->FastHasAttribute(
@@ -210,41 +206,37 @@
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
         "disableRemotePlayback attribute is present.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   availability_callbacks_.clear();
   StopListeningForAvailability();
-
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
-  resolver->Resolve();
-  return promise;
+  return ToResolvedUndefinedPromise(script_state);
 }
 
-ScriptPromise RemotePlayback::prompt(ScriptState* script_state,
-                                     ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLUndefined> RemotePlayback::prompt(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
   if (media_element_->FastHasAttribute(
           html_names::kDisableremoteplaybackAttr)) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
         "disableRemotePlayback attribute is present.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (prompt_promise_resolver_) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kOperationError,
         "A prompt is already being shown for this media element.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (!media_element_->DomWindow()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidAccessError,
         "RemotePlayback::prompt() does not work in a detached window.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (!LocalFrame::HasTransientUserActivation(
@@ -252,32 +244,33 @@
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidAccessError,
         "RemotePlayback::prompt() requires user gesture.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (!RuntimeEnabledFeatures::RemotePlaybackBackendEnabled()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotSupportedError,
         "The RemotePlayback API is disabled on this platform.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (availability_ == mojom::ScreenAvailability::UNAVAILABLE) {
     exception_state.ThrowDOMException(DOMExceptionCode::kNotFoundError,
                                       "No remote playback devices found.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (availability_ == mojom::ScreenAvailability::SOURCE_NOT_SUPPORTED) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotSupportedError,
         "The currentSrc is not compatible with remote playback");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state, exception_state.GetContext());
+  auto promise = resolver->Promise();
   prompt_promise_resolver_ = resolver;
   PromptInternal();
   RemotePlaybackMetrics::RecordRemotePlaybackLocation(
diff --git a/third_party/blink/renderer/modules/remoteplayback/remote_playback.h b/third_party/blink/renderer/modules/remoteplayback/remote_playback.h
index 18805e6..1524b5f 100644
--- a/third_party/blink/renderer/modules/remoteplayback/remote_playback.h
+++ b/third_party/blink/renderer/modules/remoteplayback/remote_playback.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.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"
@@ -36,7 +37,6 @@
 
 class AvailabilityCallbackWrapper;
 class HTMLMediaElement;
-class ScriptPromiseResolver;
 class ScriptState;
 class V8RemotePlaybackAvailabilityCallback;
 
@@ -84,15 +84,18 @@
       ExceptionState&);
 
   // Cancels updating the page via the callback specified by its id.
-  ScriptPromise cancelWatchAvailability(ScriptState*, int id, ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> cancelWatchAvailability(ScriptState*,
+                                                           int id,
+                                                           ExceptionState&);
 
   // Cancels all the callbacks watching remote playback availability changes
   // registered with this element.
-  ScriptPromise cancelWatchAvailability(ScriptState*, ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> cancelWatchAvailability(ScriptState*,
+                                                           ExceptionState&);
 
   // Shows the UI allowing user to change the remote playback state of the media
   // element (by picking a remote playback device from the list, for example).
-  ScriptPromise prompt(ScriptState*, ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> prompt(ScriptState*, ExceptionState&);
 
   String state() const;
 
@@ -180,7 +183,7 @@
   mojom::blink::ScreenAvailability availability_;
   HeapHashMap<int, Member<AvailabilityCallbackWrapper>> availability_callbacks_;
   Member<HTMLMediaElement> media_element_;
-  Member<ScriptPromiseResolver> prompt_promise_resolver_;
+  Member<ScriptPromiseResolverTyped<IDLUndefined>> prompt_promise_resolver_;
   Vector<KURL> availability_urls_;
   bool is_listening_;
   bool is_background_availability_monitoring_disabled_for_testing_ = false;
diff --git a/third_party/blink/renderer/modules/remoteplayback/remote_playback.idl b/third_party/blink/renderer/modules/remoteplayback/remote_playback.idl
index 7ec7534..45630bc81 100644
--- a/third_party/blink/renderer/modules/remoteplayback/remote_playback.idl
+++ b/third_party/blink/renderer/modules/remoteplayback/remote_playback.idl
@@ -23,6 +23,6 @@
     attribute EventHandler ondisconnect;
 
     [Measure, CallWith=ScriptState,RaisesException] Promise<long> watchAvailability(RemotePlaybackAvailabilityCallback callback);
-    [CallWith=ScriptState,RaisesException] Promise<void> cancelWatchAvailability(optional long id);
-    [Measure, CallWith=ScriptState,RaisesException] Promise<void> prompt();
+    [CallWith=ScriptState,RaisesException] Promise<undefined> cancelWatchAvailability(optional long id);
+    [Measure, CallWith=ScriptState,RaisesException] Promise<undefined> prompt();
 };
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
index 80d2533..8a1a13228 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
+++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
@@ -104,13 +104,14 @@
   return resolver->Promise();
 }
 
-ScriptPromise DOMScheduler::yield(ScriptState* script_state,
-                                  SchedulerYieldOptions* options,
-                                  ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLUndefined> DOMScheduler::yield(
+    ScriptState* script_state,
+    SchedulerYieldOptions* options,
+    ExceptionState& exception_state) {
   if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                       "Current window is detached");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (fixed_priority_continuation_queues_.empty()) {
@@ -152,14 +153,15 @@
   if (state.abort_source && state.abort_source->aborted()) {
     exception_state.RethrowV8Exception(
         state.abort_source->reason(script_state).V8ValueFor(script_state));
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   CHECK(state.priority_source);
   auto* task_queue = GetTaskQueue(state.priority_source,
                                   WebSchedulingQueueType::kContinuationQueue);
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state, exception_state.GetContext());
   MakeGarbageCollected<DOMTaskContinuation>(resolver, state.abort_source,
                                             task_queue);
   return resolver->Promise();
@@ -180,21 +182,17 @@
   return task->Id().value();
 }
 
-AtomicString DOMScheduler::isAncestor(
-    ScriptState* script_state,
-    scheduler::TaskAttributionIdType parent_id) {
-  auto* tracker =
-      scheduler::TaskAttributionTracker::From(script_state->GetIsolate());
-  if (!tracker) {
+void DOMScheduler::setTaskId(ScriptState* script_state,
+                             scheduler::TaskAttributionIdType task_id) {
+  if (!scheduler::TaskAttributionTracker::From(script_state->GetIsolate())) {
     // Can happen when a feature flag disables TaskAttribution.
-    return AtomicString("unknown");
+    return;
   }
-  const scheduler::TaskAttributionInfo* current_task = tracker->RunningTask();
-  return current_task &&
-                 tracker->IsAncestor(*current_task,
-                                     scheduler::TaskAttributionId(parent_id))
-             ? AtomicString("ancestor")
-             : AtomicString("not ancestor");
+  auto* task_info = MakeGarbageCollected<scheduler::TaskAttributionInfo>(
+      scheduler::TaskAttributionId(task_id));
+  auto* state = MakeGarbageCollected<ScriptWrappableTaskState>(
+      task_info, /*abort_source=*/nullptr, /*priority_source=*/nullptr);
+  ScriptWrappableTaskState::SetCurrent(script_state, state);
 }
 
 void DOMScheduler::CreateFixedPriorityTaskQueues(
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
index f1eff574..ce89706 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
+++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
@@ -78,11 +78,12 @@
                                       SchedulerPostTaskOptions*,
                                       ExceptionState&);
 
-  ScriptPromise yield(ScriptState*, SchedulerYieldOptions*, ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> yield(ScriptState*,
+                                         SchedulerYieldOptions*,
+                                         ExceptionState&);
 
   scheduler::TaskAttributionIdType taskId(ScriptState*);
-  AtomicString isAncestor(ScriptState*,
-                          scheduler::TaskAttributionIdType parent_id);
+  void setTaskId(ScriptState*, scheduler::TaskAttributionIdType);
 
   void ContextDestroyed() override;
 
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_continuation.cc b/third_party/blink/renderer/modules/scheduler/dom_task_continuation.cc
index 3a7f24a..4ebd07ec 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_task_continuation.cc
+++ b/third_party/blink/renderer/modules/scheduler/dom_task_continuation.cc
@@ -15,9 +15,10 @@
 
 namespace blink {
 
-DOMTaskContinuation::DOMTaskContinuation(ScriptPromiseResolver* resolver,
-                                         AbortSignal* signal,
-                                         DOMScheduler::DOMTaskQueue* task_queue)
+DOMTaskContinuation::DOMTaskContinuation(
+    ScriptPromiseResolverTyped<IDLUndefined>* resolver,
+    AbortSignal* signal,
+    DOMScheduler::DOMTaskQueue* task_queue)
     : resolver_(resolver), signal_(signal), task_queue_(task_queue) {
   CHECK(task_queue_);
 
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_continuation.h b/third_party/blink/renderer/modules/scheduler/dom_task_continuation.h
index f2a8122..8f3f9df6 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_task_continuation.h
+++ b/third_party/blink/renderer/modules/scheduler/dom_task_continuation.h
@@ -21,7 +21,7 @@
 // continuation will not be run.
 class DOMTaskContinuation final : public GarbageCollected<DOMTaskContinuation> {
  public:
-  DOMTaskContinuation(ScriptPromiseResolver*,
+  DOMTaskContinuation(ScriptPromiseResolverTyped<IDLUndefined>*,
                       AbortSignal*,
                       DOMScheduler::DOMTaskQueue*);
 
@@ -35,7 +35,7 @@
   void OnAbort();
 
   TaskHandle task_handle_;
-  Member<ScriptPromiseResolver> resolver_;
+  Member<ScriptPromiseResolverTyped<IDLUndefined>> resolver_;
   probe::AsyncTaskContext async_task_context_;
   Member<AbortSignal> signal_;
   Member<AbortSignal::AlgorithmHandle> abort_handle_;
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler.idl b/third_party/blink/renderer/modules/scheduler/scheduler.idl
index da19bad..385ff29 100644
--- a/third_party/blink/renderer/modules/scheduler/scheduler.idl
+++ b/third_party/blink/renderer/modules/scheduler/scheduler.idl
@@ -20,6 +20,5 @@
 ] interface Scheduler {
     [CallWith=ScriptState, MeasureAs=SchedulerPostTask, RaisesException] Promise<any> postTask(SchedulerPostTaskCallback callback, optional SchedulerPostTaskOptions options = {});
     [RuntimeEnabled=SchedulerYield, MeasureAs=SchedulerYield, CallWith=ScriptState, RaisesException] Promise<undefined> yield(optional SchedulerYieldOptions options = {});
-    [RuntimeEnabled=UnexposedTaskIds, CallWith=ScriptState, Exposed=Window] readonly attribute unsigned long taskId;
-    [RuntimeEnabled=UnexposedTaskIds, CallWith=ScriptState, Exposed=Window] AncestorStatus isAncestor(unsigned long parentId);
+    [RuntimeEnabled=UnexposedTaskIds, CallWith=ScriptState, Exposed=Window] attribute unsigned long taskId;
 };
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
index 0ebc076..44c0483 100644
--- a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
+++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <optional>
+#include <utility>
 
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
@@ -63,39 +64,13 @@
 }
 
 TaskAttributionInfo* TaskAttributionTrackerImpl::RunningTask() const {
-  ScriptWrappableTaskState* task_state =
-      ScriptWrappableTaskState::GetCurrent(isolate_);
-
-  // V8 embedder state may have no value in the case of a JSPromise that wasn't
-  // yet resolved.
-  return task_state ? task_state->GetTask() : running_task_.Get();
-}
-
-bool TaskAttributionTrackerImpl::IsAncestor(const TaskAttributionInfo& task,
-                                            TaskAttributionId ancestor_id) {
-  const TaskAttributionInfo* ancestor_task = nullptr;
-  ForEachAncestor(task, [&](const TaskAttributionInfo& ancestor) {
-    if (ancestor.Id() == ancestor_id) {
-      ancestor_task = &ancestor;
-      return IterationStatus::kStop;
-    }
-    return IterationStatus::kContinue;
-  });
-  return !!ancestor_task;
-}
-
-void TaskAttributionTrackerImpl::ForEachAncestor(
-    const TaskAttributionInfo& task,
-    base::FunctionRef<IterationStatus(const TaskAttributionInfo& task)>
-        visitor) {
-  const TaskAttributionInfo* current_task = &task;
-  while (current_task) {
-    const TaskAttributionInfo* parent_task = current_task->Parent();
-    if (visitor(*current_task) == IterationStatus::kStop) {
-      return;
-    }
-    current_task = parent_task;
+  if (ScriptWrappableTaskState* task_state =
+          ScriptWrappableTaskState::GetCurrent(isolate_)) {
+    return task_state->GetTask();
   }
+  // There won't be a running task outside of a `TaskScope` or microtask
+  // checkpoint.
+  return nullptr;
 }
 
 TaskAttributionTracker::TaskScope TaskAttributionTrackerImpl::CreateTaskScope(
@@ -114,60 +89,51 @@
     DOMTaskSignal* priority_source) {
   CHECK(script_state);
   CHECK_EQ(script_state->GetIsolate(), isolate_);
-  TaskAttributionInfo* running_task_to_be_restored = running_task_;
-  ScriptWrappableTaskState* continuation_task_state_to_be_restored =
+  ScriptWrappableTaskState* previous_task_state =
       ScriptWrappableTaskState::GetCurrent(isolate_);
 
-  // This compresses the task graph when encountering long task chains.
-  // TODO(crbug.com/1501999): Consider compressing the task graph further.
-  if (!parent_task || !parent_task->MaxChainLengthReached()) {
+  // Always propagate the current state (`parent_task`) when given. Otherwise
+  // create new state to begin propagating.
+  TaskAttributionInfo* running_task_info = nullptr;
+  if (!parent_task) {
     next_task_id_ = next_task_id_.NextId();
-    running_task_ =
-        MakeGarbageCollected<TaskAttributionInfo>(next_task_id_, parent_task);
+    running_task_info =
+        MakeGarbageCollected<TaskAttributionInfo>(next_task_id_);
   } else {
-    running_task_ = parent_task;
+    running_task_info = parent_task;
   }
 
+  ScriptWrappableTaskState* running_task_state =
+      MakeGarbageCollected<ScriptWrappableTaskState>(
+          running_task_info, abort_source, priority_source);
+  ScriptWrappableTaskState::SetCurrent(script_state, running_task_state);
+
+  // Fire observer callbacks after updating the CPED to keep `RunningTask()` in
+  // sync with what is passed to the observer.
   if (observer_) {
-    observer_->OnCreateTaskScope(*running_task_);
+    observer_->OnCreateTaskScope(*running_task_info);
   }
 
-  ScriptWrappableTaskState::SetCurrent(
-      script_state, MakeGarbageCollected<ScriptWrappableTaskState>(
-                        running_task_.Get(), abort_source, priority_source));
-
-  std::optional<TaskAttributionId> parent_task_id =
-      running_task_->Parent()
-          ? std::optional<TaskAttributionId>(running_task_->Parent()->Id())
-          : std::nullopt;
   TRACE_EVENT_BEGIN(
       "scheduler", "BlinkTaskScope", [&](perfetto::EventContext ctx) {
         auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
         auto* data = event->set_blink_task_scope();
         data->set_type(ToProtoEnum(type));
-        data->set_scope_task_id(running_task_->Id().value());
+        data->set_scope_task_id(running_task_info->Id().value());
         data->set_running_task_id_to_be_restored(TaskAttributionIdToInt(
-            running_task_to_be_restored ? running_task_to_be_restored->Id()
-                                        : TaskAttributionId()));
-        data->set_continuation_task_id_to_be_restored(TaskAttributionIdToInt(
-            continuation_task_state_to_be_restored &&
-                    continuation_task_state_to_be_restored->GetTask()
+            previous_task_state && previous_task_state->GetTask()
                 ? std::optional<TaskAttributionId>(
-                      continuation_task_state_to_be_restored->GetTask()->Id())
+                      previous_task_state->GetTask()->Id())
                 : std::nullopt));
-        data->set_parent_task_id(TaskAttributionIdToInt(parent_task_id));
       });
 
-  return TaskScope(this, script_state, running_task_to_be_restored,
-                   continuation_task_state_to_be_restored);
+  return TaskScope(this, script_state, previous_task_state);
 }
 
 void TaskAttributionTrackerImpl::OnTaskScopeDestroyed(
     const TaskScope& task_scope) {
-  DCHECK(running_task_);
-  running_task_ = task_scope.previous_running_task_;
-  ScriptWrappableTaskState::SetCurrent(
-      task_scope.script_state_, task_scope.previous_continuation_task_state_);
+  ScriptWrappableTaskState::SetCurrent(task_scope.script_state_,
+                                       task_scope.previous_task_state_);
   TRACE_EVENT_END("scheduler");
 }
 
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h
index 8521fe8..7c60ed9 100644
--- a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h
+++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h
@@ -39,13 +39,6 @@
 
   TaskAttributionInfo* RunningTask() const override;
 
-  bool IsAncestor(const TaskAttributionInfo& task,
-                  TaskAttributionId ancestor_id) override;
-  void ForEachAncestor(
-      const TaskAttributionInfo& task,
-      base::FunctionRef<IterationStatus(const TaskAttributionInfo& task)>
-          visitor) override;
-
   TaskScope CreateTaskScope(ScriptState* script_state,
                             TaskAttributionInfo* parent_task,
                             TaskScopeType type) override;
@@ -68,7 +61,6 @@
   void OnObserverScopeDestroyed(const ObserverScope&) override;
 
   TaskAttributionId next_task_id_;
-  Persistent<TaskAttributionInfo> running_task_ = nullptr;
   Persistent<Observer> observer_ = nullptr;
 
   // A queue of TaskAttributionInfo objects representing tasks that initiated a
diff --git a/third_party/blink/renderer/modules/screen_orientation/lock_orientation_callback.cc b/third_party/blink/renderer/modules/screen_orientation/lock_orientation_callback.cc
index 26b9c46..98cb2aa 100644
--- a/third_party/blink/renderer/modules/screen_orientation/lock_orientation_callback.cc
+++ b/third_party/blink/renderer/modules/screen_orientation/lock_orientation_callback.cc
@@ -15,7 +15,7 @@
 namespace blink {
 
 LockOrientationCallback::LockOrientationCallback(
-    ScriptPromiseResolver* resolver)
+    ScriptPromiseResolverTyped<IDLUndefined>* resolver)
     : resolver_(resolver) {}
 
 LockOrientationCallback::~LockOrientationCallback() = default;
@@ -26,11 +26,12 @@
   // resolving the promise.
   resolver_->GetExecutionContext()
       ->GetTaskRunner(TaskType::kMiscPlatformAPI)
-      ->PostTask(FROM_HERE, WTF::BindOnce(
-                                [](ScriptPromiseResolver* resolver) {
-                                  resolver->Resolve();
-                                },
-                                std::move(resolver_)));
+      ->PostTask(FROM_HERE,
+                 WTF::BindOnce(
+                     [](ScriptPromiseResolverTyped<IDLUndefined>* resolver) {
+                       resolver->Resolve();
+                     },
+                     std::move(resolver_)));
 }
 
 void LockOrientationCallback::OnError(WebLockOrientationError error) {
diff --git a/third_party/blink/renderer/modules/screen_orientation/lock_orientation_callback.h b/third_party/blink/renderer/modules/screen_orientation/lock_orientation_callback.h
index 6397c16..2910b4c 100644
--- a/third_party/blink/renderer/modules/screen_orientation/lock_orientation_callback.h
+++ b/third_party/blink/renderer/modules/screen_orientation/lock_orientation_callback.h
@@ -6,13 +6,12 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ORIENTATION_LOCK_ORIENTATION_CALLBACK_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/modules/screen_orientation/web_lock_orientation_callback.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 
 namespace blink {
 
-class ScriptPromiseResolver;
-
 // LockOrientationCallback is an implementation of WebLockOrientationCallback
 // that will resolve the underlying promise depending on the result passed to
 // the callback.
@@ -20,7 +19,7 @@
   USING_FAST_MALLOC(LockOrientationCallback);
 
  public:
-  explicit LockOrientationCallback(ScriptPromiseResolver*);
+  explicit LockOrientationCallback(ScriptPromiseResolverTyped<IDLUndefined>*);
 
   LockOrientationCallback(const LockOrientationCallback&) = delete;
   LockOrientationCallback& operator=(const LockOrientationCallback&) = delete;
@@ -31,7 +30,7 @@
   void OnError(WebLockOrientationError) override;
 
  private:
-  Persistent<ScriptPromiseResolver> resolver_;
+  Persistent<ScriptPromiseResolverTyped<IDLUndefined>> resolver_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.cc b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.cc
index a7c8bd8..ed0993cfb 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.cc
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.cc
@@ -138,14 +138,15 @@
   angle_ = angle;
 }
 
-ScriptPromise ScreenOrientation::lock(ScriptState* state,
-                                      const AtomicString& lock_string,
-                                      ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLUndefined> ScreenOrientation::lock(
+    ScriptState* state,
+    const AtomicString& lock_string,
+    ExceptionState& exception_state) {
   if (!state->ContextIsValid() || !Controller()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
         "The object is no longer associated to a window.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (GetExecutionContext()->IsSandboxed(
@@ -157,11 +158,12 @@
             ? "The window is in a fenced frame tree."
             : "The window is sandboxed and lacks the 'allow-orientation-lock' "
               "flag.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(state);
-  ScriptPromise promise = resolver->Promise();
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(state);
+  auto promise = resolver->Promise();
   Controller()->lock(StringToOrientationLock(lock_string),
                      std::make_unique<LockOrientationCallback>(resolver));
   return promise;
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.h b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.h
index d1c4ad0..1f5365c 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.h
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ORIENTATION_SCREEN_ORIENTATION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ORIENTATION_SCREEN_ORIENTATION_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -19,7 +20,6 @@
 class ExceptionState;
 class ExecutionContext;
 class LocalDOMWindow;
-class ScriptPromise;
 class ScriptState;
 class ScreenOrientationController;
 
@@ -43,9 +43,9 @@
   void SetType(display::mojom::blink::ScreenOrientation);
   void SetAngle(uint16_t);
 
-  ScriptPromise lock(ScriptState*,
-                     const AtomicString& orientation,
-                     ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> lock(ScriptState*,
+                                        const AtomicString& orientation,
+                                        ExceptionState&);
   void unlock();
 
   DEFINE_ATTRIBUTE_EVENT_LISTENER(change, kChange)
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.idl b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.idl
index 4a628c2..0f9d79f 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.idl
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.idl
@@ -27,7 +27,7 @@
     [HighEntropy=Direct, MeasureAs=ScreenOrientationAngle] readonly attribute unsigned short angle;
     [HighEntropy=Direct, MeasureAs=ScreenOrientationType] readonly attribute DOMString type;
 
-    [CallWith=ScriptState, MeasureAs=ScreenOrientationLock, RaisesException] Promise<void> lock(OrientationLockType orientation);
+    [CallWith=ScriptState, MeasureAs=ScreenOrientationLock, RaisesException] Promise<undefined> lock(OrientationLockType orientation);
     [MeasureAs=ScreenOrientationUnlock] void unlock();
 
     attribute EventHandler onchange;
diff --git a/third_party/blink/renderer/modules/sensor/testing/internals_sensor.cc b/third_party/blink/renderer/modules/sensor/testing/internals_sensor.cc
index dc3462c..5bd27047 100644
--- a/third_party/blink/renderer/modules/sensor/testing/internals_sensor.cc
+++ b/third_party/blink/renderer/modules/sensor/testing/internals_sensor.cc
@@ -135,7 +135,7 @@
 }  // namespace
 
 // static
-ScriptPromise InternalsSensor::createVirtualSensor(
+ScriptPromiseTyped<IDLUndefined> InternalsSensor::createVirtualSensor(
     ScriptState* script_state,
     Internals&,
     V8VirtualSensorType type,
@@ -147,15 +147,17 @@
   window->GetBrowserInterfaceBroker().GetInterface(
       virtual_sensor_provider.BindNewPipeAndPassReceiver());
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise promise = resolver->Promise();
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
+  auto promise = resolver->Promise();
   auto* raw_virtual_sensor_provider = virtual_sensor_provider.get();
   raw_virtual_sensor_provider->CreateVirtualSensor(
       ToMojoSensorType(type.AsEnum()), ToMojoSensorMetadata(options),
       WTF::BindOnce(
           // While we only really need |resolver|, we also take the
           // mojo::Remote<> so that it remains alive after this function exits.
-          [](ScriptPromiseResolver* resolver,
+          [](ScriptPromiseResolverTyped<IDLUndefined>* resolver,
              mojo::Remote<test::mojom::blink::WebSensorProviderAutomation>,
              device::mojom::blink::CreateVirtualSensorResult result) {
             switch (result) {
@@ -173,14 +175,14 @@
 }
 
 // static
-ScriptPromise InternalsSensor::updateVirtualSensor(
+ScriptPromiseTyped<IDLUndefined> InternalsSensor::updateVirtualSensor(
     ScriptState* script_state,
     Internals&,
     V8VirtualSensorType type,
     VirtualSensorReading* reading) {
   auto mojo_reading = ToMojoRawReading(type.AsEnum(), reading);
   if (!mojo_reading.has_value()) {
-    return ScriptPromise::Reject(
+    return ScriptPromiseTyped<IDLUndefined>::Reject(
         script_state,
         V8ThrowDOMException::CreateOrEmpty(script_state->GetIsolate(),
                                            DOMExceptionCode::kInvalidStateError,
@@ -194,15 +196,17 @@
   window->GetBrowserInterfaceBroker().GetInterface(
       virtual_sensor_provider.BindNewPipeAndPassReceiver());
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise promise = resolver->Promise();
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
+  auto promise = resolver->Promise();
   auto* raw_virtual_sensor_provider = virtual_sensor_provider.get();
   raw_virtual_sensor_provider->UpdateVirtualSensor(
       ToMojoSensorType(type.AsEnum()), std::move(mojo_reading.value()),
       WTF::BindOnce(
           // While we only really need |resolver|, we also take the
           // mojo::Remote<> so that it remains alive after this function exits.
-          [](ScriptPromiseResolver* resolver,
+          [](ScriptPromiseResolverTyped<IDLUndefined>* resolver,
              mojo::Remote<test::mojom::blink::WebSensorProviderAutomation>,
              device::mojom::blink::UpdateVirtualSensorResult result) {
             switch (result) {
@@ -222,9 +226,10 @@
 }
 
 // static
-ScriptPromise InternalsSensor::removeVirtualSensor(ScriptState* script_state,
-                                                   Internals&,
-                                                   V8VirtualSensorType type) {
+ScriptPromiseTyped<IDLUndefined> InternalsSensor::removeVirtualSensor(
+    ScriptState* script_state,
+    Internals&,
+    V8VirtualSensorType type) {
   LocalDOMWindow* window = LocalDOMWindow::From(script_state);
   CHECK(window);
   mojo::Remote<test::mojom::blink::WebSensorProviderAutomation>
@@ -232,15 +237,17 @@
   window->GetBrowserInterfaceBroker().GetInterface(
       virtual_sensor_provider.BindNewPipeAndPassReceiver());
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise promise = resolver->Promise();
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
+  auto promise = resolver->Promise();
   auto* raw_virtual_sensor_provider = virtual_sensor_provider.get();
   raw_virtual_sensor_provider->RemoveVirtualSensor(
       ToMojoSensorType(type.AsEnum()),
       WTF::BindOnce(
           // While we only really need |resolver|, we also take the
           // mojo::Remote<> so that it remains alive after this function exits.
-          [](ScriptPromiseResolver* resolver,
+          [](ScriptPromiseResolverTyped<IDLUndefined>* resolver,
              mojo::Remote<test::mojom::blink::WebSensorProviderAutomation>) {
             resolver->Resolve();
           },
diff --git a/third_party/blink/renderer/modules/sensor/testing/internals_sensor.h b/third_party/blink/renderer/modules/sensor/testing/internals_sensor.h
index 90f5ff8..00fedda9 100644
--- a/third_party/blink/renderer/modules/sensor/testing/internals_sensor.h
+++ b/third_party/blink/renderer/modules/sensor/testing/internals_sensor.h
@@ -21,17 +21,18 @@
   STATIC_ONLY(InternalsSensor);
 
  public:
-  static ScriptPromise createVirtualSensor(ScriptState*,
-                                           Internals&,
-                                           V8VirtualSensorType,
-                                           CreateVirtualSensorOptions*);
-  static ScriptPromise updateVirtualSensor(ScriptState*,
-                                           Internals&,
-                                           V8VirtualSensorType,
-                                           VirtualSensorReading*);
-  static ScriptPromise removeVirtualSensor(ScriptState*,
-                                           Internals&,
-                                           V8VirtualSensorType);
+  static ScriptPromiseTyped<IDLUndefined> createVirtualSensor(
+      ScriptState*,
+      Internals&,
+      V8VirtualSensorType,
+      CreateVirtualSensorOptions*);
+  static ScriptPromiseTyped<IDLUndefined> updateVirtualSensor(
+      ScriptState*,
+      Internals&,
+      V8VirtualSensorType,
+      VirtualSensorReading*);
+  static ScriptPromiseTyped<IDLUndefined>
+  removeVirtualSensor(ScriptState*, Internals&, V8VirtualSensorType);
   static ScriptPromiseTyped<VirtualSensorInformation>
   getVirtualSensorInformation(ScriptState*, Internals&, V8VirtualSensorType);
 };
diff --git a/third_party/blink/renderer/modules/serial/serial_port.cc b/third_party/blink/renderer/modules/serial/serial_port.cc
index 93ff223..dcdfc86 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port.cc
@@ -74,43 +74,6 @@
   }
 }
 
-// A ScriptFunction that returns the provided ScriptPromise.
-class ReturnPromiseFunction : public ScriptFunction::Callable {
- public:
-  explicit ReturnPromiseFunction(ScriptPromise promise) : promise_(promise) {}
-
-  ScriptValue Call(ScriptState*, ScriptValue) override {
-    return promise_.AsScriptValue();
-  }
-
-  void Trace(Visitor* visitor) const override {
-    visitor->Trace(promise_);
-    ScriptFunction::Callable::Trace(visitor);
-  }
-
- private:
-  ScriptPromise promise_;
-};
-
-// A ScriptFunction that calls AbortClose() on the provided SerialPort and
-// passes through the rejection.
-class AbortCloseFunction : public ScriptFunction::Callable {
- public:
-  explicit AbortCloseFunction(SerialPort* port) : port_(port) {}
-
-  ScriptValue Call(ScriptState* script_state, ScriptValue value) override {
-    port_->AbortClose();
-    return ScriptPromise::Reject(script_state, value).AsScriptValue();
-  }
-
-  void Trace(Visitor* visitor) const override {
-    visitor->Trace(port_);
-    ScriptFunction::Callable::Trace(visitor);
-  }
-
- private:
-  Member<SerialPort> port_;
-};
 }  // namespace
 
 SerialPort::SerialPort(Serial* parent, mojom::blink::SerialPortInfoPtr info)
@@ -138,26 +101,27 @@
   return info;
 }
 
-ScriptPromise SerialPort::open(ScriptState* script_state,
-                               const SerialOptions* options,
-                               ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLUndefined> SerialPort::open(
+    ScriptState* script_state,
+    const SerialOptions* options,
+    ExceptionState& exception_state) {
   if (!GetExecutionContext()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                       "Script context has shut down.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (open_resolver_) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
         "A call to open() is already in progress.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (port_.is_bound()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "The port is already open.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   auto mojo_options = device::mojom::blink::SerialConnectionOptions::New();
@@ -165,7 +129,7 @@
   if (options->baudRate() == 0) {
     exception_state.ThrowTypeError(
         "Requested baud rate must be greater than zero.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
   mojo_options->bitrate = options->baudRate();
 
@@ -179,7 +143,7 @@
     default:
       exception_state.ThrowTypeError(
           "Requested number of data bits must be 7 or 8.");
-      return ScriptPromise();
+      return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (options->parity() == "none") {
@@ -202,14 +166,14 @@
     default:
       exception_state.ThrowTypeError(
           "Requested number of stop bits must be 1 or 2.");
-      return ScriptPromise();
+      return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (options->bufferSize() == 0) {
     exception_state.ThrowTypeError(String::Format(
         "Requested buffer size (%d bytes) must be greater than zero.",
         options->bufferSize()));
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (options->bufferSize() > kMaxBufferSize) {
@@ -217,7 +181,7 @@
         String::Format("Requested buffer size (%d bytes) is greater than "
                        "the maximum allowed (%d bytes).",
                        options->bufferSize(), kMaxBufferSize));
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
   buffer_size_ = options->bufferSize();
 
@@ -226,11 +190,11 @@
   mojo_options->cts_flow_control = hardware_flow_control_;
 
   mojo::PendingRemote<device::mojom::blink::SerialPortClient> client;
-  open_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  auto callback = open_resolver_->WrapCallbackInScriptScope(
-      WTF::BindOnce(&SerialPort::OnOpen, WrapPersistent(this),
-                    client.InitWithNewPipeAndPassReceiver()));
+  open_resolver_ =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state, exception_state.GetContext());
+  auto callback = WTF::BindOnce(&SerialPort::OnOpen, WrapPersistent(this),
+                                client.InitWithNewPipeAndPassReceiver());
 
   parent_->OpenPort(info_->token, std::move(mojo_options), std::move(client),
                     std::move(callback));
@@ -372,31 +336,37 @@
   return resolver->Promise();
 }
 
-ScriptPromise SerialPort::close(ScriptState* script_state,
-                                ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLUndefined> SerialPort::close(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
   if (!port_.is_bound()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "The port is already closed.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (IsClosing()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
         "A call to close() is already in progress.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
-  close_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  ScriptPromise promise = close_resolver_->Promise();
+  close_resolver_ =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state, exception_state.GetContext());
+  auto promise = close_resolver_->Promise();
 
-  HeapVector<ScriptPromise> promises;
+  if (!readable_ && !writable_) {
+    StreamsClosed();
+    return promise;
+  }
+
   if (readable_) {
-    promises.push_back(readable_->cancel(script_state, exception_state));
+    readable_->cancel(script_state, exception_state);
     if (exception_state.HadException()) {
       AbortClose();
-      return ScriptPromise();
+      return ScriptPromiseTyped<IDLUndefined>();
     }
   }
   if (writable_) {
@@ -404,40 +374,35 @@
                        V8ThrowDOMException::CreateOrDie(
                            script_state->GetIsolate(),
                            DOMExceptionCode::kInvalidStateError, kPortClosed));
-    promises.push_back(writable_->abort(script_state, reason, exception_state));
+    writable_->abort(script_state, reason, exception_state);
     if (exception_state.HadException()) {
       AbortClose();
-      return ScriptPromise();
+      return ScriptPromiseTyped<IDLUndefined>();
     }
   }
 
-  if (promises.empty()) {
-    StreamsClosed();
-    return promise;
-  }
-
-  return ScriptPromise::All(script_state, promises)
-      .Then(MakeGarbageCollected<ScriptFunction>(
-                script_state,
-                MakeGarbageCollected<ReturnPromiseFunction>(promise)),
-            MakeGarbageCollected<ScriptFunction>(
-                script_state, MakeGarbageCollected<AbortCloseFunction>(this)));
+  return promise;
 }
 
-ScriptPromise SerialPort::forget(ScriptState* script_state,
-                                 ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLUndefined> SerialPort::forget(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
   ExecutionContext* context = GetExecutionContext();
   if (!context) {
     exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                       "Script context has shut down.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  parent_->ForgetPort(info_->token,
-                      WTF::BindOnce(&SerialPort::OnForget, WrapPersistent(this),
-                                    WrapPersistent(resolver)));
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state, exception_state.GetContext());
+  parent_->ForgetPort(
+      info_->token, WTF::BindOnce(
+                        [](ScriptPromiseResolverTyped<IDLUndefined>* resolver) {
+                          resolver->Resolve();
+                        },
+                        WrapPersistent(resolver)));
 
   return resolver->Promise();
 }
@@ -627,10 +592,7 @@
 void SerialPort::OnOpen(
     mojo::PendingReceiver<device::mojom::blink::SerialPortClient>
         client_receiver,
-    ScriptPromiseResolver* resolver,
     mojo::PendingRemote<device::mojom::blink::SerialPort> port) {
-  DCHECK_EQ(resolver, open_resolver_);
-
   if (!port) {
     open_resolver_->RejectWithDOMException(DOMExceptionCode::kNetworkError,
                                            kOpenError);
@@ -703,8 +665,4 @@
   feature_handle_for_scheduler_.reset();
 }
 
-void SerialPort::OnForget(ScriptPromiseResolver* resolver) {
-  resolver->Resolve();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/serial/serial_port.h b/third_party/blink/renderer/modules/serial/serial_port.h
index d74c2469..eb076027 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.h
+++ b/third_party/blink/renderer/modules/serial/serial_port.h
@@ -51,9 +51,9 @@
   DEFINE_ATTRIBUTE_EVENT_LISTENER(connect, kConnect)
   DEFINE_ATTRIBUTE_EVENT_LISTENER(disconnect, kDisconnect)
   SerialPortInfo* getInfo();
-  ScriptPromise open(ScriptState*,
-                     const SerialOptions* options,
-                     ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> open(ScriptState*,
+                                        const SerialOptions* options,
+                                        ExceptionState&);
   bool connected() { return connected_; }
   ReadableStream* readable(ScriptState*, ExceptionState&);
   WritableStream* writable(ScriptState*, ExceptionState&);
@@ -62,13 +62,13 @@
   ScriptPromiseTyped<IDLUndefined> setSignals(ScriptState*,
                                               const SerialOutputSignals*,
                                               ExceptionState&);
-  ScriptPromise close(ScriptState*, ExceptionState&);
-  ScriptPromise forget(ScriptState*, ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> close(ScriptState*, ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> forget(ScriptState*, ExceptionState&);
 
   const base::UnguessableToken& token() const { return info_->token; }
 
   void set_connected(bool connected) { connected_ = connected; }
-  ScriptPromise ContinueClose(ScriptState*);
+  ScriptPromiseTyped<IDLUndefined> ContinueClose(ScriptState*);
   void AbortClose();
   void StreamsClosed();
   bool IsClosing() const { return close_resolver_ != nullptr; }
@@ -99,13 +99,11 @@
                       mojo::ScopedDataPipeConsumerHandle* consumer);
   void OnConnectionError();
   void OnOpen(mojo::PendingReceiver<device::mojom::blink::SerialPortClient>,
-              ScriptPromiseResolver*,
               mojo::PendingRemote<device::mojom::blink::SerialPort>);
   void OnGetSignals(ScriptPromiseResolverTyped<SerialInputSignals>*,
                     device::mojom::blink::SerialPortControlSignalsPtr);
   void OnSetSignals(ScriptPromiseResolverTyped<IDLUndefined>*, bool success);
   void OnClose();
-  void OnForget(ScriptPromiseResolver*);
 
   const mojom::blink::SerialPortInfoPtr info_;
   bool connected_;
@@ -130,12 +128,12 @@
   bool hardware_flow_control_ = false;
 
   // Resolver for the Promise returned by open().
-  Member<ScriptPromiseResolver> open_resolver_;
+  Member<ScriptPromiseResolverTyped<IDLUndefined>> open_resolver_;
   // Resolvers for the Promises returned by getSignals() and setSignals() to
   // reject them on Mojo connection failure.
   HeapHashSet<Member<ScriptPromiseResolver>> signal_resolvers_;
   // Resolver for the Promise returned by close().
-  Member<ScriptPromiseResolver> close_resolver_;
+  Member<ScriptPromiseResolverTyped<IDLUndefined>> close_resolver_;
 
   FrameScheduler::SchedulingAffectingFeatureHandle
       feature_handle_for_scheduler_;
diff --git a/third_party/blink/renderer/modules/serial/serial_port.idl b/third_party/blink/renderer/modules/serial/serial_port.idl
index 249101c..f83fbed 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.idl
+++ b/third_party/blink/renderer/modules/serial/serial_port.idl
@@ -20,13 +20,13 @@
 
     [MeasureAs=SerialPortGetInfo] SerialPortInfo getInfo();
     [CallWith=ScriptState, RaisesException, MeasureAs=SerialPortOpen]
-    Promise<void> open(SerialOptions options);
+    Promise<undefined> open(SerialOptions options);
     [CallWith=ScriptState, RaisesException]
     Promise<SerialInputSignals> getSignals();
     [CallWith=ScriptState, RaisesException]
     Promise<undefined> setSignals(optional SerialOutputSignals signals = {});
     [RaisesException, CallWith=ScriptState, MeasureAs=SerialPortClose]
-    Promise<void> close();
+    Promise<undefined> close();
     [RaisesException, CallWith=ScriptState, MeasureAs=SerialPortForget]
-    Promise<void> forget();
+    Promise<undefined> forget();
 };
diff --git a/third_party/blink/renderer/modules/service_worker/clients.idl b/third_party/blink/renderer/modules/service_worker/clients.idl
index 31abe3b..e9fc345 100644
--- a/third_party/blink/renderer/modules/service_worker/clients.idl
+++ b/third_party/blink/renderer/modules/service_worker/clients.idl
@@ -10,5 +10,5 @@
     [CallWith=ScriptState] Promise<any> get(DOMString id);
     [CallWith=ScriptState] Promise<sequence<Client>> matchAll(optional ClientQueryOptions options = {});
     [CallWith=ScriptState] Promise<WindowClient?> openWindow(USVString url);
-    [CallWith=ScriptState] Promise<void> claim();
+    [CallWith=ScriptState] Promise<undefined> claim();
 };
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
index d24c105..39c5110 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
@@ -203,7 +203,8 @@
     : public GarbageCollected<UploadingCompletionObserver>,
       public BytesUploader::Client {
  public:
-  explicit UploadingCompletionObserver(ScriptPromiseResolver* resolver)
+  explicit UploadingCompletionObserver(
+      ScriptPromiseResolverTyped<IDLUndefined>* resolver)
       : resolver_(resolver) {}
   ~UploadingCompletionObserver() override = default;
 
@@ -217,7 +218,7 @@
   }
 
  private:
-  const Member<ScriptPromiseResolver> resolver_;
+  const Member<ScriptPromiseResolverTyped<IDLUndefined>> resolver_;
 };
 
 }  // namespace
@@ -420,7 +421,9 @@
 
     // Keep the service worker alive as long as we are reading from the request
     // body.
-    auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+    auto* resolver =
+        MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+            script_state);
     WaitUntil(script_state, resolver->Promise(), ASSERT_NO_EXCEPTION);
     auto* observer =
         MakeGarbageCollected<UploadingCompletionObserver>(resolver);
diff --git a/third_party/blink/renderer/modules/service_worker/install_event.cc b/third_party/blink/renderer/modules/service_worker/install_event.cc
index 6475d9e0..7abea8fe 100644
--- a/third_party/blink/renderer/modules/service_worker/install_event.cc
+++ b/third_party/blink/renderer/modules/service_worker/install_event.cc
@@ -23,11 +23,7 @@
 
 namespace {
 
-void DidRegisterRouter(ScriptPromiseResolver* resolver) {
-  if (!resolver->GetExecutionContext() ||
-      resolver->GetExecutionContext()->IsContextDestroyed()) {
-    return;
-  }
+void DidRegisterRouter(ScriptPromiseResolverTyped<IDLUndefined>* resolver) {
   resolver->Resolve();
 }
 
@@ -62,14 +58,14 @@
                            WaitUntilObserver* observer)
     : ExtendableEvent(type, initializer, observer), event_id_(event_id) {}
 
-ScriptPromise InstallEvent::registerRouter(
+ScriptPromiseTyped<IDLUndefined> InstallEvent::registerRouter(
     ScriptState* script_state,
     const V8UnionRouterRuleOrRouterRuleSequence* v8_rules,
     ExceptionState& exception_state) {
   ServiceWorkerGlobalScope* global_scope =
       To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
   if (!global_scope) {
-    return ScriptPromise::Reject(
+    return ScriptPromiseTyped<IDLUndefined>::Reject(
         script_state,
         V8ThrowDOMException::CreateOrDie(script_state->GetIsolate(),
                                          DOMExceptionCode::kInvalidStateError,
@@ -79,12 +75,12 @@
     case RouterRegistrationMethod::Uninitialized:
       break;
     case RouterRegistrationMethod::RegisterRouter:
-      return ScriptPromise::Reject(
+      return ScriptPromiseTyped<IDLUndefined>::Reject(
           script_state, V8ThrowException::CreateTypeError(
                             script_state->GetIsolate(),
                             "registerRouter is called multiple times."));
     case RouterRegistrationMethod::AddRoutes:
-      return ScriptPromise::Reject(
+      return ScriptPromiseTyped<IDLUndefined>::Reject(
           script_state,
           V8ThrowDOMException::CreateOrDie(
               script_state->GetIsolate(), DOMExceptionCode::kNotAllowedError,
@@ -103,24 +99,27 @@
                                   global_scope->BaseURL(),
                                   global_scope->FetchHandlerType(), rules);
   if (exception_state.HadException()) {
-    return ScriptPromise::Reject(script_state, exception_state);
+    return ScriptPromiseTyped<IDLUndefined>::Reject(script_state,
+                                                    exception_state);
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
   global_scope->GetServiceWorkerHost()->RegisterRouter(
       rules, WTF::BindOnce(&DidRegisterRouter, WrapPersistent(resolver)));
   router_registration_method_ = RouterRegistrationMethod::RegisterRouter;
   return resolver->Promise();
 }
 
-ScriptPromise InstallEvent::addRoutes(
+ScriptPromiseTyped<IDLUndefined> InstallEvent::addRoutes(
     ScriptState* script_state,
     const V8UnionRouterRuleOrRouterRuleSequence* v8_rules,
     ExceptionState& exception_state) {
   ServiceWorkerGlobalScope* global_scope =
       To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
   if (!global_scope) {
-    return ScriptPromise::Reject(
+    return ScriptPromiseTyped<IDLUndefined>::Reject(
         script_state,
         V8ThrowDOMException::CreateOrDie(script_state->GetIsolate(),
                                          DOMExceptionCode::kInvalidStateError,
@@ -128,7 +127,7 @@
   }
   switch (router_registration_method_) {
     case RouterRegistrationMethod::RegisterRouter:
-      return ScriptPromise::Reject(
+      return ScriptPromiseTyped<IDLUndefined>::Reject(
           script_state,
           V8ThrowDOMException::CreateOrDie(
               script_state->GetIsolate(), DOMExceptionCode::kNotAllowedError,
@@ -145,10 +144,13 @@
                                   global_scope->BaseURL(),
                                   global_scope->FetchHandlerType(), rules);
   if (exception_state.HadException()) {
-    return ScriptPromise::Reject(script_state, exception_state);
+    return ScriptPromiseTyped<IDLUndefined>::Reject(script_state,
+                                                    exception_state);
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
   global_scope->GetServiceWorkerHost()->AddRoutes(
       rules, WTF::BindOnce(&DidRegisterRouter, WrapPersistent(resolver)));
   router_registration_method_ = RouterRegistrationMethod::AddRoutes;
diff --git a/third_party/blink/renderer/modules/service_worker/install_event.h b/third_party/blink/renderer/modules/service_worker/install_event.h
index 4f38087..4b0767f9 100644
--- a/third_party/blink/renderer/modules/service_worker/install_event.h
+++ b/third_party/blink/renderer/modules/service_worker/install_event.h
@@ -14,7 +14,6 @@
 namespace blink {
 
 class ExceptionState;
-class ScriptPromise;
 class ScriptState;
 class V8UnionRouterRuleOrRouterRuleSequence;
 
@@ -38,12 +37,14 @@
 
   const AtomicString& InterfaceName() const override;
 
-  ScriptPromise registerRouter(ScriptState*,
-                               const V8UnionRouterRuleOrRouterRuleSequence*,
-                               ExceptionState&);
-  ScriptPromise addRoutes(ScriptState*,
-                          const V8UnionRouterRuleOrRouterRuleSequence*,
-                          ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> registerRouter(
+      ScriptState*,
+      const V8UnionRouterRuleOrRouterRuleSequence*,
+      ExceptionState&);
+  ScriptPromiseTyped<IDLUndefined> addRoutes(
+      ScriptState*,
+      const V8UnionRouterRuleOrRouterRuleSequence*,
+      ExceptionState&);
 
  protected:
   const int event_id_;
diff --git a/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.cc b/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.cc
index 10b400d..f8b8166 100644
--- a/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.cc
+++ b/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.cc
@@ -15,15 +15,17 @@
 
 namespace blink {
 
-ScriptPromise NavigationPreloadManager::enable(ScriptState* script_state) {
+ScriptPromiseTyped<IDLUndefined> NavigationPreloadManager::enable(
+    ScriptState* script_state) {
   return SetEnabled(true, script_state);
 }
 
-ScriptPromise NavigationPreloadManager::disable(ScriptState* script_state) {
+ScriptPromiseTyped<IDLUndefined> NavigationPreloadManager::disable(
+    ScriptState* script_state) {
   return SetEnabled(false, script_state);
 }
 
-ScriptPromise NavigationPreloadManager::setHeaderValue(
+ScriptPromiseTyped<IDLUndefined> NavigationPreloadManager::setHeaderValue(
     ScriptState* script_state,
     const String& value,
     ExceptionState& exception_state) {
@@ -31,11 +33,13 @@
     exception_state.ThrowTypeError(
         "The string provided to setHeaderValue ('" + value +
         "') is not a valid HTTP header field value.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise promise = resolver->Promise();
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
+  auto promise = resolver->Promise();
   registration_->SetNavigationPreloadHeader(value, resolver);
   return promise;
 }
@@ -54,10 +58,13 @@
     ServiceWorkerRegistration* registration)
     : registration_(registration) {}
 
-ScriptPromise NavigationPreloadManager::SetEnabled(bool enable,
-                                                   ScriptState* script_state) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise promise = resolver->Promise();
+ScriptPromiseTyped<IDLUndefined> NavigationPreloadManager::SetEnabled(
+    bool enable,
+    ScriptState* script_state) {
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
+  auto promise = resolver->Promise();
   registration_->EnableNavigationPreload(enable, resolver);
   return promise;
 }
diff --git a/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.h b/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.h
index 2ef2a7c..015d1d3 100644
--- a/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.h
+++ b/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.h
@@ -21,17 +21,18 @@
  public:
   explicit NavigationPreloadManager(ServiceWorkerRegistration*);
 
-  ScriptPromise enable(ScriptState*);
-  ScriptPromise disable(ScriptState*);
-  ScriptPromise setHeaderValue(ScriptState*,
-                               const String& value,
-                               ExceptionState& exception_state);
+  ScriptPromiseTyped<IDLUndefined> enable(ScriptState*);
+  ScriptPromiseTyped<IDLUndefined> disable(ScriptState*);
+  ScriptPromiseTyped<IDLUndefined> setHeaderValue(
+      ScriptState*,
+      const String& value,
+      ExceptionState& exception_state);
   ScriptPromiseTyped<NavigationPreloadState> getState(ScriptState*);
 
   void Trace(Visitor*) const override;
 
  private:
-  ScriptPromise SetEnabled(bool enable, ScriptState*);
+  ScriptPromiseTyped<IDLUndefined> SetEnabled(bool enable, ScriptState*);
 
   Member<ServiceWorkerRegistration> registration_;
 };
diff --git a/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.idl b/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.idl
index 78dff26..666b0fd1 100644
--- a/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.idl
+++ b/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.idl
@@ -7,8 +7,8 @@
     SecureContext,
     Exposed=(Window,Worker)
 ] interface NavigationPreloadManager {
-    [CallWith=ScriptState] Promise<void> enable();
-    [CallWith=ScriptState] Promise<void> disable();
-    [CallWith=ScriptState, RaisesException] Promise<void> setHeaderValue(ByteString value);
+    [CallWith=ScriptState] Promise<undefined> enable();
+    [CallWith=ScriptState] Promise<undefined> disable();
+    [CallWith=ScriptState, RaisesException] Promise<undefined> setHeaderValue(ByteString value);
     [CallWith=ScriptState] Promise<NavigationPreloadState> getState();
 };
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker.cc b/third_party/blink/renderer/modules/service_worker/service_worker.cc
index 54e0225..95adfd2 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker.cc
@@ -120,11 +120,16 @@
   host_->PostMessageToServiceWorker(std::move(message));
 }
 
-ScriptPromise ServiceWorker::InternalsTerminate(ScriptState* script_state) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise promise = resolver->Promise();
+ScriptPromiseTyped<IDLUndefined> ServiceWorker::InternalsTerminate(
+    ScriptState* script_state) {
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
+  auto promise = resolver->Promise();
   host_->TerminateForTesting(WTF::BindOnce(
-      [](ScriptPromiseResolver* resolver) { resolver->Resolve(); },
+      [](ScriptPromiseResolverTyped<IDLUndefined>* resolver) {
+        resolver->Resolve();
+      },
       WrapPersistent(resolver)));
   return promise;
 }
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker.h b/third_party/blink/renderer/modules/service_worker/service_worker.h
index f028665..e3bbc51 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker.h
@@ -98,7 +98,7 @@
   // Implements mojom::blink::ServiceWorkerObject.
   void StateChanged(mojom::blink::ServiceWorkerState new_state) override;
 
-  ScriptPromise InternalsTerminate(ScriptState*);
+  ScriptPromiseTyped<IDLUndefined> InternalsTerminate(ScriptState*);
 
  private:
   // ExecutionContextLifecycleStateObserver overrides.
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc b/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc
index 8f524e5..9d00d556 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_clients.cc
@@ -66,7 +66,7 @@
   resolver->Resolve(client);
 }
 
-void DidClaim(ScriptPromiseResolver* resolver,
+void DidClaim(ScriptPromiseResolverTyped<IDLUndefined>* resolver,
               mojom::blink::ServiceWorkerErrorType error,
               const String& error_msg) {
   if (!resolver->GetExecutionContext() ||
@@ -147,15 +147,18 @@
   return resolver->Promise();
 }
 
-ScriptPromise ServiceWorkerClients::claim(ScriptState* script_state) {
+ScriptPromiseTyped<IDLUndefined> ServiceWorkerClients::claim(
+    ScriptState* script_state) {
   ServiceWorkerGlobalScope* global_scope =
       To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
 
   // FIXME: May be null due to worker termination: http://crbug.com/413518.
   if (!global_scope)
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
   global_scope->GetServiceWorkerHost()->ClaimClients(
       WTF::BindOnce(&DidClaim, WrapPersistent(resolver)));
   return resolver->Promise();
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_clients.h b/third_party/blink/renderer/modules/service_worker/service_worker_clients.h
index 400f4bd3..3d7e865b 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_clients.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_clients.h
@@ -31,7 +31,7 @@
   ScriptPromiseTyped<IDLNullable<ServiceWorkerWindowClient>> openWindow(
       ScriptState*,
       const String& url);
-  ScriptPromise claim(ScriptState*);
+  ScriptPromiseTyped<IDLUndefined> claim(ScriptState*);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index a81f373..4fb81baa 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -160,13 +160,13 @@
 // the same as the update interval value in the event queue.
 constexpr int kDefaultTimeoutSecondsForOfflineEvent = 10;
 
-void DidSkipWaiting(ScriptPromiseResolver* resolver, bool success) {
-  if (!resolver->GetExecutionContext() ||
-      resolver->GetExecutionContext()->IsContextDestroyed())
-    return;
+void DidSkipWaiting(ScriptPromiseResolverTyped<IDLUndefined>* resolver,
+                    bool success) {
   // Per spec the promise returned by skipWaiting() can never reject.
-  if (!success)
+  if (!success) {
+    resolver->Detach();
     return;
+  }
   resolver->Resolve();
 }
 
@@ -594,14 +594,17 @@
   return service_worker_.Get();
 }
 
-ScriptPromise ServiceWorkerGlobalScope::skipWaiting(ScriptState* script_state) {
+ScriptPromiseTyped<IDLUndefined> ServiceWorkerGlobalScope::skipWaiting(
+    ScriptState* script_state) {
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   // FIXME: short-term fix, see details at:
   // https://codereview.chromium.org/535193002/.
   if (!execution_context)
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state);
   GetServiceWorkerHost()->SkipWaiting(
       WTF::BindOnce(&DidSkipWaiting, WrapPersistent(resolver)));
   return resolver->Promise();
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
index cae8aa1..88bad36 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
@@ -73,7 +73,6 @@
 class FetchEvent;
 class RespondWithObserver;
 class RequestInit;
-class ScriptPromise;
 class ScriptState;
 class ServiceWorker;
 class ServiceWorkerClients;
@@ -169,7 +168,7 @@
   ServiceWorkerRegistration* registration();
   ::blink::ServiceWorker* serviceWorker();
 
-  ScriptPromise skipWaiting(ScriptState*);
+  ScriptPromiseTyped<IDLUndefined> skipWaiting(ScriptState*);
 
   void BindServiceWorker(mojo::PendingReceiver<mojom::blink::ServiceWorker>);
   void BindControllerServiceWorker(
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.idl b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.idl
index 0a4a3e0..1903bef 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.idl
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.idl
@@ -39,7 +39,7 @@
   readonly attribute ServiceWorkerRegistration registration;
   readonly attribute ServiceWorker serviceWorker;
 
-  [CallWith=ScriptState] Promise<void> skipWaiting();
+  [CallWith=ScriptState] Promise<undefined> skipWaiting();
 
   attribute EventHandler onactivate;
   attribute EventHandler onfetch;
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_registration.cc b/third_party/blink/renderer/modules/service_worker/service_worker_registration.cc
index 87654d8be..916ca80 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_registration.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_registration.cc
@@ -71,9 +71,10 @@
   resolver->Resolve(error == mojom::ServiceWorkerErrorType::kNone);
 }
 
-void DidEnableNavigationPreload(ScriptPromiseResolver* resolver,
-                                mojom::ServiceWorkerErrorType error,
-                                const String& error_msg) {
+void DidEnableNavigationPreload(
+    ScriptPromiseResolverTyped<IDLUndefined>* resolver,
+    mojom::ServiceWorkerErrorType error,
+    const String& error_msg) {
   if (!resolver->GetExecutionContext() ||
       resolver->GetExecutionContext()->IsContextDestroyed()) {
     return;
@@ -110,9 +111,10 @@
   resolver->Resolve(dict);
 }
 
-void DidSetNavigationPreloadHeader(ScriptPromiseResolver* resolver,
-                                   mojom::ServiceWorkerErrorType error,
-                                   const String& error_msg) {
+void DidSetNavigationPreloadHeader(
+    ScriptPromiseResolverTyped<IDLUndefined>* resolver,
+    mojom::ServiceWorkerErrorType error,
+    const String& error_msg) {
   if (!resolver->GetExecutionContext() ||
       resolver->GetExecutionContext()->IsContextDestroyed()) {
     return;
@@ -242,7 +244,7 @@
 
 void ServiceWorkerRegistration::EnableNavigationPreload(
     bool enable,
-    ScriptPromiseResolver* resolver) {
+    ScriptPromiseResolverTyped<IDLUndefined>* resolver) {
   if (!host_.is_bound()) {
     return;
   }
@@ -262,7 +264,7 @@
 
 void ServiceWorkerRegistration::SetNavigationPreloadHeader(
     const String& value,
-    ScriptPromiseResolver* resolver) {
+    ScriptPromiseResolverTyped<IDLUndefined>* resolver) {
   if (!host_.is_bound()) {
     return;
   }
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_registration.h b/third_party/blink/renderer/modules/service_worker/service_worker_registration.h
index 5a8f57e..58c886ec 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_registration.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_registration.h
@@ -79,11 +79,14 @@
 
   int64_t RegistrationId() const { return registration_id_; }
 
-  void EnableNavigationPreload(bool enable, ScriptPromiseResolver* resolver);
+  void EnableNavigationPreload(
+      bool enable,
+      ScriptPromiseResolverTyped<IDLUndefined>* resolver);
   void GetNavigationPreloadState(
       ScriptPromiseResolverTyped<NavigationPreloadState>* resolver);
-  void SetNavigationPreloadHeader(const String& value,
-                                  ScriptPromiseResolver* resolver);
+  void SetNavigationPreloadHeader(
+      const String& value,
+      ScriptPromiseResolverTyped<IDLUndefined>* resolver);
 
   ScriptPromiseTyped<ServiceWorkerRegistration> update(ScriptState*,
                                                        ExceptionState&);
diff --git a/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.cc b/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.cc
index 76d4ec8..f6192b2e 100644
--- a/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.cc
+++ b/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.cc
@@ -8,7 +8,7 @@
 
 namespace blink {
 
-ScriptPromise InternalsServiceWorker::terminateServiceWorker(
+ScriptPromiseTyped<IDLUndefined> InternalsServiceWorker::terminateServiceWorker(
     ScriptState* script_state,
     Internals& internals,
     ServiceWorker* worker) {
diff --git a/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.h b/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.h
index 4a23160..1e92c23 100644
--- a/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.h
+++ b/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.h
@@ -18,9 +18,8 @@
   STATIC_ONLY(InternalsServiceWorker);
 
  public:
-  static ScriptPromise terminateServiceWorker(ScriptState*,
-                                              Internals&,
-                                              ServiceWorker*);
+  static ScriptPromiseTyped<IDLUndefined>
+  terminateServiceWorker(ScriptState*, Internals&, ServiceWorker*);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.idl b/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.idl
index b9aefca9..7b56e56 100644
--- a/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.idl
+++ b/third_party/blink/renderer/modules/service_worker/testing/internals_service_worker.idl
@@ -5,5 +5,5 @@
 [
     ImplementedAs=InternalsServiceWorker
 ] partial interface Internals {
-    [CallWith=ScriptState] Promise<void> terminateServiceWorker(ServiceWorker worker);
+    [CallWith=ScriptState] Promise<undefined> terminateServiceWorker(ServiceWorker worker);
 };
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage.cc b/third_party/blink/renderer/modules/shared_storage/shared_storage.cc
index b7b752fe..56690919 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage.cc
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage.cc
@@ -96,7 +96,7 @@
   }
 }
 
-void OnSetterMethodFinished(ScriptPromiseResolver* resolver,
+void OnSetterMethodFinished(ScriptPromiseResolverTyped<IDLAny>* resolver,
                             SharedStorage* shared_storage,
                             SharedStorageSetterMethod method,
                             GlobalScope global_scope,
@@ -356,30 +356,31 @@
   ScriptWrappable::Trace(visitor);
 }
 
-ScriptPromise SharedStorage::set(ScriptState* script_state,
-                                 const String& key,
-                                 const String& value,
-                                 ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLAny> SharedStorage::set(ScriptState* script_state,
+                                              const String& key,
+                                              const String& value,
+                                              ExceptionState& exception_state) {
   return set(script_state, key, value, SharedStorageSetMethodOptions::Create(),
              exception_state);
 }
 
-ScriptPromise SharedStorage::set(ScriptState* script_state,
-                                 const String& key,
-                                 const String& value,
-                                 const SharedStorageSetMethodOptions* options,
-                                 ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLAny> SharedStorage::set(
+    ScriptState* script_state,
+    const String& key,
+    const String& value,
+    const SharedStorageSetMethodOptions* options,
+    ExceptionState& exception_state) {
   base::TimeTicks start_time = base::TimeTicks::Now();
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow() ||
         execution_context->IsSharedStorageWorkletGlobalScope());
 
   if (!CheckBrowsingContextIsValid(*script_state, exception_state))
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLAny>();
 
-  ScriptPromiseResolver* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolverTyped<IDLAny>>(
       script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
+  auto promise = resolver->Promise();
 
   if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
                                            *resolver)) {
@@ -422,21 +423,22 @@
   return promise;
 }
 
-ScriptPromise SharedStorage::append(ScriptState* script_state,
-                                    const String& key,
-                                    const String& value,
-                                    ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLAny> SharedStorage::append(
+    ScriptState* script_state,
+    const String& key,
+    const String& value,
+    ExceptionState& exception_state) {
   base::TimeTicks start_time = base::TimeTicks::Now();
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow() ||
         execution_context->IsSharedStorageWorkletGlobalScope());
 
   if (!CheckBrowsingContextIsValid(*script_state, exception_state))
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLAny>();
 
-  ScriptPromiseResolver* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolverTyped<IDLAny>>(
       script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
+  auto promise = resolver->Promise();
 
   if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
                                            *resolver)) {
@@ -478,20 +480,21 @@
   return promise;
 }
 
-ScriptPromise SharedStorage::Delete(ScriptState* script_state,
-                                    const String& key,
-                                    ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLAny> SharedStorage::Delete(
+    ScriptState* script_state,
+    const String& key,
+    ExceptionState& exception_state) {
   base::TimeTicks start_time = base::TimeTicks::Now();
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow() ||
         execution_context->IsSharedStorageWorkletGlobalScope());
 
   if (!CheckBrowsingContextIsValid(*script_state, exception_state))
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLAny>();
 
-  ScriptPromiseResolver* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolverTyped<IDLAny>>(
       script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
+  auto promise = resolver->Promise();
 
   if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
                                            *resolver)) {
@@ -524,19 +527,20 @@
   return promise;
 }
 
-ScriptPromise SharedStorage::clear(ScriptState* script_state,
-                                   ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLAny> SharedStorage::clear(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
   base::TimeTicks start_time = base::TimeTicks::Now();
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow() ||
         execution_context->IsSharedStorageWorkletGlobalScope());
 
   if (!CheckBrowsingContextIsValid(*script_state, exception_state))
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLAny>();
 
-  ScriptPromiseResolver* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolverTyped<IDLAny>>(
       script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
+  auto promise = resolver->Promise();
 
   if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
                                            *resolver)) {
@@ -765,7 +769,7 @@
                      V8String(script_state->GetIsolate(), embedder_context));
 }
 
-ScriptPromise SharedStorage::selectURL(
+ScriptPromiseTyped<V8SharedStorageResponse> SharedStorage::selectURL(
     ScriptState* script_state,
     const String& name,
     HeapVector<Member<SharedStorageUrlWithMetadata>> urls,
@@ -775,7 +779,7 @@
                    exception_state);
 }
 
-ScriptPromise SharedStorage::selectURL(
+ScriptPromiseTyped<V8SharedStorageResponse> SharedStorage::selectURL(
     ScriptState* script_state,
     const String& name,
     HeapVector<Member<SharedStorageUrlWithMetadata>> urls,
@@ -789,14 +793,14 @@
                                            exception_state);
 }
 
-ScriptPromise SharedStorage::run(ScriptState* script_state,
-                                 const String& name,
-                                 ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLAny> SharedStorage::run(ScriptState* script_state,
+                                              const String& name,
+                                              ExceptionState& exception_state) {
   return run(script_state, name,
              SharedStorageRunOperationMethodOptions::Create(), exception_state);
 }
 
-ScriptPromise SharedStorage::run(
+ScriptPromiseTyped<IDLAny> SharedStorage::run(
     ScriptState* script_state,
     const String& name,
     const SharedStorageRunOperationMethodOptions* options,
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage.h b/third_party/blink/renderer/modules/shared_storage/shared_storage.h
index 09f8728..754db44 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage.h
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/async_iterable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_async_iterator_shared_storage.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_typedefs.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -37,41 +38,48 @@
   void Trace(Visitor*) const override;
 
   // SharedStorage IDL
-  ScriptPromise set(ScriptState*,
-                    const String& key,
-                    const String& value,
-                    ExceptionState&);
-  ScriptPromise set(ScriptState*,
-                    const String& key,
-                    const String& value,
-                    const SharedStorageSetMethodOptions* options,
-                    ExceptionState&);
-  ScriptPromise append(ScriptState*,
-                       const String& key,
-                       const String& value,
-                       ExceptionState&);
-  ScriptPromise Delete(ScriptState*, const String& key, ExceptionState&);
-  ScriptPromise clear(ScriptState*, ExceptionState&);
+  ScriptPromiseTyped<IDLAny> set(ScriptState*,
+                                 const String& key,
+                                 const String& value,
+                                 ExceptionState&);
+  ScriptPromiseTyped<IDLAny> set(ScriptState*,
+                                 const String& key,
+                                 const String& value,
+                                 const SharedStorageSetMethodOptions* options,
+                                 ExceptionState&);
+  ScriptPromiseTyped<IDLAny> append(ScriptState*,
+                                    const String& key,
+                                    const String& value,
+                                    ExceptionState&);
+  ScriptPromiseTyped<IDLAny> Delete(ScriptState*,
+                                    const String& key,
+                                    ExceptionState&);
+  ScriptPromiseTyped<IDLAny> clear(ScriptState*, ExceptionState&);
   ScriptPromiseTyped<IDLString> get(ScriptState*,
                                     const String& key,
                                     ExceptionState&);
   ScriptPromiseTyped<IDLUnsignedLong> length(ScriptState*, ExceptionState&);
   ScriptPromiseTyped<IDLDouble> remainingBudget(ScriptState*, ExceptionState&);
   ScriptValue context(ScriptState*, ExceptionState&) const;
-  ScriptPromise selectURL(ScriptState*,
-                          const String& name,
-                          HeapVector<Member<SharedStorageUrlWithMetadata>> urls,
-                          ExceptionState&);
-  ScriptPromise selectURL(ScriptState*,
-                          const String& name,
-                          HeapVector<Member<SharedStorageUrlWithMetadata>> urls,
-                          const SharedStorageRunOperationMethodOptions* options,
-                          ExceptionState&);
-  ScriptPromise run(ScriptState*, const String& name, ExceptionState&);
-  ScriptPromise run(ScriptState*,
-                    const String& name,
-                    const SharedStorageRunOperationMethodOptions* options,
-                    ExceptionState&);
+  ScriptPromiseTyped<V8SharedStorageResponse> selectURL(
+      ScriptState*,
+      const String& name,
+      HeapVector<Member<SharedStorageUrlWithMetadata>> urls,
+      ExceptionState&);
+  ScriptPromiseTyped<V8SharedStorageResponse> selectURL(
+      ScriptState*,
+      const String& name,
+      HeapVector<Member<SharedStorageUrlWithMetadata>> urls,
+      const SharedStorageRunOperationMethodOptions* options,
+      ExceptionState&);
+  ScriptPromiseTyped<IDLAny> run(ScriptState*,
+                                 const String& name,
+                                 ExceptionState&);
+  ScriptPromiseTyped<IDLAny> run(
+      ScriptState*,
+      const String& name,
+      const SharedStorageRunOperationMethodOptions* options,
+      ExceptionState&);
   ScriptPromiseTyped<SharedStorageWorklet> createWorklet(
       ScriptState*,
       const String& module_url,
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.cc b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.cc
index 8437d93..0fea7f4 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.cc
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.cc
@@ -207,7 +207,7 @@
                   resolver->DowncastTo<SharedStorageWorklet>()->Resolve(
                       shared_storage_worklet);
                 } else {
-                  resolver->Resolve();
+                  resolver->DowncastTo<IDLUndefined>()->Resolve();
                 }
               },
               WrapPersistent(resolver), WrapPersistent(this), start_time,
@@ -498,14 +498,15 @@
   return promise;
 }
 
-ScriptPromise SharedStorageWorklet::run(ScriptState* script_state,
-                                        const String& name,
-                                        ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLAny> SharedStorageWorklet::run(
+    ScriptState* script_state,
+    const String& name,
+    ExceptionState& exception_state) {
   return run(script_state, name,
              SharedStorageRunOperationMethodOptions::Create(), exception_state);
 }
 
-ScriptPromise SharedStorageWorklet::run(
+ScriptPromiseTyped<IDLAny> SharedStorageWorklet::run(
     ScriptState* script_state,
     const String& name,
     const SharedStorageRunOperationMethodOptions* options,
@@ -517,19 +518,19 @@
 
   if (!CheckBrowsingContextIsValid(*script_state, exception_state)) {
     LogSharedStorageWorkletError(SharedStorageWorkletErrorType::kRunWebVisible);
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLAny>();
   }
 
   std::optional<BlinkCloneableMessage> serialized_data =
       Serialize(options, *execution_context, exception_state);
   if (!serialized_data) {
     LogSharedStorageWorkletError(SharedStorageWorkletErrorType::kRunWebVisible);
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLAny>();
   }
 
-  ScriptPromiseResolver* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolverTyped<IDLAny>>(
       script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
+  auto promise = resolver->Promise();
 
   if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
                                            *resolver)) {
@@ -574,7 +575,7 @@
       name, std::move(*serialized_data), keep_alive, std::move(context_id),
       std::move(aggregation_coordinator_origin),
       WTF::BindOnce(
-          [](ScriptPromiseResolver* resolver,
+          [](ScriptPromiseResolverTyped<IDLAny>* resolver,
              SharedStorageWorklet* shared_storage_worklet,
              base::TimeTicks start_time, bool success,
              const String& error_message) {
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.h b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.h
index 4942a95..f3cdda8 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.h
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.h
@@ -51,11 +51,14 @@
       HeapVector<Member<SharedStorageUrlWithMetadata>> urls,
       const SharedStorageRunOperationMethodOptions* options,
       ExceptionState&);
-  ScriptPromise run(ScriptState*, const String& name, ExceptionState&);
-  ScriptPromise run(ScriptState*,
-                    const String& name,
-                    const SharedStorageRunOperationMethodOptions* options,
-                    ExceptionState&);
+  ScriptPromiseTyped<IDLAny> run(ScriptState*,
+                                 const String& name,
+                                 ExceptionState&);
+  ScriptPromiseTyped<IDLAny> run(
+      ScriptState*,
+      const String& name,
+      const SharedStorageRunOperationMethodOptions* options,
+      ExceptionState&);
 
   // Helper implementation method for `sharedStorage.worklet.addModule()` and
   // for `sharedStorage.createWorklet()`.
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_connection.cc b/third_party/blink/renderer/modules/smart_card/smart_card_connection.cc
index daf793f1..3baf7f78 100644
--- a/third_party/blink/renderer/modules/smart_card/smart_card_connection.cc
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_connection.cc
@@ -157,7 +157,7 @@
     : public GarbageCollected<TransactionState> {
  public:
   TransactionState(
-      ScriptPromiseResolver* start_transaction_request,
+      ScriptPromiseResolverTyped<IDLUndefined>* start_transaction_request,
       mojo::PendingAssociatedRemote<device::mojom::blink::SmartCardTransaction>
           pending_remote,
       ExecutionContext* execution_context);
@@ -177,12 +177,12 @@
       device::mojom::blink::SmartCardDisposition,
       base::OnceCallback<void(device::mojom::blink::SmartCardResultPtr)>);
 
-  ScriptPromiseResolver* GetStartTransactionRequest() const {
+  ScriptPromiseResolverTyped<IDLUndefined>* GetStartTransactionRequest() const {
     return start_transaction_request_.Get();
   }
 
  private:
-  Member<ScriptPromiseResolver> start_transaction_request_;
+  Member<ScriptPromiseResolverTyped<IDLUndefined>> start_transaction_request_;
   HeapMojoAssociatedRemote<device::mojom::blink::SmartCardTransaction>
       transaction_;
   ScriptValue callback_exception_;
@@ -192,7 +192,7 @@
 SmartCardConnection::TransactionState::~TransactionState() = default;
 
 SmartCardConnection::TransactionState::TransactionState(
-    ScriptPromiseResolver* start_transaction_request,
+    ScriptPromiseResolverTyped<IDLUndefined>* start_transaction_request,
     mojo::PendingAssociatedRemote<device::mojom::blink::SmartCardTransaction>
         pending_remote,
     ExecutionContext* execution_context)
@@ -288,25 +288,27 @@
       &SmartCardConnection::CloseMojoConnection, WrapWeakPersistent(this)));
 }
 
-ScriptPromise SmartCardConnection::disconnect(ScriptState* script_state,
-                                              ExceptionState& exception_state) {
+ScriptPromiseTyped<IDLUndefined> SmartCardConnection::disconnect(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
   return disconnect(
       script_state,
       V8SmartCardDisposition(V8SmartCardDisposition::Enum::kLeave),
       exception_state);
 }
 
-ScriptPromise SmartCardConnection::disconnect(
+ScriptPromiseTyped<IDLUndefined> SmartCardConnection::disconnect(
     ScriptState* script_state,
     const V8SmartCardDisposition& disposition,
     ExceptionState& exception_state) {
   if (!smart_card_context_->EnsureNoOperationInProgress(exception_state) ||
       !EnsureConnection(exception_state)) {
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state, exception_state.GetContext());
   SetOperationInProgress(resolver);
 
   connection_->Disconnect(
@@ -433,23 +435,24 @@
   return resolver->Promise();
 }
 
-ScriptPromise SmartCardConnection::setAttribute(
+ScriptPromiseTyped<IDLUndefined> SmartCardConnection::setAttribute(
     ScriptState* script_state,
     uint32_t tag,
     const DOMArrayPiece& data,
     ExceptionState& exception_state) {
   if (!smart_card_context_->EnsureNoOperationInProgress(exception_state) ||
       !EnsureConnection(exception_state)) {
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (data.IsDetached() || data.IsNull()) {
     exception_state.ThrowTypeError("Invalid data.");
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state, exception_state.GetContext());
   SetOperationInProgress(resolver);
 
   Vector<uint8_t> data_vector;
@@ -463,29 +466,31 @@
   return resolver->Promise();
 }
 
-ScriptPromise SmartCardConnection::startTransaction(
+ScriptPromiseTyped<IDLUndefined> SmartCardConnection::startTransaction(
     ScriptState* script_state,
     V8SmartCardTransactionCallback* transaction_callback,
     SmartCardTransactionOptions* options,
     ExceptionState& exception_state) {
   if (!smart_card_context_->EnsureNoOperationInProgress(exception_state) ||
       !EnsureConnection(exception_state)) {
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   if (transaction_state_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       kTransactionAlreadyExists);
-    return ScriptPromise();
+    return ScriptPromiseTyped<IDLUndefined>();
   }
 
   AbortSignal* signal = options->getSignalOr(nullptr);
   if (signal && signal->aborted()) {
-    return ScriptPromise::Reject(script_state, signal->reason(script_state));
+    return ScriptPromiseTyped<IDLUndefined>::Reject(
+        script_state, signal->reason(script_state));
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverTyped<IDLUndefined>>(
+          script_state, exception_state.GetContext());
   SetOperationInProgress(resolver);
 
   AbortSignal::AlgorithmHandle* abort_handle = nullptr;
@@ -576,7 +581,7 @@
 }
 
 void SmartCardConnection::OnDisconnectDone(
-    ScriptPromiseResolver* resolver,
+    ScriptPromiseResolverTyped<IDLUndefined>* resolver,
     device::mojom::blink::SmartCardResultPtr result) {
   ClearOperationInProgress(resolver);
 
@@ -592,7 +597,7 @@
 }
 
 void SmartCardConnection::OnPlainResult(
-    ScriptPromiseResolver* resolver,
+    ScriptPromiseResolverTyped<IDLUndefined>* resolver,
     device::mojom::blink::SmartCardResultPtr result) {
   ClearOperationInProgress(resolver);
 
@@ -652,7 +657,7 @@
 }
 
 void SmartCardConnection::OnBeginTransactionDone(
-    ScriptPromiseResolver* resolver,
+    ScriptPromiseResolverTyped<IDLUndefined>* resolver,
     V8SmartCardTransactionCallback* transaction_callback,
     AbortSignal* signal,
     AbortSignal::AlgorithmHandle* abort_handle,
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_connection.h b/third_party/blink/renderer/modules/smart_card/smart_card_connection.h
index d9b9bac..8e34c2d 100644
--- a/third_party/blink/renderer/modules/smart_card/smart_card_connection.h
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_connection.h
@@ -36,11 +36,12 @@
       ExecutionContext*);
 
   // SmartCardConnection idl
-  ScriptPromise disconnect(ScriptState* script_state,
-                           ExceptionState& exception_state);
-  ScriptPromise disconnect(ScriptState* script_state,
-                           const V8SmartCardDisposition& disposition,
-                           ExceptionState& exception_state);
+  ScriptPromiseTyped<IDLUndefined> disconnect(ScriptState* script_state,
+                                              ExceptionState& exception_state);
+  ScriptPromiseTyped<IDLUndefined> disconnect(
+      ScriptState* script_state,
+      const V8SmartCardDisposition& disposition,
+      ExceptionState& exception_state);
   ScriptPromiseTyped<DOMArrayBuffer> transmit(ScriptState* script_state,
                                               const DOMArrayPiece& send_buffer,
                                               SmartCardTransmitOptions* options,
@@ -56,14 +57,16 @@
       ScriptState* script_state,
       uint32_t tag,
       ExceptionState& exception_state);
-  ScriptPromise setAttribute(ScriptState* script_state,
-                             uint32_t tag,
-                             const DOMArrayPiece& data,
-                             ExceptionState& exception_state);
-  ScriptPromise startTransaction(ScriptState* script_state,
-                                 V8SmartCardTransactionCallback* transaction,
-                                 SmartCardTransactionOptions* options,
-                                 ExceptionState& exception_state);
+  ScriptPromiseTyped<IDLUndefined> setAttribute(
+      ScriptState* script_state,
+      uint32_t tag,
+      const DOMArrayPiece& data,
+      ExceptionState& exception_state);
+  ScriptPromiseTyped<IDLUndefined> startTransaction(
+      ScriptState* script_state,
+      V8SmartCardTransactionCallback* transaction,
+      SmartCardTransactionOptions* options,
+      ExceptionState& exception_state);
   // Called by SmartCardContext
   void OnOperationInProgressCleared();
 
@@ -78,16 +81,16 @@
   void SetOperationInProgress(ScriptPromiseResolver*);
   void ClearOperationInProgress(ScriptPromiseResolver*);
   bool EnsureConnection(ExceptionState& exception_state) const;
-  void OnDisconnectDone(ScriptPromiseResolver* resolver,
+  void OnDisconnectDone(ScriptPromiseResolverTyped<IDLUndefined>* resolver,
                         device::mojom::blink::SmartCardResultPtr result);
-  void OnPlainResult(ScriptPromiseResolver* resolver,
+  void OnPlainResult(ScriptPromiseResolverTyped<IDLUndefined>* resolver,
                      device::mojom::blink::SmartCardResultPtr result);
   void OnDataResult(ScriptPromiseResolverTyped<DOMArrayBuffer>* resolver,
                     device::mojom::blink::SmartCardDataResultPtr result);
   void OnStatusDone(ScriptPromiseResolverTyped<SmartCardConnectionStatus>*,
                     device::mojom::blink::SmartCardStatusResultPtr result);
   void OnBeginTransactionDone(
-      ScriptPromiseResolver* resolver,
+      ScriptPromiseResolverTyped<IDLUndefined>* resolver,
       V8SmartCardTransactionCallback* transaction_callback,
       AbortSignal* signal,
       AbortSignal::AlgorithmHandle* abort_handle,
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_connection.idl b/third_party/blink/renderer/modules/smart_card/smart_card_connection.idl
index c3003a8..7e33dbe 100644
--- a/third_party/blink/renderer/modules/smart_card/smart_card_connection.idl
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_connection.idl
@@ -28,7 +28,7 @@
   IsolatedContext
 ] interface SmartCardConnection {
   [CallWith=ScriptState, RaisesException]
-  Promise<void> disconnect(optional SmartCardDisposition disposition = "leave");
+  Promise<undefined> disconnect(optional SmartCardDisposition disposition = "leave");
 
   [CallWith=ScriptState, RaisesException]
   Promise<ArrayBuffer> transmit(BufferSource sendBuffer,
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
index 7d16a6e..97be6ed09 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
@@ -349,7 +349,7 @@
     adapter_info = MakeGarbageCollected<GPUAdapterInfo>(
         vendor_, architecture_, device_, description_, driver_,
         FromDawnEnum(backend_type_), FromDawnEnum(adapter_type_),
-        d3d_shader_model_);
+        d3d_shader_model_, vk_driver_version_);
     for (GPUMemoryHeapInfo* memory_heap : memory_heaps_) {
       adapter_info->AppendMemoryHeapInfo(memory_heap);
     }
diff --git a/third_party/blink/renderer/platform/fonts/custom_font_data.h b/third_party/blink/renderer/platform/fonts/custom_font_data.h
index 34d9cde..3a7323c 100644
--- a/third_party/blink/renderer/platform/fonts/custom_font_data.h
+++ b/third_party/blink/renderer/platform/fonts/custom_font_data.h
@@ -44,7 +44,6 @@
   virtual bool IsLoading() const { return false; }
   virtual bool IsLoadingFallback() const { return false; }
   virtual bool ShouldSkipDrawing() const { return false; }
-  virtual void ClearFontFaceSource() {}
   virtual bool IsPendingDataUrl() const { return false; }
 };
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index bb20644..8ebf972e 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1464,6 +1464,13 @@
     {
       name: "DocumentWrite",
     },
+    // When enabled, this allows users of DOMParser to do:
+    // (new DOMParser()).parseFromString(...,{includeShadowRoots:true}).
+    // This is being deprecated and removed: crbug.com/329330085.
+    {
+      name: "DOMParserIncludeShadowRoots",
+      status: "stable",
+    },
     // Controls whether DOMParser.parseFromString() attempts to use the
     // html fast path parser. This flag is to be used as a killswitch in case
     // of any issues.
@@ -1607,6 +1614,8 @@
       public: true,
       status: "test",
       base_feature: "none",
+      origin_trial_feature_name: "FedCmButtonMode",
+      origin_trial_allows_third_party: true,
     },
     {
       name: "FedCmDisconnect",
@@ -3728,6 +3737,10 @@
       base_feature: "none",
     },
     {
+      name: "SvgContextPaint",
+      status: "stable",
+    },
+    {
       name: "SvgCrossOriginAttribute",
       status: "stable",
     },
diff --git a/third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h b/third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h
index 73867d90..041af697 100644
--- a/third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h
+++ b/third_party/blink/renderer/platform/scheduler/public/task_attribution_info.h
@@ -9,34 +9,21 @@
 #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/heap/visitor.h"
-#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
 
 namespace blink::scheduler {
 
 class TaskAttributionInfo final : public GarbageCollected<TaskAttributionInfo> {
  public:
-  TaskAttributionInfo(TaskAttributionId task_id, TaskAttributionInfo* parent)
-      : task_id_(task_id),
-        parent_(parent),
-        chain_length_(parent ? parent->chain_length_ + 1 : 1) {}
+  explicit TaskAttributionInfo(TaskAttributionId task_id) : task_id_(task_id) {}
 
   ~TaskAttributionInfo() = default;
 
   TaskAttributionId Id() const { return task_id_; }
-  TaskAttributionInfo* Parent() const { return parent_.Get(); }
-  bool MaxChainLengthReached() const {
-    return chain_length_ >= kMaxChainLength;
-  }
 
-  void Trace(Visitor* visitor) const {
-    visitor->Trace(parent_);
-  }
+  void Trace(Visitor* visitor) const {}
 
  private:
-  static constexpr uint8_t kMaxChainLength = 10;
   const TaskAttributionId task_id_;
-  const Member<TaskAttributionInfo> parent_;
-  const uint8_t chain_length_;
 };
 
 }  // namespace blink::scheduler
diff --git a/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h b/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h
index fc4ceb3..0da51f5c 100644
--- a/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h
+++ b/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ATTRIBUTION_TRACKER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ATTRIBUTION_TRACKER_H_
 
-#include <optional>
 #include <utility>
 
 #include "base/functional/function_ref.h"
@@ -66,16 +65,12 @@
     TaskScope(TaskScope&& other)
         : task_tracker_(std::exchange(other.task_tracker_, nullptr)),
           script_state_(other.script_state_),
-          previous_running_task_(other.previous_running_task_),
-          previous_continuation_task_state_(
-              other.previous_continuation_task_state_) {}
+          previous_task_state_(other.previous_task_state_) {}
 
     TaskScope& operator=(TaskScope&& other) {
       task_tracker_ = std::exchange(other.task_tracker_, nullptr);
       script_state_ = other.script_state_;
-      previous_running_task_ = other.previous_running_task_;
-      previous_continuation_task_state_ =
-          other.previous_continuation_task_state_;
+      previous_task_state_ = other.previous_task_state_;
       return *this;
     }
 
@@ -85,12 +80,10 @@
 
     TaskScope(TaskAttributionTracker* tracker,
               ScriptState* script_state,
-              TaskAttributionInfo* previous_running_task,
-              ScriptWrappableTaskState* previous_continuation_task_state)
+              ScriptWrappableTaskState* previous_task_state)
         : task_tracker_(tracker),
           script_state_(script_state),
-          previous_running_task_(previous_running_task),
-          previous_continuation_task_state_(previous_continuation_task_state) {}
+          previous_task_state_(previous_task_state) {}
 
     // `task_tracker_` is tied to the lifetime of the isolate, which will
     // outlive the current task.
@@ -99,8 +92,7 @@
     // The rest are on the Oilpan heap, so these are stored as raw pointers
     // since the class is stack allocated.
     ScriptState* script_state_;
-    TaskAttributionInfo* previous_running_task_;
-    ScriptWrappableTaskState* previous_continuation_task_state_;
+    ScriptWrappableTaskState* previous_task_state_;
   };
 
   class Observer : public GarbageCollectedMixin {
@@ -163,18 +155,6 @@
   // Get the `TaskAttributionInfo` for the currently running task.
   virtual TaskAttributionInfo* RunningTask() const = 0;
 
-  // Returns true iff `task` has an ancestor task with `ancestor_id`.
-  virtual bool IsAncestor(const TaskAttributionInfo& task,
-                          TaskAttributionId anscestor_id) = 0;
-
-  // Runs `visitor` for each ancestor `TaskAttributionInfo` of `task`. `visitor`
-  // controls iteration with its return value.
-  enum class IterationStatus { kContinue, kStop };
-  virtual void ForEachAncestor(
-      const TaskAttributionInfo& task,
-      base::FunctionRef<IterationStatus(const TaskAttributionInfo& task)>
-          visitor) = 0;
-
   // Registers an observer to be notified when a `TaskScope` has been created.
   // Multiple `Observer`s can be registered, but only the innermost one will
   // receive callbacks.
diff --git a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
index 8f1575bf..58b4e0b7 100644
--- a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
+++ b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
@@ -278,9 +278,6 @@
     scoped_refptr<scheduler::WidgetScheduler> widget_scheduler,
     bool allow_raf_aligned_input)
     : client_(client),
-      last_touch_start_forced_nonblocking_due_to_fling_(false),
-      needs_low_latency_(false),
-      needs_unbuffered_input_for_debugger_(false),
       allow_raf_aligned_input_(allow_raf_aligned_input),
       main_task_runner_(main_task_runner),
       widget_scheduler_(std::move(widget_scheduler)) {
diff --git a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
index 7e9d5ef..291d13c 100644
--- a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
+++ b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
@@ -176,13 +176,20 @@
   friend class MainThreadEventQueueTest;
   friend class MainThreadEventQueueInitializationTest;
   raw_ptr<MainThreadEventQueueClient> client_;
-  bool last_touch_start_forced_nonblocking_due_to_fling_;
-  bool needs_low_latency_;
-  bool needs_unbuffered_input_for_debugger_;
-  bool allow_raf_aligned_input_;
-  bool needs_low_latency_until_pointer_up_ = false;
+  const bool allow_raf_aligned_input_;
+  bool last_touch_start_forced_nonblocking_due_to_fling_ = false;
   bool has_pointerrawupdate_handlers_ = false;
 
+  // These variables are read on the compositor thread but are
+  // written on the main thread, so we use atomics to keep them
+  // lock free. Reading these variables off of the compositor thread
+  // is best effort. It is fine that the compositor executes a slightly
+  // different path for events in flight while these variables are
+  // mutated via the main thread.
+  std::atomic<bool> needs_low_latency_ = false;
+  std::atomic<bool> needs_unbuffered_input_for_debugger_ = false;
+  std::atomic<bool> needs_low_latency_until_pointer_up_ = false;
+
   // Contains data to be shared between main thread and compositor thread.
   struct SharedState {
     SharedState();
diff --git a/third_party/blink/tools/blinkpy/common/net/git_cl.py b/third_party/blink/tools/blinkpy/common/net/git_cl.py
index 0b3ec9a..8ebdcd1 100644
--- a/third_party/blink/tools/blinkpy/common/net/git_cl.py
+++ b/third_party/blink/tools/blinkpy/common/net/git_cl.py
@@ -51,8 +51,9 @@
 
 # TODO(crbug.com/41483974): Replace `issue_number` and `patchset` paired
 # arguments in `GitCL.*` with this more meaningful type.
-class Changelist(NamedTuple):
-    issue: str
+class CLRevisionID(NamedTuple):
+    """An identifier for a Gerrit CL patchset."""
+    issue: int
     patchset: Optional[int] = None
 
     def __str__(self) -> str:
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 06f4f03..259ff6a 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
@@ -726,6 +726,7 @@
             'ui::IsCellOrTableHeader',
             'ui::IsClickable',
             'ui::IsComboBox',
+            'ui::IsComboBoxContainer',
             'ui::IsContainerWithSelectableChildren',
             'ui::IsDialog',
             'ui::IsEmbeddingElement',
diff --git a/third_party/blink/tools/blinkpy/tool/commands/build_resolver.py b/third_party/blink/tools/blinkpy/tool/commands/build_resolver.py
index 3c970f39..260d4604 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/build_resolver.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/build_resolver.py
@@ -15,7 +15,7 @@
 from blinkpy.common.net.web import Web
 from blinkpy.common.net.git_cl import (
     BuildStatuses,
-    Changelist,
+    CLRevisionID,
     GitCL,
     TryJobStatus,
 )
@@ -202,7 +202,7 @@
         if not issue_number.isdigit():
             raise UnresolvedBuildException(
                 'No issue number for current branch.')
-        cl = Changelist(issue_number, patchset)
+        cl = CLRevisionID(int(issue_number), patchset)
         _log.info(f'Fetching status for {pluralize("build", len(builders))} '
                   f'from {cl}.')
         build_statuses = self._git_cl.latest_try_jobs(issue_number,
diff --git a/third_party/blink/tools/blinkpy/w3c/common.py b/third_party/blink/tools/blinkpy/w3c/common.py
index c80c4555..1c62f3a2 100644
--- a/third_party/blink/tools/blinkpy/w3c/common.py
+++ b/third_party/blink/tools/blinkpy/w3c/common.py
@@ -18,6 +18,7 @@
 EXPORT_PR_LABEL = 'chromium-export'
 PROVISIONAL_PR_LABEL = 'do not merge yet'
 
+AUTOROLLER_EMAIL = 'wpt-autoroller@chops-service-accounts.iam.gserviceaccount.com'
 # These are only set in a new WPT checkout, and they should be consistent with
 # the bot's GitHub account (chromium-wpt-export-bot).
 DEFAULT_WPT_COMMITTER_NAME = 'Chromium WPT Sync'
diff --git a/third_party/blink/tools/blinkpy/w3c/gerrit.py b/third_party/blink/tools/blinkpy/w3c/gerrit.py
index f8c5692333..a08da14a 100644
--- a/third_party/blink/tools/blinkpy/w3c/gerrit.py
+++ b/third_party/blink/tools/blinkpy/w3c/gerrit.py
@@ -8,11 +8,13 @@
 import logging
 from datetime import datetime
 from requests.exceptions import HTTPError
-from typing import Iterator, List, Tuple
+from typing import Iterator, List, Mapping, Tuple
 from urllib.parse import urlencode, urlsplit, urlunsplit, quote
 
+from blinkpy.common.host import Host
 from blinkpy.common.net.network_transaction import NetworkTimeout
 from blinkpy.common.path_finder import RELATIVE_WPT_TESTS
+from blinkpy.common.net.git_cl import CLRevisionID
 from blinkpy.w3c.chromium_commit import ChromiumCommit
 from blinkpy.w3c.common import is_file_exportable
 
@@ -60,6 +62,12 @@
         self.user = user
         self.token = token
 
+    @classmethod
+    def from_credentials(cls, host: Host,
+                         credentials: Mapping[str, str]) -> 'GerritAPI':
+        return cls(host, credentials['GERRIT_USER'],
+                   credentials['GERRIT_TOKEN'])
+
     def get(self,
             path: str,
             query_params: List[Tuple[str, str]],
@@ -213,6 +221,15 @@
         return self._data['revisions'][self.current_revision_sha]
 
     @property
+    def current_revision_id(self) -> CLRevisionID:
+        patchset = int(self.current_revision['_number'])
+        return CLRevisionID(self.number, patchset)
+
+    @property
+    def latest_revision_id(self) -> CLRevisionID:
+        return CLRevisionID(self.number)
+
+    @property
     def has_review_started(self):
         return self._data.get('has_review_started')
 
diff --git a/third_party/blink/tools/blinkpy/w3c/import_notifier.py b/third_party/blink/tools/blinkpy/w3c/import_notifier.py
index 7812fc9..73bfa095 100644
--- a/third_party/blink/tools/blinkpy/w3c/import_notifier.py
+++ b/third_party/blink/tools/blinkpy/w3c/import_notifier.py
@@ -17,7 +17,7 @@
 
 from blinkpy.common import path_finder
 from blinkpy.common.checkout.git import CommitRange
-from blinkpy.common.net.git_cl import Changelist
+from blinkpy.common.net.git_cl import CLRevisionID
 from blinkpy.common.system.executive import ScriptError
 from blinkpy.web_tests.models import typ_types
 from blinkpy.web_tests.models.test_expectations import (
@@ -35,15 +35,19 @@
     BuganizerError,
     BuganizerIssue,
 )
-from blinkpy.w3c.common import WPT_GH_URL, WPT_GH_RANGE_URL_TEMPLATE
+from blinkpy.w3c.common import (
+    AUTOROLLER_EMAIL,
+    WPT_GH_URL,
+    WPT_GH_RANGE_URL_TEMPLATE,
+)
 from blinkpy.w3c.directory_owners_extractor import DirectoryOwnersExtractor
+from blinkpy.w3c.gerrit import GerritAPI, GerritCL, OutputOption
 from blinkpy.w3c.wpt_expectations_updater import WPTExpectationsUpdater
 from blinkpy.w3c.wpt_results_processor import TestType
 
 _log = logging.getLogger(__name__)
 
 GITHUB_COMMIT_PREFIX = WPT_GH_URL + 'commit/'
-SHORT_GERRIT_PREFIX = 'https://crrev.com/c/'
 CHECKS_URL_TEMPLATE = 'https://chromium-review.googlesource.com/c/chromium/src/+/{}/{}?checksPatchset=1&tab=checks'
 
 BUGANIZER_WPT_COMPONENT = '1456176'
@@ -54,10 +58,12 @@
                  host,
                  chromium_git,
                  local_wpt,
+                 gerrit_api: GerritAPI,
                  buganizer_client: Optional[BuganizerClient] = None):
         self.host = host
         self.git = chromium_git
         self.local_wpt = local_wpt
+        self._gerrit_api = gerrit_api
         self._buganizer_client = buganizer_client or BuganizerClient()
 
         self.default_port = host.port_factory.get()
@@ -71,9 +77,7 @@
              import_range: CommitRange,
              wpt_revision_start,
              wpt_revision_end,
-             issue,
-             patchset,
-             dry_run=True):
+             dry_run: bool = False) -> bool:
         """Files bug reports for new failures.
 
         Arguments:
@@ -83,31 +87,37 @@
                 (exclusive), i.e. the last imported revision.
             wpt_revision_end: The end of the imported WPT revision range
                 (inclusive), i.e. the current imported revision.
-            issue: The issue number of the import CL (a string).
-            patchset: The patchset number of the import CL (a string).
             dry_run: If True, no bugs will be actually filed to crbug.com.
 
+        Returns:
+            True iff the notifications were successful.
+
         Note: "test names" are paths of the tests relative to web_tests.
         """
-        gerrit_url = str(Changelist(issue))
-        gerrit_url_with_ps = str(Changelist(issue, patchset)) + '/'
+        cl = self._cl_for_wpt_revision(wpt_revision_end)
+        if not cl:
+            _log.warning('Unable to find last imported CL; '
+                         'skipping bug filing.')
+            return False
 
-        self.examine_baseline_changes(import_range, gerrit_url_with_ps)
+        self.examine_baseline_changes(import_range, cl.current_revision_id)
         self.examine_new_test_expectations(import_range)
-
         bugs = self.create_bugs_from_new_failures(wpt_revision_start,
-                                                  wpt_revision_end, issue)
+                                                  wpt_revision_end,
+                                                  cl.latest_revision_id)
         self.file_bugs(bugs, dry_run)
+        return True
 
     def examine_baseline_changes(self, import_range: CommitRange,
-                                 gerrit_url_with_ps: str):
+                                 cl_revision: CLRevisionID):
         """Examines all changed baselines to find new failures.
 
         Arguments:
             import_range: The commits before (exclusive) and after (inclusive)
                 the imported CL.
-            gerrit_url_with_ps: Gerrit URL of this CL with the patchset number.
+            cl_revision: Issue and patchset numbers of the imported CL.
         """
+        assert cl_revision.patchset, cl_revision
         sep = re.escape(self.host.filesystem.sep)
         platform_pattern = f'(platform|flag-specific){sep}([^{sep}]+){sep}'
         baseline_pattern = re.compile(f'web_tests{sep}({platform_pattern})?')
@@ -127,7 +137,7 @@
             if self.more_failures_in_baseline(lines_before, lines_after):
                 self.new_failures_by_directory[directory].append(
                     TestFailure.from_file(test, changed_file,
-                                          gerrit_url_with_ps))
+                                          f'{cl_revision}/'))
 
     def more_failures_in_baseline(
         self,
@@ -206,29 +216,31 @@
             {abs_path: self.git.show_blob(path, ref).decode()})
         return expectations.get_updated_lines(abs_path)
 
-    def create_bugs_from_new_failures(self, wpt_revision_start: str,
-                                      wpt_revision_end: str,
-                                      issue: str) -> List[BuganizerIssue]:
+    def create_bugs_from_new_failures(
+        self,
+        wpt_revision_start: str,
+        wpt_revision_end: str,
+        cl_revision: CLRevisionID,
+    ) -> List[BuganizerIssue]:
         """Files bug reports for new failures.
 
-        Args:
+        Arguments:
             wpt_revision_start: The start of the imported WPT revision range
                 (exclusive), i.e. the last imported revision.
             wpt_revision_end: The end of the imported WPT revision range
                 (inclusive), i.e. the current imported revision.
-            issue: Issue number of the CL.
+            cl_revision: Issue number of the imported CL.
 
-        Return:
+        Returns:
             A list of issues that should be filed.
         """
-        gerrit_url = SHORT_GERRIT_PREFIX + issue
-        patchset1_checks_url = CHECKS_URL_TEMPLATE.format(issue, '1')
+        checks_url = CHECKS_URL_TEMPLATE.format(cl_revision.issue, '1')
         imported_commits = self.local_wpt.commits_in_range(
             wpt_revision_start, wpt_revision_end)
         bugs = []
         for directory, failures in self.new_failures_by_directory.items():
             summary = '[WPT] New failures introduced in {} by import {}'.format(
-                directory, gerrit_url)
+                directory, cl_revision)
 
             full_directory = self.host.filesystem.join(
                 self.finder.web_tests_dir(), directory)
@@ -249,10 +261,10 @@
 
             prologue = ('WPT import {} introduced new failures in {}:\n\n'
                         'List of new failures:\n'.format(
-                            gerrit_url, directory))
+                            cl_revision, directory))
             failure_list = ''.join(f'{failure.message}\n'
                                    for failure in failures)
-            checks = '\nSee {} for details.\n'.format(patchset1_checks_url)
+            checks = '\nSee {} for details.\n'.format(checks_url)
 
             expectations_statement = (
                 '\nExpectations or baseline files [0] have been automatically '
@@ -359,6 +371,16 @@
             except BuganizerError as error:
                 _log.exception('Failed to file bug', exc_info=error)
 
+    def _cl_for_wpt_revision(self, wpt_revision: str) -> Optional[GerritCL]:
+        query = ' '.join([
+            f'owner:{AUTOROLLER_EMAIL}',
+            f'prefixsubject:"Import wpt@{wpt_revision}"',
+            'status:merged',
+        ])
+        output = GerritAPI.DEFAULT_OUTPUT | OutputOption.MESSAGES
+        cls = self._gerrit_api.query_cls(query, limit=1, output_options=output)
+        return cls[0] if cls else None
+
 
 class TestFailure(NamedTuple):
     """A simple abstraction of a new test failure for the notifier."""
diff --git a/third_party/blink/tools/blinkpy/w3c/import_notifier_unittest.py b/third_party/blink/tools/blinkpy/w3c/import_notifier_unittest.py
index 7ca79e29..f87c1d0 100644
--- a/third_party/blink/tools/blinkpy/w3c/import_notifier_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/import_notifier_unittest.py
@@ -9,6 +9,8 @@
 
 from blinkpy.common.checkout.git import CommitRange
 from blinkpy.common.host_mock import MockHost
+from blinkpy.common.net.git_cl import CLRevisionID
+from blinkpy.common.net.rpc import RESPONSE_PREFIX
 from blinkpy.common.path_finder import (
     RELATIVE_WEB_TESTS,
     PathFinder,
@@ -17,6 +19,7 @@
 from blinkpy.common.system.filesystem_mock import MockFileSystem
 from blinkpy.w3c.buganizer import BuganizerError
 from blinkpy.w3c.directory_owners_extractor import WPTDirMetadata
+from blinkpy.w3c.gerrit import GerritAPI
 from blinkpy.w3c.local_wpt_mock import MockLocalWPT
 from blinkpy.w3c.import_notifier import (ImportNotifier, TestFailure,
                                          CHECKS_URL_TEMPLATE)
@@ -62,8 +65,9 @@
         self.git = self.host.git()
         self.local_wpt = MockLocalWPT()
         self.buganizer_client = mock.Mock()
+        self.gerrit_api = GerritAPI(self.host, 'wpt-autoroller', 'fake-token')
         self.notifier = ImportNotifier(self.host, self.git, self.local_wpt,
-                                       self.buganizer_client)
+                                       self.gerrit_api, self.buganizer_client)
         self.notifier.default_port.set_option_default('manifest_update', False)
 
     def test_more_failures_in_baseline_more_fails(self):
@@ -162,6 +166,64 @@
             self.git.add(path)
         self.git.commit_locally_with_message('commit')
 
+    def test_main(self):
+        """Exercise the `ImportNotifier` end-to-end happy path."""
+        contents_before = textwrap.dedent("""\
+            # results: [ Failure Pass Timeout ]
+            """)
+        self._write_and_commit({
+            MOCK_WEB_TESTS + 'external/wpt/foo/DIR_METADATA':
+            '',
+            MOCK_WEB_TESTS + 'TestExpectations':
+            contents_before,
+        })
+        contents_after = textwrap.dedent("""\
+            # results: [ Failure Pass Timeout ]
+            external/wpt/foo/bar.html [ Failure ]
+            """)
+        self._write_and_commit({
+            MOCK_WEB_TESTS + 'TestExpectations':
+            contents_after,
+        })
+        gerrit_query = (
+            'https://chromium-review.googlesource.com/changes/'
+            '?q=owner:wpt-autoroller%40chops-service-accounts.'
+            'iam.gserviceaccount.com'
+            '+prefixsubject:"Import+wpt%40abcdef"+status:merged'
+            '&n=1&o=CURRENT_FILES&o=CURRENT_REVISION&o=COMMIT_FOOTERS'
+            '&o=DETAILED_ACCOUNTS&o=MESSAGES')
+        payload = {
+            '_number': 77777,
+            'change_id': 'I8888',
+            'current_revision': '999999',
+            'revisions': {
+                '999999': {
+                    '_number': 4,
+                },
+            },
+        }
+        self.host.web.urls = {
+            gerrit_query:
+            RESPONSE_PREFIX + b'\n' + json.dumps([payload]).encode(),
+        }
+
+        dir_metadata = WPTDirMetadata(buganizer_public_component='123',
+                                      should_notify=True)
+        with mock.patch.object(self.notifier.owners_extractor,
+                               'read_dir_metadata',
+                               return_value=dir_metadata):
+            self.notifier.main(CommitRange('HEAD~1', 'HEAD'), '543210',
+                               'abcdef')
+
+        self.buganizer_client.NewIssue.assert_called_once()
+        issue = self.buganizer_client.NewIssue.call_args.args[0]
+        self.assertEqual(
+            issue.title, '[WPT] New failures introduced in external/wpt/foo '
+            'by import https://crrev.com/c/77777')
+        self.assertEqual(issue.component_id, '123')
+        self.assertEqual(issue.cc, [])
+        self.assertIn('543210...abcdef', issue.description)
+
     def test_examine_baseline_changes(self):
         contents_before = textwrap.dedent("""\
             [PASS] subtest
@@ -190,8 +252,7 @@
 
         gerrit_url_with_ps = 'https://crrev.com/c/12345/3/'
         self.notifier.examine_baseline_changes(CommitRange('HEAD~1', 'HEAD'),
-                                               gerrit_url_with_ps)
-
+                                               CLRevisionID(12345, 3))
         self.assertEqual(
             self.notifier.new_failures_by_directory, {
                 'external/wpt/foo': [
@@ -335,7 +396,7 @@
             ]
         }
         bugs = self.notifier.create_bugs_from_new_failures(
-            'SHA_START', 'SHA_END', '12345')
+            'SHA_START', 'SHA_END', CLRevisionID(12345))
 
         # Only one directory has WPT-NOTIFY enabled.
         self.assertEqual(len(bugs), 1)
@@ -376,7 +437,7 @@
                                'read_dir_metadata',
                                return_value=dir_metadata):
             (bug, ) = self.notifier.create_bugs_from_new_failures(
-                'SHA_START', 'SHA_END', '12345')
+                'SHA_START', 'SHA_END', CLRevisionID(12345))
             self.assertEqual(bug.cc, [])
             self.assertEqual(bug.component_id, '123')
             self.assertEqual(
@@ -397,7 +458,7 @@
                                'read_dir_metadata',
                                return_value=dir_metadata):
             bugs = self.notifier.create_bugs_from_new_failures(
-                'SHA_START', 'SHA_END', 'https://crrev.com/c/12345')
+                'SHA_START', 'SHA_END', CLRevisionID(12345))
         self.notifier.file_bugs(bugs, dry_run=True)
         self.buganizer_client.NewIssue.assert_not_called()
 
@@ -421,7 +482,7 @@
                                'read_dir_metadata',
                                return_value=dir_metadata):
             bugs = self.notifier.create_bugs_from_new_failures(
-                'SHA_START', 'SHA_END', 'https://crrev.com/c/12345')
+                'SHA_START', 'SHA_END', CLRevisionID(12345))
         self.assertEqual(len(bugs), 2)
 
         self.buganizer_client.NewIssue.side_effect = BuganizerError
diff --git a/third_party/blink/tools/blinkpy/w3c/test_exporter.py b/third_party/blink/tools/blinkpy/w3c/test_exporter.py
index 2415af7..075eeff 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_exporter.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_exporter.py
@@ -65,8 +65,8 @@
             host=self.host,
             user=credentials['GH_USER'],
             token=credentials['GH_TOKEN'])
-        self.gerrit = self.gerrit or GerritAPI(
-            self.host, credentials['GERRIT_USER'], credentials['GERRIT_TOKEN'])
+        self.gerrit = self.gerrit or GerritAPI.from_credentials(
+            self.host, credentials)
         self.local_repo = self.local_repo or self.project_config.local_repo_factory(
             host=self.host, gh_token=credentials['GH_TOKEN'])
 
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer.py b/third_party/blink/tools/blinkpy/w3c/test_importer.py
index 904aed5..d5a3e69 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer.py
@@ -24,6 +24,7 @@
 from blinkpy.w3c.chromium_exportable_commits import exportable_commits_over_last_n_commits
 from blinkpy.w3c.common import read_credentials, is_testharness_baseline, is_file_exportable, WPT_GH_URL
 from blinkpy.w3c.directory_owners_extractor import DirectoryOwnersExtractor
+from blinkpy.w3c.gerrit import GerritAPI
 from blinkpy.w3c.local_wpt import LocalWPT
 from blinkpy.w3c.test_copier import TestCopier
 from blinkpy.w3c.wpt_expectations_updater import WPTExpectationsUpdater
@@ -200,7 +201,9 @@
             if self.git_cl.get_cl_status().lower() != 'closed':
                 self.git_cl.close()
 
-        if not self.send_notifications(local_wpt, options.auto_file_bugs):
+        gerrit_api = GerritAPI.from_credentials(self.host, credentials)
+        if not self.send_notifications(local_wpt, gerrit_api,
+                                       options.auto_file_bugs):
             return 1
         return 0
 
@@ -679,19 +682,18 @@
             _log.error('Cannot find last WPT import.')
             return None
 
-    def send_notifications(self, local_wpt, auto_file_bugs):
+    def send_notifications(self,
+                           local_wpt: LocalWPT,
+                           gerrit_api: GerritAPI,
+                           auto_file_bugs: bool = True) -> bool:
         from blinkpy.w3c.import_notifier import ImportNotifier
-        issue = self.git_cl.run(['status', '--field=id']).strip()
-        patchset = self.git_cl.run(['status', '--field=patch']).strip()
         # Construct the notifier here so that any errors won't affect the import.
-        notifier = ImportNotifier(self.host, self.project_git, local_wpt)
-        notifier.main(CommitRange('origin/main', 'HEAD'),
-                      self.last_wpt_revision,
-                      self.wpt_revision,
-                      issue,
-                      patchset,
-                      dry_run=not auto_file_bugs)
-        return True
+        notifier = ImportNotifier(self.host, self.project_git, local_wpt,
+                                  gerrit_api)
+        return notifier.main(CommitRange('origin/main', 'HEAD'),
+                             self.last_wpt_revision,
+                             self.wpt_revision,
+                             dry_run=(not auto_file_bugs))
 
     def update_testlist_with_idlharness_changes(self, testlist_path):
         """Update testlist file to include idlharness test changes
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 20c115c..f7dfa61 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -297,11 +297,6 @@
 # View transition SPA failures with MPA serialization.
 crbug.com/1443559 [ Mac ] virtual/view-transition-mpa-serialization/external/wpt/css/css-view-transitions/fractional-translation-from-transform.html [ Failure ]
 
-# View transition failures with accessibility.
-crbug.com/1486181 virtual/view-transition-mpa-serialization/external/wpt/css/css-view-transitions/content-visibility-auto-shared-element.html [ Crash ]
-crbug.com/1486181 virtual/view-transition-wide-gamut/external/wpt/css/css-view-transitions/content-visibility-auto-shared-element.html [ Crash ]
-crbug.com/1486181 virtual/view-transition/external/wpt/css/css-view-transitions/content-visibility-auto-shared-element.html [ Crash ]
-
 # Flaky on Mac
 crbug.com/40946176 [ Mac ] virtual/view-transition/external/wpt/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter.html [ Failure Pass ]
 crbug.com/40946176 [ Mac ] virtual/view-transition-mpa-serialization/external/wpt/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter.html [ Failure Pass ]
@@ -6935,6 +6930,9 @@
 crbug.com/330126415 [ Win ] http/tests/devtools/console/console-stack-overflow.js [ Failure Pass ]
 crbug.com/330126277 [ Win ] media/controls/video-overlay-cast-dark-rendering.html [ Failure Pass ]
 
+# Gardener 2023-03-18 (EST)
+crbug.com/330207209 [ Linux ] external/wpt/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-immediate.html [ Failure Pass ]
+
 # Flaky test on Mac14/Mac14-arm64
 [ Mac14 ] css3/blending/svg-blend-exclusion.html [ Failure ]
 [ Mac14 ] css3/blending/svg-blend-multiply.html [ Failure ]
diff --git a/third_party/blink/web_tests/WebGPUExpectations b/third_party/blink/web_tests/WebGPUExpectations
index 7a453d38..5e051dc 100644
--- a/third_party/blink/web_tests/WebGPUExpectations
+++ b/third_party/blink/web_tests/WebGPUExpectations
@@ -3,21 +3,22 @@
 # results: [ Timeout Crash Pass Failure Slow Skip RetryOnFailure ]
 
 # WebGPU tests are only run on GPU bots, so they are skipped by default and run
-# separately from other Web Tests. The WebGPU reftests use this expectations
-# file. Note, non-ref tests use a different expectations file:
-# //third_party/dawn/webgpu-cts/expectations.txt
+# separately from other Web Tests. The WebGPU tests in blink/web_tests (both
+# reftests from WebGPU CTS, and Chromium-specific tests) use this expectations
+# file. Note, *non-reftest* WebGPU CTS tests use a different harness with a
+# different expectations file: //third_party/dawn/webgpu-cts/expectations.txt
 
 # Workaround page loaded when content_shell starts, but not run as a test.
 crbug.com/1231599 wpt_internal/webgpu/000_run_me_first.https.html [ Skip ]
 
-# STORAGE_BINDING is only support on WIN.
+# STORAGE_BINDING is only supported on Windows.
 crbug.com/1241369 [ Mac ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_rgba8unorm_store.https.html [ Skip ]
 crbug.com/1241369 [ Mac ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_rgba16float_store.https.html [ Skip ]
 crbug.com/1241369 [ Linux ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_rgba8unorm_store.https.html [ Skip ]
 crbug.com/1241369 [ Linux ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_rgba16float_store.https.html [ Skip ]
 
-# SharedImageBackingFactoryIOSurface takes rgba8unorm as bgra8unorm.
-# https://source.chromium.org/chromium/chromium/src/+/main:gpu/command_buffer/service/shared_image_backing_factory_iosurface.mm;l=217?q=SharedImageBackingFactoryIOSurface::CreateSharedImage&ss=chromium%2Fchromium%2Fsrc
+# IOSurfaceImageBackingFactory::CreateSharedImage overrides rgba8unorm to bgra8unorm.
+# https://source.chromium.org/chromium/chromium/src/+/main:gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.mm;l=284;drc=72e88ec372c13cf0b6ee0a76fb639e2998399746
 crbug.com/1241369 [ Mac ] wpt_internal/webgpu/web_platform/reftests/canvas_clear.https.html [ Skip ]
 
 # Test fails due to scaling precision issues
diff --git a/third_party/blink/web_tests/accessibility/aria-row-name.html b/third_party/blink/web_tests/accessibility/aria-row-name.html
index 02e65c07..52a260d 100644
--- a/third_party/blink/web_tests/accessibility/aria-row-name.html
+++ b/third_party/blink/web_tests/accessibility/aria-row-name.html
@@ -20,6 +20,21 @@
     <div role="gridcell">data</div>
   </div>
 </div>
+
+<input type="text" role="combobox" aria-activedescendant="treegrid4-row1">
+<div role="grid">
+    <div id="treegrid4-row1" role="row">
+        <div role="gridcell">data</div>
+    </div>
+</div>
+</body>
+
+<input type="text" role="combobox" aria-controls="treegrid5" aria-activedescendant="treegrid5-row1">
+<div id="treegrid5" role="grid">
+    <div id="treegrid5-row1" role="row">
+        <div role="gridcell">data</div>
+    </div>
+</div>
 </body>
 
 <script>
@@ -42,4 +57,14 @@
     assert_equals(axRow.name, '');
 }, "The row's name must empty when the parent is not focusable and there is no other labelling markup (for performance reasons)");
 
+test(function(t) {
+    var axRow = axElementById("treegrid4-row1");
+    assert_equals(axRow.name, 'data');
+}, "The row's name must concatenate the children if a textfield controlling the grid has aria-activedescendant");
+
+test(function(t) {
+    var axRow = axElementById("treegrid5-row1");
+    assert_equals(axRow.name, 'data');
+}, "The row's name must concatenate the children if the grid has a previous sibling input with aria-activedescendant");
+
 </script>
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 0a7e5b773..c7997b4 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
@@ -135066,6 +135066,35 @@
         {}
        ]
       ],
+      "custom-highlight-painting-019.html": [
+       "8c5ccbf020c8670c54334570341450cf627c50c9",
+       [
+        null,
+        [
+         [
+          "/css/css-highlight-api/painting/custom-highlight-painting-019-ref.html",
+          "=="
+         ]
+        ],
+        {
+         "fuzzy": [
+          [
+           null,
+           [
+            [
+             0,
+             130
+            ],
+            [
+             0,
+             4
+            ]
+           ]
+          ]
+         ]
+        }
+       ]
+      ],
       "custom-highlight-painting-below-grammar.html": [
        "93d2560040b791a0685151cc29587c771fad8c03",
        [
@@ -282978,6 +283007,19 @@
      ]
     },
     "responsive": {
+     "neutral-keyframe.html": [
+      "813aa7c04c5c2925bf620c31cdcf43c34bcd1536",
+      [
+       null,
+       [
+        [
+         "/web-animations/responsive/neutral-keyframe-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "toggle-animated-iframe-visibility.html": [
       "b5c32f26d8df952e0e5e673af41473e2f8474027",
       [
@@ -292860,7 +292902,7 @@
        []
       ],
       "continue_on.py": [
-       "42b4f3f8fdcbf6bcd436806aada2e6d3c09d2c6d",
+       "6a1a9900d756c4183bf4b7635ce2c855aa34510e",
        []
       ],
       "disconnect-iframe.html": [
@@ -292972,7 +293014,7 @@
        []
       ],
       "request-params-check.py": [
-       "6c610e6e201dd157de1338546fd6970a946e5fb6",
+       "08c28e32b7942d6db83f7b9c2e4664c9cf47987f",
        []
       ],
       "resolve.html": [
@@ -293004,19 +293046,19 @@
        []
       ],
       "token_with_account_id.py": [
-       "52fb20184bc081186e7f9aaab7561dc87c8906bd",
+       "04e7b5b56b9e560b8ebdaef150ef5037aeb18be0",
        []
       ],
       "token_with_auto_selected_flag.py": [
-       "93ccf3ee7e2277716dc1c9d5b02805b9e2b398cc",
+       "3e011ce788c88aae5363d367a6c653b655867354",
        []
       ],
       "token_with_http_error.py": [
-       "c8d95ab63d7ebda6d32a60307f123bd858f39891",
+       "05b9945ba80dad9fd3bf87a467d50a7f4e9d3071",
        []
       ],
       "token_with_rp_mode.py": [
-       "515736416f49cb3e7116c1cdb98f42a23e959f4f",
+       "add634c99bb959eefe821ffad9bb12d9e2a904a2",
        []
       ],
       "two_accounts.py": [
@@ -318209,6 +318251,10 @@
        "8cb5b69d98c6a82e7385f413a57c604e409ccd88",
        []
       ],
+      "custom-highlight-painting-019-ref.html": [
+       "22662336845f03aefa743b3f1721c127f2789b18",
+       []
+      ],
       "custom-highlight-painting-below-grammar-ref.html": [
        "d660daf7c745efeb663e652d83a6e92c9d6e39d0",
        []
@@ -348796,10 +348842,6 @@
       "e2b453f4638fd0efd8a0b89e6cdae05d351b8b12",
       []
      ],
-     "unreached.https.html": [
-      "bd389ec4fbd5a8f47b83f7c8299d5669890120cd",
-      []
-     ],
      "utils.js": [
       "d6cca9143752c15ee6470d25058a6535b55a1466",
       []
@@ -390738,6 +390780,10 @@
       "1bae74b022786fecb5824dcceefadacfa5a9b438",
       []
      ],
+     "neutral-keyframe-ref.html": [
+      "3893330fb969bbf46257de4b7a984a9bf2996ebb",
+      []
+     ],
      "perspective-expected.txt": [
       "e170d2976579d8321ebaeff7e318ba91fff99a16",
       []
@@ -390748,6 +390794,10 @@
        []
       ]
      },
+     "responsive-test.js": [
+      "feca53259cf4ba600675440d463d8a9b65c8e7c9",
+      []
+     ],
      "rowGap-expected.txt": [
       "ec7b6457b1f7e0a42557615d391d8cfe50dfbdb4",
       []
@@ -437691,14 +437741,14 @@
       ]
      ],
      "at-position-try-allowed-declarations.html": [
-      "622e9827b449eecdcfdf2680d58174c550ca4631",
+      "5586000e7630b226055e9b6b90fc63716adb6a8a",
       [
        null,
        {}
       ]
      ],
      "at-position-try-cssom.html": [
-      "dc248f4e511e66c20a384cbe23b407fb576ccb19",
+      "91172c518531316323566c9bba24ecfc0e2e27fd",
       [
        null,
        {}
@@ -437909,6 +437959,13 @@
        {}
       ]
      ],
+     "position-try-cascade.html": [
+      "c5ee04b3f4f5218b3a9cb0f3120895c4eef5986f",
+      [
+       null,
+       {}
+      ]
+     ],
      "position-try-container-query.html": [
       "528217c917c5f1e2b948a54bf114addf45d2e795",
       [
@@ -437937,6 +437994,13 @@
        {}
       ]
      ],
+     "position-try-initial-transition.html": [
+      "0e81607ae65bdf1a2848e3de3f1cf5d746c30ee9",
+      [
+       null,
+       {}
+      ]
+     ],
      "position-try-options-limit.html": [
       "32a4f592fa05fcc4b4f058a60c92d2b37d0afb75",
       [
@@ -468974,6 +469038,16 @@
        {}
       ]
      ],
+     "transition-in-hidden-page.html": [
+      "af8050eb77905ec6c37a490db0ff17b09a6e5deb",
+      [
+       null,
+       {
+        "testdriver": true,
+        "timeout": "long"
+       }
+      ]
+     ],
      "transition-skipped-after-animation-started.html": [
       "56c477a55f6e6e4dfc73ca40077dc0938ee6a1d7",
       [
@@ -499646,15 +499720,6 @@
       }
      ]
     ],
-    "content-shared-storage-get.https.html": [
-     "8afa8de54160ea130300f6973390816e8b3d7e88",
-     [
-      null,
-      {
-       "timeout": "long"
-      }
-     ]
-    ],
     "coop-bcg-swap.https.html": [
      "5a414fdfa168ca3aef0eeef67199e8d71c47fdc4",
      [
@@ -499825,13 +499890,6 @@
       {}
      ]
     ],
-    "disable-untrusted-network.https.html": [
-     "726728e4899413d1a1aefe9fab73f54c1014ed79",
-     [
-      null,
-      {}
-     ]
-    ],
     "disallowed-navigation-to-blob.https.html": [
      "3722609410745847a34b91555f3cd8e5b402d0bf",
      [
@@ -500478,44 +500536,6 @@
       {}
      ]
     ],
-    "revoke-nested-fenced-frame-in-iframe-navigation.https.html": [
-     "3a44c3c2f46eaa6578eb88187d5f04e34f3cb987",
-     [
-      null,
-      {
-       "timeout": "long"
-      }
-     ]
-    ],
-    "revoke-nested-fenced-frame-navigation.https.html": [
-     "b80350a588891944198b5762a903389521b4544f",
-     [
-      null,
-      {
-       "timeout": "long"
-      }
-     ]
-    ],
-    "revoke-popup.https.html": [
-     "e4a2bb26ad2f74354bbeeb6e5f8edada9b30ba99",
-     [
-      null,
-      {
-       "testdriver": true,
-       "timeout": "long"
-      }
-     ]
-    ],
-    "revoke-unfenced-top-navigation.https.html": [
-     "873404768f0cdca9e912337f1829c47c67f7a9f1",
-     [
-      null,
-      {
-       "testdriver": true,
-       "timeout": "long"
-      }
-     ]
-    ],
     "sandbox-attribute.https.html": [
      "1458145e4377e573f8aa13f5cb4d0dfdc7e09182",
      [
@@ -648945,7 +648965,7 @@
       ]
      ],
      "grid-roles.html": [
-      "c40771ba9f2e6825e3d120b70bd2eb5d2231bf28",
+      "2ca3705cf01a6dd462ff4371ca3d01c4234ae50c",
       [
        null,
        {
@@ -648963,7 +648983,7 @@
       ]
      ],
      "list-roles.html": [
-      "71c672c60132c73a554799b99e6c8ebebf5b9067",
+      "7917263f87cfd7877250d8618ed00186b75784ff",
       [
        null,
        {
@@ -648972,7 +648992,7 @@
       ]
      ],
      "listbox-roles.html": [
-      "50208229278d4f49e0651d4e3e18a10d457d16a8",
+      "3d26f04d51a343131b67145a429adf6d93628a66",
       [
        null,
        {
@@ -648981,7 +649001,7 @@
       ]
      ],
      "menu-roles.html": [
-      "4af4d8d2ff02c841178fd197430cc82d00239430",
+      "15b8ddc2e87fa12dd15b8ba563039583deb5d7f9",
       [
        null,
        {
@@ -649026,7 +649046,7 @@
       ]
      ],
      "tab-roles.html": [
-      "e3d14741c84516e1c6aa51987a1c5485a3b4545a",
+      "fafbbf95fbcb13d9d78706c4e3aa58bb610a261d",
       [
        null,
        {
@@ -649044,7 +649064,7 @@
       ]
      ],
      "tree-roles.html": [
-      "eb1084040deb6bba5ba8d266882b6177132442b3",
+      "5d179fbcbf281160d90813e38bd7e660f98cbce0",
       [
        null,
        {
@@ -654231,6 +654251,13 @@
        {}
       ]
      ],
+     "background-position-responsive.html": [
+      "7b1a6cd4dbfe0fa1fac615a9bcbe42a7a47d8a6f",
+      [
+       null,
+       {}
+      ]
+     ],
      "backgroundPosition.html": [
       "cd6c6991a361bfecd70bdecbea946570153b68b7",
       [
@@ -654273,6 +654300,13 @@
        {}
       ]
      ],
+     "box-shadow-responsive.html": [
+      "4f5325dca6d5b965e75378ed19e39c8a5068c5c2",
+      [
+       null,
+       {}
+      ]
+     ],
      "boxShadow.html": [
       "bd1911132dd20b8d8efbc40b0d91ab0e9777d093",
       [
diff --git a/third_party/blink/web_tests/external/wpt/accname/name/comp_label-expected.txt b/third_party/blink/web_tests/external/wpt/accname/name/comp_label-expected.txt
index fc9b4158..f3bb00108 100644
--- a/third_party/blink/web_tests/external/wpt/accname/name/comp_label-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/accname/name/comp_label-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] aria-label undefined on img w/ alt
   assert_equals: <img alt="alt" aria-label="undefined" data-expectedlabel="alt" data-testname="aria-label undefined on img w/ alt" class="ex"> expected "alt" but got "undefined"
 [FAIL] aria-label undefined on img w/o alt
@@ -12,11 +12,5 @@
   assert_equals: <img alt="" aria-label="undefined" data-expectedlabel="title" data-testname="aria-label undefined on img w/ empty alt but w/ title" class="ex"> expected "title" but got "undefined"
 [FAIL] button's hidden referenced name (visibility:hidden) with hidden aria-labelledby traversal falls back to aria-label
   assert_equals: <button aria-labelledby="span4" aria-label="foo" data-expectedlabel="foo" data-testname="button's hidden referenced name (visibility:hidden) with hidden aria-labelledby traversal falls back to aria-label" class="ex">\n  <span id="span4">\n    <span id="span5" style="visibility:hidden;">label</span>\n  </span>\n  x\n</button> expected "foo" but got "label"
-[FAIL] textarea with tab character as aria-label does not use aria-label as name
-  assert_equals: <textarea aria-label="  " title="title" data-testname="textarea with tab character as aria-label does not use aria-label as name" data-expectedlabel="title" class="ex">textarea contents</textarea> expected "title" but got ""
-[FAIL] button with carriage return as aria-label does not use aria-label as name
-  assert_equals: <button aria-label="\n" data-testname="button with carriage return as aria-label does not use aria-label as name" data-expectedlabel="my button" class="ex">my button</button> expected "my button" but got ""
-[FAIL] button with space characters as aria-label does not use aria-label as name
-  assert_equals: <button aria-label="      " data-testname="button with space characters as aria-label does not use aria-label as name" data-expectedlabel="my button" class="ex">my button</button> expected "my button" but got ""
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-repeats.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-repeats.html
index 9cee41f..a2bfa488 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-repeats.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-repeats.html
@@ -55,7 +55,7 @@
 </script>
 
 <div id=open2>
-  <template shadowrootmode=open shadowrootdelegatesfocus shadowrootclonable serializable>
+  <template shadowrootmode=open shadowrootdelegatesfocus shadowrootclonable shadowrootserializable>
     Open, delegates focus (not the default), clonable (not the default)
     serializable (not the default), named slot assignment (the default)
   </template>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/gethtml.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/gethtml.tentative.html
index eabd391..c03749c 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/gethtml.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/gethtml.tentative.html
@@ -36,7 +36,7 @@
       default: throw new Error(`Invalid serializable ${serializable}`);
     }
     const delegatesAttr = delegatesFocus ? ' shadowrootdelegatesfocus=""' : '';
-    const serializableAttr = expectedSerializable ? ' serializable=""' : '';
+    const serializableAttr = expectedSerializable ? ' shadowrootserializable=""' : '';
     const clonableAttr = clonable ? ' shadowrootclonable=""' : '';
 
     if (allowsShadowDom && declarativeShadowDom) {
diff --git a/third_party/blink/web_tests/external/wpt/wai-aria/role/grid-roles.html b/third_party/blink/web_tests/external/wpt/wai-aria/role/grid-roles.html
index c40771ba9..2ca3705 100644
--- a/third_party/blink/web_tests/external/wpt/wai-aria/role/grid-roles.html
+++ b/third_party/blink/web_tests/external/wpt/wai-aria/role/grid-roles.html
@@ -43,6 +43,12 @@
     </div>
   </div>
 
+  <!--
+    CORE-AAM requires that, for elements with roles not contained in the
+    required context, user agents must ignore the role token and return the
+    computed role as if the ignored role token had not been included.
+    See https://w3c.github.io/core-aam/#roleMappingComputedRole
+  -->
   <span role="row" data-testname="orphaned row outside the context of table" class="ex-generic">x</span>
   <span role="rowgroup" data-testname="orphaned rowgroup outside the context of row" class="ex-generic">x</span>
   <div role="gridcell" data-testname="orphaned div with gridcell role outside the context of row" class="ex-generic">x</div>
diff --git a/third_party/blink/web_tests/external/wpt/wai-aria/role/list-roles.html b/third_party/blink/web_tests/external/wpt/wai-aria/role/list-roles.html
index 71c672c..7917263 100644
--- a/third_party/blink/web_tests/external/wpt/wai-aria/role/list-roles.html
+++ b/third_party/blink/web_tests/external/wpt/wai-aria/role/list-roles.html
@@ -18,6 +18,12 @@
   <div role="listitem" data-testname="last simple listitem" data-expectedrole="listitem" class="ex">x</div>
 </div>
 
+<!--
+  CORE-AAM requires that, for elements with roles not contained in the
+  required context, user agents must ignore the role token and return the
+  computed role as if the ignored role token had not been included.
+  See https://w3c.github.io/core-aam/#roleMappingComputedRole
+-->
 <div role="listitem" data-testname="orphan div with listitem role" class="ex-generic">x</div>
 <p role="listitem" data-testname="orphan p with listitem role" data-expectedrole="paragraph" class="ex">x</p>
 
diff --git a/third_party/blink/web_tests/external/wpt/wai-aria/role/listbox-roles.html b/third_party/blink/web_tests/external/wpt/wai-aria/role/listbox-roles.html
index 50208229..3d26f04 100644
--- a/third_party/blink/web_tests/external/wpt/wai-aria/role/listbox-roles.html
+++ b/third_party/blink/web_tests/external/wpt/wai-aria/role/listbox-roles.html
@@ -31,6 +31,12 @@
     </li>
 </ul>
 
+<!--
+  CORE-AAM requires that, for elements with roles not contained in the
+  required context, user agents must ignore the role token and return the
+  computed role as if the ignored role token had not been included.
+  See https://w3c.github.io/core-aam/#roleMappingComputedRole
+-->
 <nav role="option" data-testname="orphaned option outside the context of listbox" data-expectedrole="navigation"
      class="ex">x
 </nav>
diff --git a/third_party/blink/web_tests/external/wpt/wai-aria/role/menu-roles.html b/third_party/blink/web_tests/external/wpt/wai-aria/role/menu-roles.html
index 4af4d8d..15b8ddc2 100644
--- a/third_party/blink/web_tests/external/wpt/wai-aria/role/menu-roles.html
+++ b/third_party/blink/web_tests/external/wpt/wai-aria/role/menu-roles.html
@@ -55,6 +55,12 @@
     </div>
 </div>
 
+<!--
+  CORE-AAM requires that, for elements with roles not contained in the
+  required context, user agents must ignore the role token and return the
+  computed role as if the ignored role token had not been included.
+  See https://w3c.github.io/core-aam/#roleMappingComputedRole
+-->
 <nav role="menuitem" data-testname="orphaned menuitem outside the context of menu/menubar" data-expectedrole="navigation"
      class="ex">x
 </nav>
diff --git a/third_party/blink/web_tests/external/wpt/wai-aria/role/tab-roles.html b/third_party/blink/web_tests/external/wpt/wai-aria/role/tab-roles.html
index e3d1474..fafbbf9 100644
--- a/third_party/blink/web_tests/external/wpt/wai-aria/role/tab-roles.html
+++ b/third_party/blink/web_tests/external/wpt/wai-aria/role/tab-roles.html
@@ -82,7 +82,12 @@
     <div role="tabpanel" data-testname="role is tabpanel as sibling to ul with child role none li elements" data-expectedrole="tabpanel" class="ex">Tab one's stuff</div>
     <div role="tabpanel" data-testname="role is tabpanel as sibling to ul with child role none li elements (duplicate)" data-expectedrole="tabpanel" class="ex">Tab two's stuff</div>
 
-<!--orphan tab semantics -->
+<!--
+  CORE-AAM requires that, for elements with roles not contained in the
+  required context, user agents must ignore the role token and return the
+  computed role as if the ignored role token had not been included.
+  See https://w3c.github.io/core-aam/#roleMappingComputedRole
+-->
 <button role="tab" data-testname="orphan button with tab role" data-expectedrole="button" class="ex">x</button>
 <span role="tab" data-testname="orphan span with tab role" class="ex-generic">x</span>
 
diff --git a/third_party/blink/web_tests/external/wpt/wai-aria/role/tree-roles.html b/third_party/blink/web_tests/external/wpt/wai-aria/role/tree-roles.html
index eb108404..5d179fb 100644
--- a/third_party/blink/web_tests/external/wpt/wai-aria/role/tree-roles.html
+++ b/third_party/blink/web_tests/external/wpt/wai-aria/role/tree-roles.html
@@ -102,6 +102,12 @@
   </tbody>
 </table>
 
+<!--
+  CORE-AAM requires that, for elements with roles not contained in the
+  required context, user agents must ignore the role token and return the
+  computed role as if the ignored role token had not been included.
+  See https://w3c.github.io/core-aam/#roleMappingComputedRole
+-->
 <nav role="treeitem" data-testname="orphaned treeitem outside the context of tree" data-expectedrole="navigation" class="ex">x</nav>
 <button role="treeitem" data-testname="orphaned button with treeitem role outside tree context" data-expectedrole="button" class="ex">x</button>
 
diff --git a/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt b/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt
index f39d594..9b2cbde 100644
--- a/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt
@@ -20,7 +20,6 @@
 frame "0" - didFinishLoadForFrame
 frame "0" - BeginNavigation request to 'http://127.0.0.1:8000/loading/resources/redirect-methods-result.php', http method POST
 frame "0" - DidStartNavigation
-frame "0" - DidStartNavigation
 frame "0" - ReadyToCommitNavigation
 frame "0" - didCommitLoadForFrame
 frame "0" - didFinishDocumentLoadForFrame
@@ -42,7 +41,6 @@
 frame "1" - didFinishLoadForFrame
 frame "1" - BeginNavigation request to 'http://127.0.0.1:8000/loading/resources/redirect-methods-result.php', http method POST
 frame "1" - DidStartNavigation
-frame "1" - DidStartNavigation
 frame "1" - ReadyToCommitNavigation
 frame "1" - didCommitLoadForFrame
 frame "1" - didFinishDocumentLoadForFrame
@@ -64,7 +62,6 @@
 frame "2" - didFinishLoadForFrame
 frame "2" - BeginNavigation request to 'http://127.0.0.1:8000/loading/resources/redirect-methods-result.php', http method POST
 frame "2" - DidStartNavigation
-frame "2" - DidStartNavigation
 frame "2" - ReadyToCommitNavigation
 frame "2" - didCommitLoadForFrame
 frame "2" - didFinishDocumentLoadForFrame
@@ -86,7 +83,6 @@
 frame "3" - didFinishLoadForFrame
 frame "3" - BeginNavigation request to 'http://127.0.0.1:8000/loading/resources/redirect-methods-result.php', http method POST
 frame "3" - DidStartNavigation
-frame "3" - DidStartNavigation
 frame "3" - ReadyToCommitNavigation
 frame "3" - didCommitLoadForFrame
 frame "3" - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browser/remove_user_context/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browser/remove_user_context/invalid-expected.txt
new file mode 100644
index 0000000..6455502
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browser/remove_user_context/invalid-expected.txt
@@ -0,0 +1,12 @@
+This is a wdspec test.
+[FAIL] test_params_user_context_invalid_type[None]
+  webdriver.bidi.error.UnknownErrorException: unknown error (Invalid parameters)
+[FAIL] test_params_user_context_invalid_type[False]
+  webdriver.bidi.error.UnknownErrorException: unknown error (Invalid parameters)
+[FAIL] test_params_user_context_invalid_type[42]
+  webdriver.bidi.error.UnknownErrorException: unknown error (Invalid parameters)
+[FAIL] test_params_user_context_invalid_type[value3]
+  webdriver.bidi.error.UnknownErrorException: unknown error (Invalid parameters)
+[FAIL] test_params_user_context_invalid_type[value4]
+  webdriver.bidi.error.UnknownErrorException: unknown error (Invalid parameters)
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/create/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/create/invalid-expected.txt
new file mode 100644
index 0000000..64b1bfb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/create/invalid-expected.txt
@@ -0,0 +1,4 @@
+This is a wdspec test.
+[FAIL] test_params_user_context_invalid_value_with_ref_context
+  Failed: DID NOT RAISE <class 'webdriver.bidi.error.NoSuchUserContextException'>
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/locator-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/locator-expected.txt
index 61fdbc85..c52f0705 100644
--- a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/locator-expected.txt
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/locator-expected.txt
@@ -1,20 +1,24 @@
 This is a wdspec test.
+[FAIL] test_find_by_locator[css-div]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
+[FAIL] test_find_by_locator[xpath-//div]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_locator[innerText-foobarBARbaz]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (locateNodes does not support innerText locator type.)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_inner_text[ignore_case_true_full_match_no_max_depth]
-  webdriver.bidi.error.InvalidArgumentException: invalid argument (Invalid input in "locator".)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_inner_text[ignore_case_false_full_match_no_max_depth]
-  webdriver.bidi.error.InvalidArgumentException: invalid argument (Invalid input in "locator".)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_inner_text[ignore_case_true_partial_match_no_max_depth]
-  webdriver.bidi.error.InvalidArgumentException: invalid argument (Invalid input in "locator".)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_inner_text[ignore_case_false_partial_match_no_max_depth]
-  webdriver.bidi.error.InvalidArgumentException: invalid argument (Invalid input in "locator".)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_inner_text[ignore_case_true_full_match_max_depth_zero]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (locateNodes does not support innerText locator type.)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_inner_text[ignore_case_false_full_match_max_depth_zero]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (locateNodes does not support innerText locator type.)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_inner_text[ignore_case_true_partial_match_max_depth_zero]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (locateNodes does not support innerText locator type.)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_inner_text[ignore_case_false_partial_match_max_depth_zero]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (locateNodes does not support innerText locator type.)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/max_node_count-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/max_node_count-expected.txt
index 40470bf..f752a0b 100644
--- a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/max_node_count-expected.txt
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/max_node_count-expected.txt
@@ -1,16 +1,16 @@
 This is a wdspec test.
 [FAIL] test_find_by_locator_limit_return_count[css_single]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (maxNodeCount is not supported)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_locator_limit_return_count[xpath_single]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (maxNodeCount is not supported)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_locator_limit_return_count[inner_text_single]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (maxNodeCount is not supported)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_locator_limit_return_count[css_multiple]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (maxNodeCount is not supported)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_locator_limit_return_count[xpath_multiple]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (maxNodeCount is not supported)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_find_by_locator_limit_return_count[inner_text_multiple]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (maxNodeCount is not supported)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_several_context_nodes
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (maxNodeCount is not supported)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/serialization_options-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/serialization_options-expected.txt
index e11bcc3f..b59e028 100644
--- a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/serialization_options-expected.txt
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/serialization_options-expected.txt
@@ -1,6 +1,6 @@
 This is a wdspec test.
 [FAIL] test_locate_nodes_serialization_options[open]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (serializationOptions is not supported)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 [FAIL] test_locate_nodes_serialization_options[closed]
-  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (serializationOptions is not supported)
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (Command 'browsingContext.locateNodes' not yet implemented.)
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/input/set_files/files-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/input/set_files/files-expected.txt
index 788af26d..21d6b5b 100644
--- a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/input/set_files/files-expected.txt
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/input/set_files/files-expected.txt
@@ -1,4 +1,6 @@
 This is a wdspec test.
+[FAIL] test_set_files_something_then_empty
+  AssertionError: assert [{'type': 'input', 'files': ['noop.txt']}, {'type': 'change', 'files': ['noop.txt']}, {'type': 'cancel', 'files': ['noop.txt']}] == [{'files': ['noop.txt'], 'type': 'input'}, {'files': ['noop.txt'], 'type': 'change'}, {'files': [], 'type': 'input'}, {'files': [], 'type': 'change'}]
 [FAIL] test_set_files_twice_same
   AssertionError: assert [{'type': 'input', 'files': ['noop.txt']}, {'type': 'change', 'files': ['noop.txt']}, {'type': 'input', 'files': ['noop.txt']}, {'type': 'change', 'files': ['noop.txt']}] == [{'files': ['noop.txt'], 'type': 'input'}, {'files': ['noop.txt'], 'type': 'change'}, {'files': ['noop.txt'], 'type': 'cancel'}]
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/add_intercept/contexts-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/add_intercept/contexts-expected.txt
new file mode 100644
index 0000000..b661e231
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/add_intercept/contexts-expected.txt
@@ -0,0 +1,10 @@
+This is a wdspec test.
+[FAIL] test_other_context[beforeRequestSent]
+  webdriver.bidi.modules.script.ScriptEvaluateResultException: AbortError: signal is aborted without reason
+[FAIL] test_other_context[responseStarted]
+  webdriver.bidi.modules.script.ScriptEvaluateResultException: AbortError: signal is aborted without reason
+[FAIL] test_other_context_with_event_subscription
+  AssertionError
+[FAIL] test_two_contexts_global_intercept
+  AssertionError
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/add_intercept/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/add_intercept/invalid-expected.txt
new file mode 100644
index 0000000..c6c9d64
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/add_intercept/invalid-expected.txt
@@ -0,0 +1,8 @@
+This is a wdspec test.
+[FAIL] test_params_contexts_invalid_value[value0]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_contexts_invalid_value[value1]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_contexts_context_non_top_level
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_response/credentials-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_response/credentials-expected.txt
new file mode 100644
index 0000000..7949799
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_response/credentials-expected.txt
@@ -0,0 +1,10 @@
+This is a wdspec test.
+[FAIL] test_wrong_credentials[fetch]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_wrong_credentials[navigate]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_correct_credentials[fetch]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_correct_credentials[navigate]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_response/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_response/invalid-expected.txt
new file mode 100644
index 0000000..9f04ea3f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_response/invalid-expected.txt
@@ -0,0 +1,278 @@
+This is a wdspec test.
+[FAIL] test_params_cookies_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_invalid_type[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_invalid_type[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_invalid_value[empty object]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_invalid_value[missing value]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_invalid_value[missing name]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_name_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_name_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_name_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_name_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_name_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_invalid_type[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_invalid_value[value0]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_invalid_value[value1]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_invalid_value[value2]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_type_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_type_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_type_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_type_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_type_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_type_invalid_value[]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_type_invalid_value[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[False-domain]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[False-expiry]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[False-path]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[False-sameSite]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[42-domain]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[42-expiry]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[42-path]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[42-sameSite]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[value2-domain]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[value2-expiry]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[value2-path]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[value2-sameSite]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[value3-domain]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[value3-expiry]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[value3-path]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_string_properties_invalid_type[value3-sameSite]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_same_site_invalid_value[]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_same_site_invalid_value[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_bool_properties_invalid_type[42-httpOnly]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_bool_properties_invalid_type[42-secure]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_bool_properties_invalid_type[foo-httpOnly]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_bool_properties_invalid_type[foo-secure]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_bool_properties_invalid_type[value2-httpOnly]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_bool_properties_invalid_type[value2-secure]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_bool_properties_invalid_type[value3-httpOnly]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_bool_properties_invalid_type[value3-secure]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_max_age_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_max_age_invalid_type[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_max_age_invalid_type[value2]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_max_age_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_max_age_invalid_value[4.3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_value_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_value_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_value_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_value_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_cookies_cookie_value_value_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_invalid_type[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_invalid_value[missing username]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_invalid_value[missing password]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_invalid_value[missing username and password]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_invalid_value[missing type]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_type_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_type_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_type_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_type_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_type_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_type_invalid_value[]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_type_invalid_value[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_username_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_username_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_username_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_username_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_username_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_password_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_password_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_password_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_password_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_credentials_password_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_invalid_type[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_invalid_type[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_name_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_name_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_name_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_name_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_name_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_invalid_type[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_invalid_value[value0]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_invalid_value[value1]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_invalid_value[value2]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_type_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_type_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_type_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_type_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_type_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_type_invalid_value[]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_type_invalid_value[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_value_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_value_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_value_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_value_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_headers_header_value_value_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_request_invalid_phase
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_reason_phrase_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_reason_phrase_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_reason_phrase_invalid_type[value2]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_reason_phrase_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_status_code_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_status_code_invalid_type[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_status_code_invalid_type[value2]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_status_code_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_status_code_invalid_value[-1]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_status_code_invalid_value[4.3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_response/request-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_response/request-expected.txt
new file mode 100644
index 0000000..97f31300
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_response/request-expected.txt
@@ -0,0 +1,10 @@
+This is a wdspec test.
+[FAIL] test_continue_auth_required[fetch]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_continue_auth_required[navigate]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_continue_response_started[fetch]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_continue_response_started[navigate]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid-expected.txt
new file mode 100644
index 0000000..4ecb27c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid-expected.txt
@@ -0,0 +1,68 @@
+This is a wdspec test.
+[FAIL] test_params_request_invalid_phase[beforeRequestSent]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_request_invalid_phase[responseStarted]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_request_no_such_request_after_cancel
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_request_no_such_request_after_provideCredentials
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_invalid_value[]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_invalid_value[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_invalid_credentials[missing username]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_invalid_credentials[missing password]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_invalid_credentials[missing username and password]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_invalid_credentials[missing type]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_invalid_credentials[missing credentials]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_type_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_type_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_type_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_type_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_type_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_type_invalid_value[]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_type_invalid_value[foo]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_username_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_username_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_username_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_username_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_username_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_password_invalid_type[None]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_password_invalid_type[False]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_password_invalid_type[42]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_password_invalid_type[value3]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_params_action_provideCredentials_credentials_password_invalid_type[value4]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/provide_response/request-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/provide_response/request-expected.txt
index 1c132240..323683ad 100644
--- a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/provide_response/request-expected.txt
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/provide_response/request-expected.txt
@@ -1,10 +1,14 @@
 This is a wdspec test.
 [FAIL] test_provide_response_auth_required[fetch]
-  webdriver.bidi.error.UnknownErrorException: unknown error (Invalid http status code or phrase)
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
 [FAIL] test_provide_response_auth_required[navigate]
-  webdriver.bidi.error.UnknownErrorException: unknown error (Invalid http status code or phrase)
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
 [FAIL] test_provide_response_phase[fetch-beforeRequestSent]
-  webdriver.bidi.error.UnknownErrorException: unknown error (Invalid http status code or phrase)
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_provide_response_phase[fetch-responseStarted]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
 [FAIL] test_provide_response_phase[navigate-beforeRequestSent]
-  webdriver.bidi.error.UnknownErrorException: unknown error (Invalid http status code or phrase)
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
+[FAIL] test_provide_response_phase[navigate-responseStarted]
+  TypeError: add_intercept() got an unexpected keyword argument 'contexts'
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/script/realm_created/realm_created-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/script/realm_created/realm_created-expected.txt
new file mode 100644
index 0000000..8dd38c9
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/script/realm_created/realm_created-expected.txt
@@ -0,0 +1,6 @@
+This is a wdspec test.
+[FAIL] test_shared_worker
+  asyncio.exceptions.TimeoutError
+[FAIL] test_service_worker
+  asyncio.exceptions.TimeoutError
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/session/subscribe/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/session/subscribe/invalid-expected.txt
new file mode 100644
index 0000000..2756cb0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/session/subscribe/invalid-expected.txt
@@ -0,0 +1,6 @@
+This is a wdspec test.
+[FAIL] test_params_events_empty
+  Failed: DID NOT RAISE <class 'webdriver.bidi.error.InvalidArgumentException'>
+[FAIL] test_params_contexts_empty
+  Failed: DID NOT RAISE <class 'webdriver.bidi.error.InvalidArgumentException'>
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid-expected.txt
new file mode 100644
index 0000000..713077808bd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/session/unsubscribe/invalid-expected.txt
@@ -0,0 +1,8 @@
+This is a wdspec test.
+[FAIL] test_params_events_invalid_type[None]
+  Failed: DID NOT RAISE <class 'webdriver.bidi.error.InvalidArgumentException'>
+[FAIL] test_params_events_empty
+  Failed: DID NOT RAISE <class 'webdriver.bidi.error.InvalidArgumentException'>
+[FAIL] test_params_contexts_empty
+  Failed: DID NOT RAISE <class 'webdriver.bidi.error.InvalidArgumentException'>
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/delete_cookies/filter-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/delete_cookies/filter-expected.txt
new file mode 100644
index 0000000..3b850800
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/delete_cookies/filter-expected.txt
@@ -0,0 +1,40 @@
+This is a wdspec test.
+[FAIL] test_filter[filter0]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter[filter1]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter[filter2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_domain
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_expiry[1-2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_expiry[1-None]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_name
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_same_site[none-strict]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_same_site[lax-none]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_same_site[strict-none]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_same_site[lax-strict]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_same_site[strict-lax]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_secure[True-False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_secure[False-True]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_path[/webdriver/tests/support-/]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_path[/-None]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_path[/webdriver-/webdriver/tests]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_http_only[True-False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_filter_http_only[False-True]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/delete_cookies/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/delete_cookies/invalid-expected.txt
new file mode 100644
index 0000000..783e8615
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/delete_cookies/invalid-expected.txt
@@ -0,0 +1,176 @@
+This is a wdspec test.
+[FAIL] test_params_filter_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_invalid_type[foo]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_domain_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_domain_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_domain_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_domain_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_expiry_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_expiry_invalid_type[foo]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_expiry_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_expiry_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_expiry_invalid_type[-1]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_expiry_invalid_type[0.5]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_http_only_invalid_type[foo]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_http_only_invalid_type[value1]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_http_only_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_http_only_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_name_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_name_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_name_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_name_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_path_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_path_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_path_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_path_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_same_site_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_same_site_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_same_site_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_same_site_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_same_site_invalid_value[]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_same_site_invalid_value[INVALID_SAME_SITE_STATE]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_secure_invalid_type[foo]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_secure_invalid_type[value1]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_secure_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_secure_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_size_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_size_invalid_type[foo]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_size_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_size_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_size_invalid_type[-1]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_size_invalid_type[0.5]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_invalid_type[foo]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_type_invalid_type[None]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_type_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_type_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_type_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_type_invalid_type[value4]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_type_invalid_value
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_base64_type_invalid_type[None]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_base64_type_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_base64_type_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_base64_type_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_base64_type_invalid_type[value4]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_string_type_invalid_type[None]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_string_type_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_string_type_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_string_type_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_filter_value_string_type_invalid_type[value4]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_invalid_type[foo]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_type_invalid_type[None]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_type_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_type_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_type_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_type_invalid_type[value4]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_type_invalid_value
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_context_invalid_type[None]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_context_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_context_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_context_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_context_invalid_type[value4]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_partition_invalid_context
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_source_origin_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_source_origin_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_source_origin_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_source_origin_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_user_context_invalid_type[False]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_user_context_invalid_type[42]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_user_context_invalid_type[value2]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_user_context_invalid_type[value3]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_params_partition_user_context_invalid_value
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/delete_cookies/partition-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/delete_cookies/partition-expected.txt
new file mode 100644
index 0000000..a3577af
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/delete_cookies/partition-expected.txt
@@ -0,0 +1,22 @@
+This is a wdspec test.
+[FAIL] test_default_partition[with document cookie]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_default_partition[with set cookie]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_partition_context[with document cookie]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_partition_context[with set cookie]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_partition_context_iframe[same_origin]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_partition_context_iframe[cross_origin]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_partition_source_origin[http]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_partition_source_origin[https]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_partition_user_context[with document cookie]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+[FAIL] test_partition_user_context[with set cookie]
+  webdriver.bidi.error.UnsupportedOperationException: unsupported operation (storage.deleteCookies is not supported yet)
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/filter-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/filter-expected.txt
new file mode 100644
index 0000000..c4c7809
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/filter-expected.txt
@@ -0,0 +1,4 @@
+This is a wdspec test.
+[FAIL] test_filter[filter2]
+  assert 0 == 2
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/invalid-expected.txt
new file mode 100644
index 0000000..a173c835
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/invalid-expected.txt
@@ -0,0 +1,4 @@
+This is a wdspec test.
+[FAIL] test_params_partition_user_context_invalid_value
+  webdriver.bidi.error.UnknownErrorException: unknown error (Failed to find browser context for id foo)
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/partition-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/partition-expected.txt
index 2729eb1..bb08e1d 100644
--- a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/partition-expected.txt
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/get_cookies/partition-expected.txt
@@ -1,4 +1,12 @@
 This is a wdspec test.
+[FAIL] test_partition_context
+  AssertionError: assert {} == {'userContext': 'default'}
 [FAIL] test_partition_context_with_different_domain
   AssertionError: assert [{'domain': 'not-web-platform.test', 'goog:priority': 'Medium', 'goog:sameParty': False, 'goog:session': True, 'goog:sourcePort': 443, 'goog:sourceScheme': 'Secure', 'httpOnly': False, 'name': 'foo', 'path': '/', 'sameSite': 'none', 'secure': True, 'size': 6, 'value': {'type': 'string', 'value': 'bar'}}] == [{'domain': 'not-web-platform.test', 'httpOnly': False, 'name': 'foo', 'path': '/', 'sameSite': 'none', 'secure': True, 'size': 6, 'value': {'type': 'string', 'value': 'bar'}}]
+[FAIL] test_partition_context_iframe[same_origin]
+  AssertionError: Key set should be present: {'userContext'}
+[FAIL] test_partition_context_iframe[cross_origin]
+  AssertionError: Key set should be present: {'userContext'}
+[FAIL] test_partition_default_user_context
+  AssertionError: Key set should be present: {'userContext'}
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/set_cookie/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/set_cookie/invalid-expected.txt
new file mode 100644
index 0000000..cb99bcb0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/set_cookie/invalid-expected.txt
@@ -0,0 +1,4 @@
+This is a wdspec test.
+[FAIL] test_partition_storage_key_user_context_invalid_value
+  webdriver.bidi.error.UnableToSetCookieException: unable to set cookie ([object Object])
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/set_cookie/partition-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/set_cookie/partition-expected.txt
new file mode 100644
index 0000000..e3ebc796
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/storage/set_cookie/partition-expected.txt
@@ -0,0 +1,6 @@
+This is a wdspec test.
+[FAIL] test_partition_context
+  AssertionError: Key set should be present: {'userContext'}
+[FAIL] test_partition_context_frame
+  AssertionError: Key set should be present: {'userContext'}
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/win11-arm64/media/controls/video-overlay-cast-dark-rendering-expected.png b/third_party/blink/web_tests/platform/win11-arm64/media/controls/video-overlay-cast-dark-rendering-expected.png
index 2dccf2e7..bf1040cb 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/media/controls/video-overlay-cast-dark-rendering-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/media/controls/video-overlay-cast-dark-rendering-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index 217a610f..746f921 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -1115,10 +1115,10 @@
 html element template
     property content
     property parseparts
-    property serializable
     property shadowRootClonable
     property shadowRootDelegatesFocus
     property shadowRootMode
+    property shadowRootSerializable
 html element textarea
     property autocomplete
     property checkValidity
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 728e152..ce08ee63 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
@@ -1235,6 +1235,12 @@
 interface ChannelSplitterNode : AudioNode
     attribute @@toStringTag
     method constructor
+interface ChapterInformation
+    attribute @@toStringTag
+    getter artwork
+    getter startTime
+    getter title
+    method constructor
 interface CharacterBoundsUpdateEvent : Event
     attribute @@toStringTag
     getter rangeEnd
@@ -4974,16 +4980,16 @@
     attribute @@toStringTag
     getter content
     getter parseparts
-    getter serializable
     getter shadowRootClonable
     getter shadowRootDelegatesFocus
     getter shadowRootMode
+    getter shadowRootSerializable
     method constructor
     setter parseparts
-    setter serializable
     setter shadowRootClonable
     setter shadowRootDelegatesFocus
     setter shadowRootMode
+    setter shadowRootSerializable
 interface HTMLTextAreaElement : HTMLElement
     attribute @@toStringTag
     getter autocomplete
@@ -6113,7 +6119,6 @@
     setter album
     setter artist
     setter artwork
-    setter chapterInfo
     setter title
 interface MediaQueryList : EventTarget
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/callbacks-in-microtasks.html b/third_party/blink/web_tests/wpt_internal/task-tracking/callbacks-in-microtasks.html
index 51cb8412..56dc7c1 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/callbacks-in-microtasks.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/callbacks-in-microtasks.html
@@ -5,18 +5,19 @@
 <title>Verify that tasks nested in microtasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
 
 promise_test(t => {
-  const scriptId = scheduler.taskId;
+  const scriptId = initializeTaskId();
 
   return scheduler.postTask(async _ => {
     // We should be in a new task.
     const postTaskId = scheduler.taskId;
-    assert_not_equals(scriptId, postTaskId,
-      "postTaskId should not be equal to the script task ID.");
+    assert_equals(scriptId, postTaskId,
+      "postTaskId should be equal to the script task ID.");
 
     let eventFired = false;
     // Set things up so the abort event listener is an ancestor of this task.
@@ -25,7 +26,6 @@
       assert_equals(scheduler.taskId, postTaskId,
         "aborted task ID is the same as the postTask ID, as it is fired" +
         " synchronously.");
-      assert_equals(scheduler.isAncestor(postTaskId), "ancestor");
       eventFired = true;
     });
 
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/messageport-postmessage-same-origin-iframe.html b/third_party/blink/web_tests/wpt_internal/task-tracking/messageport-postmessage-same-origin-iframe.html
index b9b5096..052ea78 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/messageport-postmessage-same-origin-iframe.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/messageport-postmessage-same-origin-iframe.html
@@ -5,11 +5,12 @@
 <title>Verify that postmessage tasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
 promise_test(t => {
-  const scriptId = scheduler.taskId;
+  const scriptId = initializeTaskId();
   const channel = new MessageChannel();
 
   const frame = document.createElement("iframe");
@@ -24,7 +25,7 @@
       try {
         // This is not an ancestor because task attribution doesn't get passed
         // along when the ports get disentangled.
-        assert_equals(scheduler.isAncestor(e.data), "not ancestor");
+        assert_not_equals(scheduler.taskId, e.data);
         resolve();
       } catch(e) {
         reject(e);
@@ -35,4 +36,4 @@
 
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/requestAnimationFrame.html b/third_party/blink/web_tests/wpt_internal/task-tracking/requestAnimationFrame.html
index 43a734e..c2600bf7 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/requestAnimationFrame.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/requestAnimationFrame.html
@@ -5,16 +5,17 @@
 <title>Verify that requestAnimationFrame tasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await new Promise(resolve => setTimeout(resolve, 0));
     requestAnimationFrame(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject("Not an ancestor");
@@ -25,14 +26,14 @@
 
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await new Promise(resolve => setTimeout(resolve, 0));
     requestAnimationFrame(() => {
       const intermediateId = scheduler.taskId;
       requestAnimationFrame(() => {
         try {
-          assert_equals(scheduler.isAncestor(initialId), "ancestor");
-          assert_equals(scheduler.isAncestor(intermediateId), "ancestor");
+          assert_equals(scheduler.taskId, initialId);
+          assert_equals(scheduler.taskId, intermediateId);
           resolve();
         } catch {
           reject("Not an ancestor");
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/requestIdleCallback.html b/third_party/blink/web_tests/wpt_internal/task-tracking/requestIdleCallback.html
index e9f097d..5303615 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/requestIdleCallback.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/requestIdleCallback.html
@@ -5,16 +5,17 @@
 <title>Verify that requestIdleCallback tasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await new Promise(resolve => setTimeout(resolve, 0));
     requestIdleCallback(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject("Not an ancestor");
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/resources/mixed-promise-helper.js b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/mixed-promise-helper.js
index 8c0ac41..d41b4ae 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/resources/mixed-promise-helper.js
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/mixed-promise-helper.js
@@ -7,8 +7,9 @@
 
 const prepare_and_run_test = test => {
   // The task before the test's promise starts running, independent from the
-  // image load task.
-  parent_task = scheduler.taskId;
+  // image load task. Initialize this here so the propagated data is different
+  // from the sibling task.
+  parent_task = initializeTaskId();
 
   // Run the test in its own task.
   setTimeout(test, 5);
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/resources/script.js b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/script.js
index 75486b7..8bbc198 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/resources/script.js
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/script.js
@@ -1 +1 @@
-  window.results.push(scheduler.isAncestor(window.initialId));
+  window.results.push(scheduler.taskId == window.initialId);
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/resources/task-ids.js b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/task-ids.js
new file mode 100644
index 0000000..6d73d8c
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/task-ids.js
@@ -0,0 +1,9 @@
+let nextId = 10000;
+
+// Helper function to set a an ID for the current task, which will be propagated
+// to descendant tasks and microtasks.
+function initializeTaskId() {
+  const id = nextId++;
+  scheduler.taskId = id;
+  return id;
+}
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-doc-write.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-doc-write.html
index f9bc04f..6acaa753 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-doc-write.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-doc-write.html
@@ -5,18 +5,19 @@
 <title>Verify that tasks are tracked across scripts evaluated due to document.write calls.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
-window.initialId = scheduler.taskId;
+window.initialId = initializeTaskId();
 window.results = [];
 const expected = [
-  { value: "ancestor", name: "Inline script task" },
-  { value: "ancestor", name: "Inline module script task" },
-  { value: "ancestor", name: "External blocking script task" },
+  { value: true, name: "Inline script task" },
+  { value: true, name: "Inline module script task" },
+  { value: true, name: "External blocking script task" },
 ];
 
-const scriptToEval = `window.results.push(scheduler.isAncestor(window.initialId));`;
+const scriptToEval = `window.results.push(scheduler.taskId == window.initialId);`;
 document.write("<script>" + scriptToEval + "<\/script>");
 document.write("<script type=module>" + scriptToEval + "<\/script>");
 document.write("<script src='resources/script.js'><\/script>");
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-messageport-postmessage-cross-origin-iframe.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-messageport-postmessage-cross-origin-iframe.html
index 28bf92d..07e9fbcd 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-messageport-postmessage-cross-origin-iframe.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-messageport-postmessage-cross-origin-iframe.html
@@ -6,11 +6,12 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/common/get-host-info.sub.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
 promise_test(t => {
-  const scriptId = scheduler.taskId;
+  const scriptId = initializeTaskId();
   const channel = new MessageChannel();
   const {REMOTE_ORIGIN} = get_host_info();
 
@@ -24,7 +25,7 @@
   return new Promise((resolve, reject) => {
     channel.port1.onmessage = e => {
       try {
-        assert_equals(scheduler.isAncestor(e.data), "not ancestor");
+        assert_not_equals(scheduler.taskId, e.data);
         resolve();
       } catch(e) {
         reject(e);
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-mixed-await-chain.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-mixed-await-chain.html
index f435313..13e48560 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-mixed-await-chain.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-mixed-await-chain.html
@@ -6,6 +6,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/mixed-promise-helper.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
@@ -16,19 +17,18 @@
     const response = await fetch("/resources/blank.html")
     await new Promise(r => {
       // A JS-based promise, resolved when the image loads.
-      assert_equals(scheduler.isAncestor(parent_task), "ancestor");
-      assert_equals(scheduler.isAncestor(sibling_task), "not ancestor");
+      assert_equals(scheduler.taskId, parent_task);
+      assert_not_equals(scheduler.taskId, sibling_task);
       assert_equals(scheduler.taskId, promise_task);
-      assert_not_equals(scheduler.taskId, parent_task);
       image_loaded = r;
       image_can_load();
     });
-    assert_equals(scheduler.isAncestor(parent_task), "ancestor");
-    assert_equals(scheduler.isAncestor(sibling_task), "not ancestor");
+    assert_equals(scheduler.taskId, parent_task);
+    assert_not_equals(scheduler.taskID, sibling_task);
     assert_equals(scheduler.taskId, promise_task);
     const body = await response.text();
-    assert_equals(scheduler.isAncestor(parent_task), "ancestor");
-    assert_equals(scheduler.isAncestor(sibling_task), "not ancestor");
+    assert_equals(scheduler.taskId, parent_task);
+    assert_not_equals(scheduler.taskId, sibling_task);
     assert_equals(scheduler.taskId, promise_task);
   }, "All microtasks in the await chain after fetching a resource  are " +
      "descendants of the initiating task.");
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-mixed-promise-chain.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-mixed-promise-chain.html
index 887645d..2c26821a 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-mixed-promise-chain.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-mixed-promise-chain.html
@@ -6,6 +6,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/mixed-promise-helper.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
@@ -16,32 +17,31 @@
       fetch("/resources/blank.html")
       .then(response => new Promise(r => {
         // A JS-based promise, resolved when the image loads.
-        assert_equals(scheduler.isAncestor(parent_task), "ancestor");
-        assert_equals(scheduler.isAncestor(sibling_task), "not ancestor");
+        assert_equals(scheduler.taskId, parent_task);
+        assert_not_equals(scheduler.taskId, sibling_task);
         assert_equals(scheduler.taskId, promise_task);
-        assert_not_equals(scheduler.taskId, parent_task);
         image_loaded = r;
         fetched_response = response;
         image_can_load();
       }))
       .then(response => {
-        assert_equals(scheduler.isAncestor(parent_task), "ancestor");
-        assert_equals(scheduler.isAncestor(sibling_task), "not ancestor");
+        assert_equals(scheduler.taskId, parent_task);
+        assert_not_equals(scheduler.taskId, sibling_task);
         assert_equals(scheduler.taskId, promise_task);
         return response.text();
       })
       .then(body => {
         return new Promise(interimResolve => {
-          assert_equals(scheduler.isAncestor(parent_task), "ancestor");
-          assert_equals(scheduler.isAncestor(sibling_task), "not ancestor");
+          assert_equals(scheduler.taskId, parent_task);
+          assert_not_equals(scheduler.taskId, sibling_task);
           assert_equals(scheduler.taskId, promise_task);
           interimResolve();
         });
       })
       .then(() => {
         try {
-          assert_equals(scheduler.isAncestor(parent_task), "ancestor");
-          assert_equals(scheduler.isAncestor(sibling_task), "not ancestor");
+          assert_equals(scheduler.taskId, parent_task)
+          assert_not_equals(scheduler.taskId, sibling_task);
           assert_equals(scheduler.taskId, promise_task);
           resolve();
         } catch {
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-postmessage.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-postmessage.html
index ac3a2ec..ab411aa 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-postmessage.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-postmessage.html
@@ -5,6 +5,7 @@
 <title>Verify that postmessage tasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
@@ -12,7 +13,7 @@
   return new Promise((resolve, reject) => {
     channel.port1.onmessage = () => {
       try {
-        assert_equals(scheduler.isAncestor(scriptId), "ancestor");
+        assert_equals(scheduler.taskId, scriptId);
         resolve();
       } catch(e) {
         reject(e);
@@ -22,7 +23,7 @@
 }
 
 promise_test(t => {
-  const scriptId = scheduler.taskId;
+  const scriptId = initializeTaskId();
   const channel1 = new MessageChannel();
   const channel2 = new MessageChannel();
 
@@ -35,4 +36,4 @@
 
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html
index c7ab6bc..94561d48 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html
@@ -5,45 +5,35 @@
 <title>Verify that setTimeout tasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
 promise_test(async t => {
-  const scriptId = scheduler.taskId;
+  // `initialId` should be propagated through the whole chain.
+  const initialId = initializeTaskId();
 
   await scheduler.postTask(async () => {
-    // New task; ID should have changed.
-    const taskId1 = scheduler.taskId;
-    let taskId2;
-    assert_not_equals(scriptId, taskId1);
-    assert_equals(scheduler.isAncestor(scriptId), "ancestor");
+    assert_equals(scheduler.taskId, initialId);
 
     await scheduler.postTask(() => {
-      // New task; IDs should have changed again.
-      taskId2 = scheduler.taskId;
-      assert_not_equals(taskId1, taskId2);
-      assert_equals(scheduler.isAncestor(scriptId), "ancestor");
-      assert_equals(scheduler.isAncestor(taskId1), "ancestor");
+      assert_equals(scheduler.taskId, initialId);
     });
 
-    assert_equals(scheduler.isAncestor(taskId1), "ancestor");
-    assert_equals(scheduler.isAncestor(taskId2), "not ancestor");
+    assert_equals(scheduler.taskId, initialId);
   });
 }, "postTask chain.");
 
 promise_test(async () => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await scheduler.postTask(async () => {
-      const currentTaskId = scheduler.taskId;
-      assert_equals(scheduler.isAncestor(initialId), "ancestor",
+      assert_equals(scheduler.taskId, initialId,
                     "initialId is an ancestor of the currentTaskId");
       await fetch("/resources/blank.html");
-      assert_equals(scheduler.isAncestor(initialId), "ancestor",
+      assert_equals(scheduler.taskId, initialId,
                     "initialId is ancestor of fetch continuation.");
-      assert_equals(scheduler.isAncestor(currentTaskId), "ancestor",
-                    "currentTaskId is an ancestor of fetch continuation");
     });
 }, "Single postTask with multiple internal awaited tasks");
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html
index 01a8a89..c911045 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html
@@ -5,17 +5,18 @@
 <title>Verify that promise tasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
 
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await new Promise(r => setTimeout(r, 0));
     queueMicrotask(() => {
       try {
-          assert_equals(scheduler.isAncestor(initialId), "ancestor");
+          assert_equals(scheduler.taskId, initialId);
           resolve();
         } catch {
           reject("Queued microtask is not a descendant");
@@ -26,7 +27,7 @@
 
 promise_test(() => {
   return new Promise((resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     fetch("/resources/blank.html")
     .then(response => response.text())
     .then(body => {
@@ -36,7 +37,7 @@
     })
     .then(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject();
@@ -48,7 +49,7 @@
 
 promise_test(() => {
   return new Promise((resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     fetch("/resources/blank.html")
     .then(response => response.text())
     .then(body => {
@@ -61,7 +62,7 @@
     })
     .then(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject();
@@ -73,11 +74,11 @@
 
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     const response = await fetch("/resources/blank.html");
     const body = await response.text();
     try {
-      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      assert_equals(scheduler.taskId, initialId);
       resolve();
     } catch {
       reject();
@@ -88,7 +89,7 @@
 
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     let promiseId;
     await new Promise(r => setTimeout(r, 10));
     try {
@@ -101,12 +102,12 @@
       // get to you.
     }
     try {
-      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      assert_equals(scheduler.taskId, initialId);
     } catch {
       reject("InitialId is not an ancestor");
     }
     try {
-      assert_equals(scheduler.isAncestor(promiseId), "ancestor");
+      assert_equals(scheduler.taskId, promiseId);
       resolve();
     } catch {
       reject("PromiseId is not an ancestor");
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html
index 3d8df75..01d8221 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html
@@ -5,16 +5,17 @@
 <title>Verify that setInterval tasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await new Promise(resolve => setInterval(resolve, 0));
     queueMicrotask(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject("Not an ancestor");
@@ -25,11 +26,11 @@
 
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await new Promise(resolve => setInterval(resolve, 100));
     queueMicrotask(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject("Not an ancestor");
@@ -40,7 +41,7 @@
 
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     let counter = 0;
     await new Promise(resolve => setInterval(() => {
       ++counter;
@@ -50,7 +51,7 @@
     }, 100));
     queueMicrotask(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject("Not an ancestor");
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html
index 176bb77..6c866b4 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html
@@ -5,16 +5,17 @@
 <title>Verify that setTimeout tasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 </head>
 <body>
 <script>
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await new Promise(resolve => setTimeout(resolve, 0));
     queueMicrotask(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject("Not an ancestor");
@@ -25,14 +26,14 @@
 
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await new Promise(internal_resolve => {
       window.resolve = internal_resolve;
       setTimeout("resolve()", 100);
     });
     queueMicrotask(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject("Not an ancestor");
@@ -49,7 +50,7 @@
     window.reject = reject;
     setTimeout(`
      try {
-       assert_equals(scheduler.isAncestor(initialId), "ancestor");
+       assert_equals(scheduler.taskId, initialId);
        resolve();
      } catch {
        reject("Not an ancestor");
@@ -59,11 +60,11 @@
 
 promise_test(() => {
   return new Promise(async (resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     await new Promise(resolve => setTimeout(resolve, 100));
     queueMicrotask(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject("Not an ancestor");
@@ -73,10 +74,10 @@
 }, "A long setTimeout microtask is a descendant of the dispatching task");
 
 promise_test(async () => {
-  const initialId = scheduler.taskId;
+  const initialId = initializeTaskId();
   await new Promise((resolve, reject) => setTimeout(() => {
     try {
-      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      assert_equals(scheduler.taskId, initialId);
       resolve();
     } catch {
       reject("Initial task not identified as ancestor of first setTimeout");
@@ -85,7 +86,7 @@
   }, 10));
   await new Promise((resolve, reject) => setTimeout(() => {
     try {
-      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      assert_equals(scheduler.taskId, initialId);
       resolve();
     } catch {
       reject("Initial task not identified as ancestor of second setTimeout");
@@ -94,10 +95,10 @@
 }, "An async chain of setTimeouts task properly track their ancestors");
 
 promise_test(async () => {
-  const initialId = scheduler.taskId;
+  const initialId = initializeTaskId();
   return new Promise((resolve, reject) => {
     try {
-      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      assert_equals(scheduler.taskId, initialId);
       resolve();
     } catch {
       reject("The task is not its own ancestor");
@@ -108,31 +109,31 @@
 promise_test(async t => {
   let firstTaskParent;
   let firstTaskDescendent;
-  let secondTaskParent;
 
-  const initialId = scheduler.taskId;
+  const initialId = initializeTaskId();
 
   const first = new Promise((resolve, reject) => setTimeout(t.step_func(() => {
-    firstTaskParent = scheduler.taskId;
+    assert_equals(scheduler.taskId, initialId);
+    firstTaskParent = initializeTaskId();
     setTimeout(t.step_func(() => {
       firstTaskDescendent = scheduler.taskId;
-      assert_not_equals(firstTaskDescendent, firstTaskParent);
-      assert_equals(scheduler.isAncestor(firstTaskParent), "ancestor");
-      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      assert_equals(firstTaskDescendent, firstTaskParent);
+      assert_not_equals(firstTaskDescendent, initialId);
       resolve();
     }), 0);
   }), 15));
+
   const second = new Promise((resolve, reject) => setTimeout(t.step_func(() => {
-    secondTaskParent = scheduler.taskId;
+    assert_equals(scheduler.taskId, initialId);
+    const secondTaskParent = initializeTaskId();
     setTimeout(t.step_func(() => {
       try {
-        assert_not_equals(scheduler.taskId, secondTaskParent);
+        assert_equals(scheduler.taskId, secondTaskParent);
         assert_not_equals(scheduler.taskId, firstTaskDescendent);
-        assert_equals(scheduler.isAncestor(secondTaskParent), "ancestor");
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_not_equals(scheduler.taskId, initialId);
         resolve();
-      } catch {
-        reject("Initial task not identified as ancestor of second setTimeout");
+      } catch (e) {
+        reject(e);
       }
     }), 0);
   }), 30));
@@ -140,19 +141,18 @@
 }, "Two unrelated setTimeout tasks properly track their ancestors");
 
 promise_test(async t => {
-
-  window.initialId = scheduler.taskId;
+  window.initialId = initializeTaskId();
 
   const first = new Promise((resolve, reject) => setTimeout(t.step_func(() => {
-    window.firstTaskParent = scheduler.taskId;
+    assert_equals(scheduler.taskId, initialId);
+    window.firstTaskParent = initializeTaskId();
     window.resolve1 = resolve;
     window.reject1 = reject;
     setTimeout(`
       try {
         window.firstTaskDescendent = scheduler.taskId;
-        assert_not_equals(window.firstTaskDescendent, window.firstTaskParent);
-        assert_equals(scheduler.isAncestor(window.firstTaskParent), "ancestor");
-        assert_equals(scheduler.isAncestor(window.initialId), "ancestor");
+        assert_equals(window.firstTaskDescendent, firstTaskParent);
+        assert_not_equals(window.firstTaskDescendent, initialId);
         resolve1();
       } catch {
         reject1("Initial task not identified as ancestor of first setTimeout");
@@ -162,13 +162,13 @@
   const second = new Promise((resolve, reject) => setTimeout(t.step_func(() => {
     window.resolve2 = resolve;
     window.reject2 = reject;
-    secondTaskParent = scheduler.taskId;
+    assert_equals(scheduler.taskId, initialId);
+    window.secondTaskParent = initializeTaskId();
     setTimeout(`
       try {
-        assert_not_equals(scheduler.taskId, window.secondTaskParent);
+        assert_equals(scheduler.taskId, window.secondTaskParent);
         assert_not_equals(scheduler.taskId, window.firstTaskDescendent);
-        assert_equals(scheduler.isAncestor(window.secondTaskParent), "ancestor");
-        assert_equals(scheduler.isAncestor(window.initialId), "ancestor");
+        assert_not_equals(scheduler.taskId, window.initialId);
         resolve2();
       } catch {
         reject2("Initial task not identified as ancestor of second setTimeout");
@@ -179,29 +179,14 @@
 }, "Two unrelated setTimeout tasks (delivered as strings) properly track their ancestors");
 
 promise_test(async t => {
-
-  window.initialId = scheduler.taskId;
+  const initialId = initializeTaskId();
   let times = 0;
-  const test_times = 15;
-  // This value is 8 because the script execution itself is one parent task,
-  // and then test running is another. That leaves us with 8 timeouts before
-  // exhausting the max dependency chain link.
-  const non_equal_times = 8;
+  const ITERATIONS = 15;
 
   await new Promise((resolve, reject) => setTimeout(t.step_func(() => {
-    const parent = scheduler.taskId;
-    let last_parent_id = parent;
     const timeout = () => {
-      assert_not_equals(scheduler.taskId, parent);
-      if (times < non_equal_times) {
-        assert_not_equals(scheduler.taskId, last_parent_id);
-      } else {
-        assert_equals(scheduler.taskId, last_parent_id);
-      }
-      assert_equals(scheduler.isAncestor(parent), "ancestor");
-      assert_equals(scheduler.isAncestor(initialId), "ancestor");
-      if (++times < test_times) {
-        last_parent_id = scheduler.taskId;
+      assert_equals(scheduler.taskId, initialId);
+      if (++times < ITERATIONS) {
         setTimeout(t.step_func(timeout), 0);
       } else {
         resolve();
@@ -210,6 +195,7 @@
     setTimeout(t.step_func(timeout), 0);
   }), 0));
 }, "Recursive setTimeout");
+
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-xhr.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-xhr.html
index fa556d2..623b3a3 100644
--- a/third_party/blink/web_tests/wpt_internal/task-tracking/track-xhr.html
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-xhr.html
@@ -3,17 +3,18 @@
 <title>Verify that promise tasks can be properly tracked.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/task-ids.js"></script>
 
 <script>
 'use strict'
 
 promise_test((t) => {
   return new Promise((resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
     const request = new XMLHttpRequest();
     request.onload = t.step_func(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
         resolve();
       } catch {
         reject("XHR is not a descendant");
@@ -26,11 +27,34 @@
 
 promise_test((t) => {
   return new Promise((resolve, reject) => {
-    const initialId = scheduler.taskId;
+    const initialId = initializeTaskId();
+    const request = new XMLHttpRequest();
+    request.addEventListener('load', t.step_func(() => {
+      Promise.resolve().then(() => {});
+    }));
+    // This microtask checkpoint from the first listener will clear the
+    // continuation data in v8. Make sure the continuation data is set for the
+    // second listener.
+    request.addEventListener('load', t.step_func(() => {
+      try {
+        assert_equals(scheduler.taskId, initialId);
+        resolve();
+      } catch {
+        reject("XHR is not a descendant)");
+      }
+    }));
+    request.open("GET", "/resources/blank.html");
+    request.send();
+  });
+}, "An XMLHttpRequest load event is a descendant of the requesting task (multiple listeners)");
+
+promise_test((t) => {
+  return new Promise((resolve, reject) => {
+    const initialId = initializeTaskId();
     const request = new XMLHttpRequest();
     request.onreadystatechange= t.step_func(() => {
       try {
-        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        assert_equals(scheduler.taskId, initialId);
       } catch {
         reject("XHR readystatechange is not a descendant");
       }
@@ -50,12 +74,12 @@
     let id2;
 
     scheduler.postTask(() => {
-      id1 = scheduler.taskId;
+      id1 = initializeTaskId();
       request = new XMLHttpRequest();
       request.onload = t.step_func(() => {
         try {
-          assert_equals(scheduler.isAncestor(id1), "not ancestor");
-          assert_equals(scheduler.isAncestor(id2), "ancestor");
+          assert_not_equals(scheduler.taskId, id1);
+          assert_equals(scheduler.taskId, id2);
           resolve();
         } catch {
           reject("XHR is not a descendant of the correct task");
@@ -65,7 +89,8 @@
     });
 
     scheduler.postTask(() => {
-      id2 = scheduler.taskId;
+      id2 = initializeTaskId();
+      assert_equals(scheduler.taskId, id2);
       request.send();
     });
   });
@@ -78,7 +103,7 @@
     let id2;
 
     scheduler.postTask(() => {
-      id1 = scheduler.taskId;
+      id1 = initializeTaskId();
       request = new XMLHttpRequest();
       request.open("GET", "/resources/blank.html");
 
@@ -87,8 +112,8 @@
         request.open("GET", "/resources/blank.html");
         request.onload = t.step_func(() => {
           try {
-            assert_equals(scheduler.isAncestor(id1), "ancestor");
-            assert_equals(scheduler.isAncestor(id2), "not ancestor");
+            assert_equals(scheduler.taskId, id1);
+            assert_not_equals(scheduler.taskId, id2);
             resolve();
           } catch {
             reject("XHR is not a descendant of the correct task");
@@ -99,10 +124,49 @@
     });
 
     scheduler.postTask(() => {
-      id2 = scheduler.taskId;
+      id2 = initializeTaskId();
       request.send();
     });
   });
 }, "XMLHttpRequest load event is an ancestor of send() when reusing the object");
 
+promise_test((t) => {
+  // This resolves the promise created below in an unrelated task.
+  let innerResolve;
+  (async function() {
+    while (!innerResolve) {
+      await scheduler.postTask(() => {}, {delay: 50});
+    }
+    innerResolve();
+  })();
+
+  return new Promise((resolve, reject) => {
+    // Run the rest of the test in a separate task so the polling tasks above
+    // won't be descendants of `initialId`.
+    scheduler.postTask(() => {
+      const initialId = initializeTaskId();
+      const request = new XMLHttpRequest();
+      request.addEventListener('load', t.step_func(() => {
+        assert_equals(scheduler.taskId, initialId);
+        Promise.resolve().then(() => {});
+      }));
+
+      // The microtask in the event listener above clears the continuation data
+      // in v8. Ensure that the right context is captured for the microtask
+      // below resolving the related promise in a non-ancestor task.
+      request.addEventListener('load', t.step_func(async () => {
+        await new Promise((r) => { innerResolve = r; });
+        try {
+          assert_equals(scheduler.taskId, initialId);
+          resolve();
+        } catch (e) {
+          reject("XHR is not a descendant");
+        }
+      }));
+      request.open("GET", "/resources/blank.html");
+      request.send();
+    });
+  });
+}, "XMLHttpRequest load event is an ancestor of send() after a microtask checkpoint");
+
 </script>
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index 6a0e446..feaef4bf 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -56,7 +56,7 @@
 all_sources = crypto_sources + ssl_sources + pki_sources + pki_internal_headers
 all_headers = crypto_headers + ssl_headers + pki_headers + pki_internal_headers
 
-if (enable_rust_boringssl) {
+if (enable_rust) {
   rust_bindgen("raw_bssl_sys_bindings") {
     header = "src/rust/bssl-sys/wrapper.h"
     deps = [ ":boringssl" ]
diff --git a/third_party/catapult b/third_party/catapult
index 079202a..735ecd7 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 079202a7fd7b9a5016aba3cd8e4d8a35f12d5ed4
+Subproject commit 735ecd76d25b509aea2c4b5b04ca85655f2a974c
diff --git a/third_party/chromite b/third_party/chromite
index 284a94f..c6c74fc 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit 284a94feca39f9a9cdf09f5938312d48b6a17795
+Subproject commit c6c74fc7f352ccbe37d0a5c9d27c7cca37f40d6e
diff --git a/third_party/chromium-variations b/third_party/chromium-variations
index 2af5a48..e78e275 160000
--- a/third_party/chromium-variations
+++ b/third_party/chromium-variations
@@ -1 +1 @@
-Subproject commit 2af5a482054fe7fdc7edc80e9ed2008704385ce4
+Subproject commit e78e275e34f94fdf333245137878f0f6482db67d
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index b8c9c22..1fe9e8e 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -38,7 +38,6 @@
   CHECKED_STATE_CHANGED: 'checkedStateChanged',
   CHECKED_STATE_DESCRIPTION_CHANGED: 'checkedStateDescriptionChanged',
   CHILDREN_CHANGED: 'childrenChanged',
-  CLASS_NAME_CHANGED: 'classNameChanged',
   CLICKED: 'clicked',
   COLLAPSED: 'collapsed',
   CONTROLS_CHANGED: 'controlsChanged',
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index e4d36e1..2b2fd37c 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: N/A
-Revision: c4d4a4d83e864df270c267888176ffa415a599b0
+Revision: ccd20652bc4c7b9ab1878f5dcc0886d40d9a17c9
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/client/annotation_list.cc b/third_party/crashpad/crashpad/client/annotation_list.cc
index bcf7ca7..c736607 100644
--- a/third_party/crashpad/crashpad/client/annotation_list.cc
+++ b/third_party/crashpad/crashpad/client/annotation_list.cc
@@ -19,6 +19,46 @@
 
 namespace crashpad {
 
+template <typename T>
+T* AnnotationList::IteratorBase<T>::operator*() const {
+  CHECK_NE(curr_, tail_);
+  return curr_;
+}
+
+template <typename T>
+T* AnnotationList::IteratorBase<T>::operator->() const {
+  CHECK_NE(curr_, tail_);
+  return curr_;
+}
+
+template <typename T>
+AnnotationList::IteratorBase<T>& AnnotationList::IteratorBase<T>::operator++() {
+  CHECK_NE(curr_, tail_);
+  curr_ = curr_->GetLinkNode();
+  return *this;
+}
+
+template <typename T>
+AnnotationList::IteratorBase<T> AnnotationList::IteratorBase<T>::operator++(
+    int) {
+  T* const old_curr = curr_;
+  ++(*this);
+  return IteratorBase(old_curr, tail_);
+}
+
+template <typename T>
+bool AnnotationList::IteratorBase<T>::operator!=(
+    const IteratorBase& other) const {
+  return !(*this == other);
+}
+
+template <typename T>
+AnnotationList::IteratorBase<T>::IteratorBase(T* head, const Annotation* tail)
+    : curr_(head), tail_(tail) {}
+
+template class AnnotationList::IteratorBase<Annotation>;
+template class AnnotationList::IteratorBase<const Annotation>;
+
 AnnotationList::AnnotationList()
     : tail_pointer_(&tail_),
       head_(Annotation::Type::kInvalid, nullptr, nullptr),
@@ -65,49 +105,6 @@
   }
 }
 
-AnnotationList::Iterator::Iterator(Annotation* head, const Annotation* tail)
-    : curr_(head), tail_(tail) {}
-
-AnnotationList::Iterator::~Iterator() = default;
-
-Annotation* AnnotationList::Iterator::operator*() const {
-  CHECK_NE(curr_, tail_);
-  return curr_;
-}
-
-AnnotationList::Iterator& AnnotationList::Iterator::operator++() {
-  CHECK_NE(curr_, tail_);
-  curr_ = curr_->GetLinkNode();
-  return *this;
-}
-
-bool AnnotationList::Iterator::operator==(
-    const AnnotationList::Iterator& other) const {
-  return curr_ == other.curr_;
-}
-
-AnnotationList::ConstIterator::ConstIterator(const Annotation* head,
-                                             const Annotation* tail)
-    : curr_(head), tail_(tail) {}
-
-AnnotationList::ConstIterator::~ConstIterator() = default;
-
-const Annotation* AnnotationList::ConstIterator::operator*() const {
-  CHECK_NE(curr_, tail_);
-  return curr_;
-}
-
-AnnotationList::ConstIterator& AnnotationList::ConstIterator::operator++() {
-  CHECK_NE(curr_, tail_);
-  curr_ = curr_->GetLinkNode();
-  return *this;
-}
-
-bool AnnotationList::ConstIterator::operator==(
-    const AnnotationList::ConstIterator& other) const {
-  return curr_ == other.curr_;
-}
-
 AnnotationList::Iterator AnnotationList::begin() {
   return Iterator(head_.GetLinkNode(), tail_pointer_);
 }
diff --git a/third_party/crashpad/crashpad/client/annotation_list.h b/third_party/crashpad/crashpad/client/annotation_list.h
index eec7fb4..ce1f861 100644
--- a/third_party/crashpad/crashpad/client/annotation_list.h
+++ b/third_party/crashpad/crashpad/client/annotation_list.h
@@ -15,6 +15,8 @@
 #ifndef CRASHPAD_CLIENT_ANNOTATION_LIST_H_
 #define CRASHPAD_CLIENT_ANNOTATION_LIST_H_
 
+#include <iterator>
+
 #include "build/build_config.h"
 #include "client/annotation.h"
 
@@ -61,47 +63,46 @@
   void Add(Annotation* annotation);
 
   //! \brief An InputIterator for the AnnotationList.
-  class Iterator {
+  template <typename T>
+  class IteratorBase {
    public:
-    ~Iterator();
+    using difference_type = signed int;
+    using value_type = T*;
+    using reference = T*;
+    using pointer = void;
+    using iterator_category = std::input_iterator_tag;
 
-    Annotation* operator*() const;
-    Iterator& operator++();
-    bool operator==(const Iterator& other) const;
-    bool operator!=(const Iterator& other) const { return !(*this == other); }
+    IteratorBase(const IteratorBase& other) = default;
+    IteratorBase(IteratorBase&& other) = default;
 
-   private:
-    friend class AnnotationList;
-    Iterator(Annotation* head, const Annotation* tail);
+    ~IteratorBase() = default;
 
-    Annotation* curr_;
-    const Annotation* const tail_;
+    IteratorBase& operator=(const IteratorBase& other) = default;
+    IteratorBase& operator=(IteratorBase&& other) = default;
 
-    // Copy and assign are required.
-  };
+    T* operator*() const;
+    T* operator->() const;
 
-  //! \brief An InputIterator for iterating a const AnnotationList.
-  class ConstIterator {
-   public:
-    ~ConstIterator();
+    IteratorBase& operator++();
+    IteratorBase operator++(int);
 
-    const Annotation* operator*() const;
-    ConstIterator& operator++();
-    bool operator==(const ConstIterator& other) const;
-    bool operator!=(const ConstIterator& other) const {
-      return !(*this == other);
+    bool operator==(const IteratorBase& other) const {
+      return curr_ == other.curr_;
     }
 
+    bool operator!=(const IteratorBase& other) const;
+
    private:
     friend class AnnotationList;
-    ConstIterator(const Annotation* head, const Annotation* tail);
+    IteratorBase(T* head, const Annotation* tail);
 
-    const Annotation* curr_;
-    const Annotation* const tail_;
-
-    // Copy and assign are required.
+    T* curr_ = nullptr;
+    const Annotation* tail_ = nullptr;
   };
 
+  using Iterator = IteratorBase<Annotation>;
+  using ConstIterator = IteratorBase<const Annotation>;
+
   //! \brief Returns an iterator to the first element of the annotation list.
   Iterator begin();
   ConstIterator begin() const { return cbegin(); }
diff --git a/third_party/crashpad/crashpad/client/annotation_list_test.cc b/third_party/crashpad/crashpad/client/annotation_list_test.cc
index 0ac87ff..41ecfa05 100644
--- a/third_party/crashpad/crashpad/client/annotation_list_test.cc
+++ b/third_party/crashpad/crashpad/client/annotation_list_test.cc
@@ -14,7 +14,10 @@
 
 #include "client/annotation.h"
 
+#include <algorithm>
+#include <iterator>
 #include <string>
+#include <type_traits>
 #include <vector>
 
 #include "base/rand_util.h"
@@ -27,6 +30,52 @@
 namespace test {
 namespace {
 
+#if (__cplusplus >= 202002L)
+template <typename Iterator>
+  requires std::input_iterator<Iterator>
+void VerifyIsInputIterator(Iterator) {}
+#else
+template <typename Iterator>
+struct IsLegacyIteratorImpl {
+  static constexpr bool value =
+      std::is_copy_constructible_v<Iterator> &&
+      std::is_copy_assignable_v<Iterator> && std::is_destructible_v<Iterator> &&
+      std::is_swappable_v<Iterator> &&
+      // check that std::iterator_traits has the necessary types (check only one
+      // needed as std::iterator is required to define only if all are defined)
+      !std::is_same_v<typename std::iterator_traits<Iterator>::reference,
+                      void> &&
+      std::is_same_v<decltype(++std::declval<Iterator>()), Iterator&> &&
+      !std::is_same_v<decltype(*std::declval<Iterator>()), void>;
+};
+
+template <typename Iterator>
+struct IsLegacyInputIteratorImpl {
+  static constexpr bool value =
+      IsLegacyIteratorImpl<Iterator>::value &&
+      std::is_base_of_v<
+          std::input_iterator_tag,
+          typename std::iterator_traits<Iterator>::iterator_category> &&
+      std::is_convertible_v<decltype(std::declval<Iterator>() !=
+                                     std::declval<Iterator>()),
+                            bool> &&
+      std::is_convertible_v<decltype(std::declval<Iterator>() ==
+                                     std::declval<Iterator>()),
+                            bool> &&
+      std::is_same_v<decltype(*std::declval<Iterator>()),
+                     typename std::iterator_traits<Iterator>::reference> &&
+      std::is_same_v<decltype(++std::declval<Iterator>()), Iterator&> &&
+      std::is_same_v<decltype(std::declval<Iterator>()++), Iterator> &&
+      std::is_same_v<decltype(*(++std::declval<Iterator>())),
+                     typename std::iterator_traits<Iterator>::reference>;
+};
+
+template <typename Iterator>
+void VerifyIsInputIterator(Iterator) {
+  static_assert(IsLegacyInputIteratorImpl<Iterator>::value);
+}
+#endif
+
 TEST(AnnotationListStatic, Register) {
   ASSERT_FALSE(AnnotationList::Get());
   EXPECT_TRUE(AnnotationList::Register());
@@ -222,6 +271,23 @@
   EXPECT_EQ(const_iterator, annotations_.cend());
 }
 
+TEST_F(AnnotationList, IteratorIsInputIterator) {
+  one_.Set("1");
+  two_.Set("2");
+
+  // Check explicitly that the iterators meet the interface of an input
+  // iterator.
+  VerifyIsInputIterator(annotations_.begin());
+  VerifyIsInputIterator(annotations_.cbegin());
+  VerifyIsInputIterator(annotations_.end());
+  VerifyIsInputIterator(annotations_.cend());
+
+  // Additionally verify that std::distance accepts the iterators. It requires
+  // the iterators to comply to the input iterator interface.
+  EXPECT_EQ(std::distance(annotations_.begin(), annotations_.end()), 2);
+  EXPECT_EQ(std::distance(annotations_.cbegin(), annotations_.cend()), 2);
+}
+
 class RaceThread : public Thread {
  public:
   explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {}
diff --git a/third_party/crashpad/crashpad/test/ios/crash_type_xctest.mm b/third_party/crashpad/crashpad/test/ios/crash_type_xctest.mm
index 1b3b567..a76f686 100644
--- a/third_party/crashpad/crashpad/test/ios/crash_type_xctest.mm
+++ b/third_party/crashpad/crashpad/test/ios/crash_type_xctest.mm
@@ -177,7 +177,10 @@
   [rootObject_ crashException];
   // After https://reviews.llvm.org/D141222 exceptions call
   // __libcpp_verbose_abort, which Chromium sets to `brk 0` in release.
-#if defined(CRASHPAD_IS_IN_CHROMIUM) && defined(NDEBUG)
+  // After https://crrev.com/c/5375084, Chromium does not set `brk 0` for local
+  // release builds and official DCHECK builds.
+#if defined(CRASHPAD_IS_IN_CHROMIUM) && defined(NDEBUG) && \
+    defined(OFFICIAL_BUILD) && !defined(DCHECK_ALWAYS_ON)
   [self verifyCrashReportException:SIGABRT];
 #else
   [self verifyCrashReportException:EXC_SOFT_SIGNAL];
diff --git a/third_party/crashpad/crashpad/util/misc/arm64_pac_bti.S b/third_party/crashpad/crashpad/util/misc/arm64_pac_bti.S
index 85da8b56..77961d4 100644
--- a/third_party/crashpad/crashpad/util/misc/arm64_pac_bti.S
+++ b/third_party/crashpad/crashpad/util/misc/arm64_pac_bti.S
@@ -34,7 +34,7 @@
 #endif
 
 #if defined(__ARM_FEATURE_PAC_DEFAULT)
-#if ((__ARM_FEATURE_PAC_DEFAULT & ((1<<0)|(1<<2))) == 0)
+#if ((__ARM_FEATURE_PAC_DEFAULT & ((1<<0)|(1<<1))) == 0)
 #error Pointer authentication defines no valid key!
 #endif
 #define GNU_PROPERTY_AARCH64_PAC 1 // Has PAC
diff --git a/third_party/cros-components/BUILD.gn b/third_party/cros-components/BUILD.gn
index 5944985d4..f8ed2826 100644
--- a/third_party/cros-components/BUILD.gn
+++ b/third_party/cros-components/BUILD.gn
@@ -13,6 +13,7 @@
   "button/button.ts",
   "card/card.ts",
   "checkbox/checkbox.ts",
+  "chip/chip.ts",
   "dropdown/dropdown.ts",
   "dropdown/dropdown_option.ts",
   "helpers/helpers.ts",
@@ -25,6 +26,7 @@
   "menu/menu_separator.ts",
   "menu/menu_util.ts",
   "menu/sub_menu_item.ts",
+  "orca_feedback/orca-feedback.ts",
   "radio/radio.ts",
   "sidenav/sidenav.ts",
   "sidenav/sidenav_item.ts",
@@ -101,7 +103,7 @@
 
   import_mappings = [
     "lit|//resources/mwc/lit/index.js",
-    "@lit/|//resources/mwc/@lit/",
+    "@lit/task|//resources/mwc/lit/index.js",
 
     "@material/|chrome://resources/mwc/@material/",
 
diff --git a/third_party/dawn b/third_party/dawn
index 88bd94c..c00db02 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 88bd94c569428eddacba38af0700d5d09fde3744
+Subproject commit c00db02cbbfedcc03ce23a2f237468d1405a5cda
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 59c1900d..ca4cfda 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 59c1900dd6408d4977b2f50b84ad08a475b92215
+Subproject commit ca4cfdaf00d28ceb9c801779a852ad017f6326c1
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index 66b4b8e..7cb88b4 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit 66b4b8e55ce57e007a9abaf12ec4aacc14fbdb47
+Subproject commit 7cb88b45f4b8a5eed9fc512a5c8dd65a082d3456
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index a11a408..936bfad 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit a11a408bd5d81d06adac27ef13e74a24630d45c6
+Subproject commit 936bfad4657d91c0e3badb8eb5dbe9d51610d442
diff --git a/third_party/expat/0001-Do-not-claim-getrandom.patch b/third_party/expat/0001-Do-not-claim-getrandom.patch
index df931d2..34937a4 100644
--- a/third_party/expat/0001-Do-not-claim-getrandom.patch
+++ b/third_party/expat/0001-Do-not-claim-getrandom.patch
@@ -1,8 +1,17 @@
 diff --git a/third_party/expat/include/expat_config/expat_config.h b/third_party/expat/include/expat_config/expat_config.h
-index 278d7a8..c693a14a 100644
+index bc83d399214ed..81d6af75a6467 100644
 --- a/third_party/expat/include/expat_config/expat_config.h
 +++ b/third_party/expat/include/expat_config/expat_config.h
-@@ -23,7 +23,7 @@
+@@ -14,7 +14,7 @@
+ /* #undef HAVE_ARC4RANDOM */
+ 
+ /* Define to 1 if you have the `arc4random_buf' function. */
+-#define HAVE_ARC4RANDOM_BUF 1
++/* #define HAVE_ARC4RANDOM_BUF 1 */
+ 
+ /* define if the compiler supports basic C++11 syntax */
+ #define HAVE_CXX11 1
+@@ -29,7 +29,7 @@
  #define HAVE_GETPAGESIZE 1
  
  /* Define to 1 if you have the `getrandom' function. */
@@ -11,7 +20,7 @@
  
  /* Define to 1 if you have the <inttypes.h> header file. */
  #define HAVE_INTTYPES_H 1
-@@ -50,7 +50,7 @@
+@@ -56,7 +56,7 @@
  #define HAVE_STRING_H 1
  
  /* Define to 1 if you have `syscall' and `SYS_getrandom'. */
diff --git a/third_party/expat/roll-expat.sh b/third_party/expat/roll-expat.sh
index 4545871..f7a9bb5 100755
--- a/third_party/expat/roll-expat.sh
+++ b/third_party/expat/roll-expat.sh
@@ -10,7 +10,7 @@
   STEP="update README.chromium" &&
   EXPAT_VERSION=$(git -C third_party/expat/src/ describe --long) &&
   EXPAT_COMMIT=$(git -C third_party/expat/src/ rev-parse HEAD) &&
-  EXPAT_DATE=$(date "+%Y%m%d") &&
+  EXPAT_DATE=$(date "+%Y-%m-%d") &&
   EXPAT_CPE_VERSION=$(echo ${EXPAT_VERSION} | sed -r -e's/^R_([0-9]+)_([0-9]+)_([0-9]+)-[0-9]+-g[0-9a-f]+$/\1.\2.\3/') &&
   [ ${EXPAT_VERSION} != ${EXPAT_CPE_VERSION} ] &&
   sed -i'' -e "s/^Version: .*\$/Version: ${EXPAT_VERSION}/" third_party/expat/README.chromium &&
diff --git a/third_party/googletest/src b/third_party/googletest/src
index c231e6f..eff443c 160000
--- a/third_party/googletest/src
+++ b/third_party/googletest/src
@@ -1 +1 @@
-Subproject commit c231e6f5b152029dbd5fa4a9e0c04095035aec3f
+Subproject commit eff443c6ef5eb6ab598bfaae27f9427fdb4f6af7
diff --git a/third_party/jni_zero/jni_zero.gni b/third_party/jni_zero/jni_zero.gni
index e0a5eac9..5f0e508 100644
--- a/third_party/jni_zero/jni_zero.gni
+++ b/third_party/jni_zero/jni_zero.gni
@@ -9,6 +9,8 @@
 
 declare_args() {
   # Enables JNI multiplexing to reduce JNI native methods overhead.
+  # When we want to "enable" this, we can use this line instead/
+  # allow_jni_multiplexing = !is_java_debug && is_component_build == false
   allow_jni_multiplexing = false
 
   # Use hashed symbol names to reduce JNI symbol overhead.
diff --git a/third_party/libaom/README.chromium b/third_party/libaom/README.chromium
index 23193bcc..196910eb 100644
--- a/third_party/libaom/README.chromium
+++ b/third_party/libaom/README.chromium
@@ -2,7 +2,7 @@
 Short Name: libaom
 URL: https://aomedia.googlesource.com/aom/
 Version: N/A
-Revision: 77317a77d321d093f2ac0ba0775c32d28bfeee80
+Revision: 158761dfb40e77f3a54c3b14a596112837baa24b
 CPEPrefix: cpe:/a:aomedia:aomedia:3.8.1
 License: BSD
 License File: source/libaom/LICENSE
diff --git a/third_party/libaom/source/config/config/aom_version.h b/third_party/libaom/source/config/config/aom_version.h
index 2ac965b6..2fb7d03 100644
--- a/third_party/libaom/source/config/config/aom_version.h
+++ b/third_party/libaom/source/config/config/aom_version.h
@@ -11,9 +11,9 @@
 
 #define VERSION_MAJOR 3
 #define VERSION_MINOR 8
-#define VERSION_PATCH 1
-#define VERSION_EXTRA "336-g77317a77d3"
+#define VERSION_PATCH 2
+#define VERSION_EXTRA "343-g158761dfb4"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "3.8.1-336-g77317a77d3"
-#define VERSION_STRING " 3.8.1-336-g77317a77d3"
+#define VERSION_STRING_NOSP "3.8.2-343-g158761dfb4"
+#define VERSION_STRING " 3.8.2-343-g158761dfb4"
diff --git a/third_party/libaom/source/libaom b/third_party/libaom/source/libaom
index 77317a7..158761d 160000
--- a/third_party/libaom/source/libaom
+++ b/third_party/libaom/source/libaom
@@ -1 +1 @@
-Subproject commit 77317a77d321d093f2ac0ba0775c32d28bfeee80
+Subproject commit 158761dfb40e77f3a54c3b14a596112837baa24b
diff --git a/third_party/libavif/src b/third_party/libavif/src
index de32f53..43ec9ac 160000
--- a/third_party/libavif/src
+++ b/third_party/libavif/src
@@ -1 +1 @@
-Subproject commit de32f53d28cc8915acba6bedc8ee738e7f7445f5
+Subproject commit 43ec9ace31c6ca11efddddb61b94b744450d46e2
diff --git a/third_party/libjingle_xmpp/xmllite/xmlparser_unittest.cc b/third_party/libjingle_xmpp/xmllite/xmlparser_unittest.cc
index 82e29def..cd141d2 100644
--- a/third_party/libjingle_xmpp/xmllite/xmlparser_unittest.cc
+++ b/third_party/libjingle_xmpp/xmllite/xmlparser_unittest.cc
@@ -165,14 +165,22 @@
 
   fragment = "ph'><test";
   parser.Parse(fragment.c_str(), fragment.length(), false);
-  EXPECT_EQ("START (hmph:stream, id='abcdefg', xmlns='j:c', "
-      "http://www.w3.org/2000/xmlns/:stream='hmph') ", handler.StrClear());
-
-  fragment = "ing/><again/>abracad";
+  fragment = "ing/>";
   parser.Parse(fragment.c_str(), fragment.length(), false);
-  EXPECT_EQ("START (j:c:testing) END START (j:c:again) END TEXT (abracad) ",
+  // Note: crbug.com/330014030. We don't validate state between the 2 calls to
+  // Parse() above since expat changed in 2.6.0 how it parses partials:
+  // https://github.com/libexpat/libexpat/commit/9cdf9b8d77d5c2c2a27d15fb68dd3f83cafb45a1
+  // https://github.com/libexpat/libexpat/blob/8548bc03fdb887c8720f01e95440f1406bd15ffa/expat/Changes#L83
+  EXPECT_EQ(
+      "START (hmph:stream, id='abcdefg', xmlns='j:c', "
+      "http://www.w3.org/2000/xmlns/:stream='hmph') "
+      "START (j:c:testing) END ",
       handler.StrClear());
 
+  fragment = "<again/>abracad";
+  parser.Parse(fragment.c_str(), fragment.length(), false);
+  EXPECT_EQ("START (j:c:again) END TEXT (abracad) ", handler.StrClear());
+
   fragment = "abra</stream:";
   parser.Parse(fragment.c_str(), fragment.length(), false);
   EXPECT_EQ("TEXT (abra) ", handler.StrClear());
diff --git a/third_party/material_web_components/BUILD.gn b/third_party/material_web_components/BUILD.gn
index 22b508f..be04ce4 100644
--- a/third_party/material_web_components/BUILD.gn
+++ b/third_party/material_web_components/BUILD.gn
@@ -270,7 +270,7 @@
 
   import_mappings = [
     "lit|//resources/mwc/lit/index.js",
-    "@lit/task|//resources/mwc/@lit/task/index.js",
+    "@lit/task|//resources/mwc/lit/index.js",
   ]
   import_var_mappings = [
     "lit/static-html:html|staticHtml",
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn
index b610524..ba3e7790 100644
--- a/third_party/nearby/BUILD.gn
+++ b/third_party/nearby/BUILD.gn
@@ -9,8 +9,6 @@
 import("//build/config/rust.gni")
 
 assert(enable_rust, "Nearby Presence requires rust to be enabled")
-assert(enable_rust_boringssl,
-       "Nearby Presence requires boringssl to be enabled")
 
 # Nearby Config
 config("nearby_include_config") {
diff --git a/third_party/pefile_py3/README.chromium b/third_party/pefile_py3/README.chromium
index 248e1e5..75e058e 100644
--- a/third_party/pefile_py3/README.chromium
+++ b/third_party/pefile_py3/README.chromium
@@ -16,3 +16,5 @@
 Local Modifications:
  - Only pefile.py, ordlookup, and LICENSE are present.
  - BUILD.gn, OWNERS, and README.chromium files were added.
+ - pefile.py patched to increase the 0x10000000 offset limit for
+ https://crbug.com/329661971.
diff --git a/third_party/pefile_py3/pefile.py b/third_party/pefile_py3/pefile.py
index c162fbd..8a71e4e 100755
--- a/third_party/pefile_py3/pefile.py
+++ b/third_party/pefile_py3/pefile.py
@@ -138,6 +138,9 @@
 # Limit number of exported symbols
 MAX_SYMBOL_EXPORT_COUNT = 0x2000
 
+# Arbitrary maximum offset to detect suspicious/bogus files.
+MAX_OFFSET = 0x40000000
+
 IMAGE_DOS_SIGNATURE             = 0x5A4D
 IMAGE_DOSZM_SIGNATURE           = 0x4D5A
 IMAGE_NE_SIGNATURE              = 0x454E
@@ -2430,16 +2433,16 @@
                 self.__warnings.append(
                     'Error parsing section {0}. PointerToRawData points beyond the end of the file.'.format(i))
 
-            if section.Misc_VirtualSize > 0x10000000:
+            if section.Misc_VirtualSize > MAX_OFFSET:
                 simultaneous_errors += 1
                 self.__warnings.append(
-                    'Suspicious value found parsing section {0}. VirtualSize is extremely large > 256MiB.'.format(i))
+                    'Suspicious value found parsing section {0}. VirtualSize is extremely large > {1}.'.format(i, MAX_OFFSET))
 
             if self.adjust_SectionAlignment( section.VirtualAddress,
-                self.OPTIONAL_HEADER.SectionAlignment, self.OPTIONAL_HEADER.FileAlignment ) > 0x10000000:
+                self.OPTIONAL_HEADER.SectionAlignment, self.OPTIONAL_HEADER.FileAlignment ) > MAX_OFFSET:
                 simultaneous_errors += 1
                 self.__warnings.append(
-                    'Suspicious value found parsing section {0}. VirtualAddress is beyond 0x10000000.'.format(i))
+                    'Suspicious value found parsing section {0}. VirtualAddress is beyond {1}.'.format(i, MAX_OFFSET))
 
             if ( self.OPTIONAL_HEADER.FileAlignment != 0 and
                 ( section.PointerToRawData % self.OPTIONAL_HEADER.FileAlignment) != 0):
@@ -4397,7 +4400,7 @@
         return table
 
 
-    def get_memory_mapped_image(self, max_virtual_address=0x10000000, ImageBase=None):
+    def get_memory_mapped_image(self, max_virtual_address=MAX_OFFSET, ImageBase=None):
         """Returns the data corresponding to the memory layout of the PE file.
 
         The data includes the PE header and the sections loaded at offsets
diff --git a/third_party/perfetto b/third_party/perfetto
index 7e63f42..dfcd224 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 7e63f42d755ca255162f1f327126f90e34465581
+Subproject commit dfcd22426a245ba1a1a5e1e47b17fd27d3409bbb
diff --git a/third_party/skia b/third_party/skia
index 9950dc8..2f07d8e 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 9950dc8ec6fd376a48fb1e892ed4ef040bf07331
+Subproject commit 2f07d8e1829ba5bcd0868e3d27e644b87b110598
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 074f27e..0d7ba97 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 074f27e83c5591c976f789a7f3bd4447124e9640
+Subproject commit 0d7ba9788b088d29e91a83f2ecc682aeee03542d
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index 0f6a21d..c56e469 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit 0f6a21daf476a8c60dd8bebed05ad94fc503b66a
+Subproject commit c56e46958b2d7e2d47576f624ae1f66b2c04a849
diff --git a/third_party/webgpu-cts/ts_sources.txt b/third_party/webgpu-cts/ts_sources.txt
index 0cbe631..d47777e 100644
--- a/third_party/webgpu-cts/ts_sources.txt
+++ b/third_party/webgpu-cts/ts_sources.txt
@@ -742,6 +742,7 @@
 src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
 src/webgpu/shader/validation/expression/call/builtin/min.spec.ts
 src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts
+src/webgpu/shader/validation/expression/call/builtin/normalize.spec.ts
 src/webgpu/shader/validation/expression/call/builtin/pack4xI8.spec.ts
 src/webgpu/shader/validation/expression/call/builtin/pack4xI8Clamp.spec.ts
 src/webgpu/shader/validation/expression/call/builtin/pack4xU8.spec.ts
diff --git a/third_party/webrtc b/third_party/webrtc
index ab93959..802552a 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit ab9395924c18aed2c5cfc114c06d153a1c2c68ae
+Subproject commit 802552a8030d82ad07b72aa738f814f3a0030810
diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt
index 8389cdd..34175a7 100644
--- a/third_party/zlib/CMakeLists.txt
+++ b/third_party/zlib/CMakeLists.txt
@@ -74,6 +74,16 @@
 
     SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a+crc+crypto")
   endif()
+
+  if (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64")
+    add_definitions(-DRISCV_RVV)
+    add_definitions(-DDEFLATE_SLIDE_HASH_RVV)
+    add_definitions(-DADLER32_SIMD_RVV)
+    #TODO(cavalcantii): add remaining flags as we port optimizations to RVV.
+    # Required by CPU features detection code.
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=riscv64-unknown-linux-gnu -march=rv64gcv")
+  endif()
+
 endif()
 
 #
@@ -180,20 +190,26 @@
 # Update list of source files if optimizations were enabled
 #============================================================================
 if (ENABLE_SIMD_OPTIMIZATIONS)
-  list(REMOVE_ITEM ZLIB_SRCS inflate.c)
+  if (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64")
+    message("RISCVV: Add optimizations.")
+    list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/cpu_features.h)
+    list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/cpu_features.c)
+  else()
+    list(REMOVE_ITEM ZLIB_SRCS inflate.c)
 
-  list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/adler32_simd.h)
-  list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/chunkcopy.h)
-  list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/inffast_chunk.h)
-  list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/cpu_features.h)
-  list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/crc32_simd.h)
+    list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/adler32_simd.h)
+    list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/chunkcopy.h)
+    list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/inffast_chunk.h)
+    list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/cpu_features.h)
+    list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/crc32_simd.h)
 
-  list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/adler32_simd.c)
-  list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/inffast_chunk.c)
-  list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/inflate.c)
-  list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/cpu_features.c)
-  list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/crc32_simd.c)
-  list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/crc_folding.c)
+    list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/adler32_simd.c)
+    list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/inffast_chunk.c)
+    list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/inflate.c)
+    list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/cpu_features.c)
+    list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/crc32_simd.c)
+    list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/crc_folding.c)
+  endif()
 endif()
 
 # parse the full version number from zlib.h and include in ZLIB_FULL_VERSION
diff --git a/third_party/zlib/adler32.c b/third_party/zlib/adler32.c
index 99a2944..ebd1889 100644
--- a/third_party/zlib/adler32.c
+++ b/third_party/zlib/adler32.c
@@ -90,7 +90,8 @@
         return adler | (sum2 << 16);
     }
 
-#if defined(ADLER32_SIMD_SSSE3) || defined(ADLER32_SIMD_NEON)
+#if defined(ADLER32_SIMD_SSSE3) || defined(ADLER32_SIMD_NEON) \
+    || defined(RISCV_RVV)
     /*
      * Use SIMD to compute the adler32. Since this function can be
      * freely used, check CPU features here. zlib convention is to
diff --git a/third_party/zlib/cpu_features.c b/third_party/zlib/cpu_features.c
index 64e0428..34ae7b9 100644
--- a/third_party/zlib/cpu_features.c
+++ b/third_party/zlib/cpu_features.c
@@ -33,9 +33,13 @@
 int ZLIB_INTERNAL x86_cpu_enable_simd = 0;
 int ZLIB_INTERNAL x86_cpu_enable_avx512 = 0;
 
+int ZLIB_INTERNAL riscv_cpu_enable_rvv = 0;
+int ZLIB_INTERNAL riscv_cpu_enable_vclmul = 0;
+
 #ifndef CPU_NO_SIMD
 
-#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_FUCHSIA) || defined(ARMV8_OS_IOS)
+#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || \
+    defined(ARMV8_OS_FUCHSIA) || defined(ARMV8_OS_IOS)
 #include <pthread.h>
 #endif
 
@@ -62,7 +66,10 @@
 static void _cpu_check_features(void);
 #endif
 
-#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_MACOS) || defined(ARMV8_OS_FUCHSIA) || defined(X86_NOT_WINDOWS) || defined(ARMV8_OS_IOS)
+#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || \
+    defined(ARMV8_OS_MACOS) || defined(ARMV8_OS_FUCHSIA) || \
+    defined(X86_NOT_WINDOWS) || defined(ARMV8_OS_IOS) || \
+    defined(RISCV_RVV)
 #if !defined(ARMV8_OS_MACOS)
 // _cpu_check_features() doesn't need to do anything on mac/arm since all
 // features are known at build time, so don't call it.
@@ -184,6 +191,23 @@
     x86_cpu_enable_avx512 = _xgetbv(0) & 0x00000040;
 #endif
 }
+#endif // x86 & NO_SIMD
+
+#elif defined(RISCV_RVV)
+#include <sys/auxv.h>
+
+#ifndef ZLIB_HWCAP_RVV
+#define ZLIB_HWCAP_RVV (1 << ('v' - 'a'))
 #endif
-#endif
-#endif
+
+/* TODO(cavalcantii)
+ * - add support for Android@RISCV i.e. __riscv_hwprobe().
+ * - detect vclmul (crypto extensions).
+ */
+static void _cpu_check_features(void)
+{
+  unsigned long features = getauxval(AT_HWCAP);
+  riscv_cpu_enable_rvv = !!(features & ZLIB_HWCAP_RVV);
+}
+#endif // ARM | x86 | RISCV
+#endif // NO SIMD CPU
diff --git a/third_party/zlib/crc32.c b/third_party/zlib/crc32.c
index cf8579f..32686f92 100644
--- a/third_party/zlib/crc32.c
+++ b/third_party/zlib/crc32.c
@@ -706,7 +706,8 @@
      * place to cache CPU features if needed for those later, more
      * interesting crc32() calls.
      */
-#if defined(CRC32_SIMD_SSE42_PCLMUL) || defined(CRC32_ARMV8_CRC32)
+#if defined(CRC32_SIMD_SSE42_PCLMUL) || defined(CRC32_ARMV8_CRC32) \
+    || defined(RISCV_RVV)
     /*
      * Since this routine can be freely used, check CPU features here.
      */
@@ -1085,7 +1086,8 @@
     /* Some bots compile with optimizations disabled, others will emulate
      * ARM on x86 and other weird combinations.
      */
-#if defined(CRC32_SIMD_SSE42_PCLMUL) || defined(CRC32_ARMV8_CRC32)
+#if defined(CRC32_SIMD_SSE42_PCLMUL) || defined(CRC32_ARMV8_CRC32) \
+    || defined(RISCV_RVV)
     /* We got to verify CPU features, so exploit the common usage pattern
      * of calling this function with Z_NULL for an initial valid crc value.
      * This allows to cache the result of the feature check and avoid extraneous
diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c
index a67d195..b9a3120 100644
--- a/third_party/zlib/deflate.c
+++ b/third_party/zlib/deflate.c
@@ -401,7 +401,8 @@
     // for all wrapper formats (e.g. RAW, ZLIB, GZIP).
     // Feature detection is not triggered while using RAW mode (i.e. we never
     // call crc32() with a NULL buffer).
-#if defined(CRC32_ARMV8_CRC32) || defined(CRC32_SIMD_SSE42_PCLMUL)
+#if defined(CRC32_ARMV8_CRC32) || defined(CRC32_SIMD_SSE42_PCLMUL) \
+    || defined(RISCV_RVV)
     cpu_check_features();
 #endif
 
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index f7d2635c..2b013a3 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -41743,8 +41743,6 @@
   <suffix name="CompanionSidePanel" label="For companion side panel feature."/>
   <suffix name="CompanionSidePanelRegionSearch"
       label="For companion side panel region search feature."/>
-  <suffix name="ComposeMenuNewBadgeFeature"
-      label="For Compose new badge in context menu feature"/>
   <suffix name="ComposeMSBBSettingsFeature"
       label="For Compose MSBB Settings badge feature"/>
   <suffix name="ComposeNewBadgeFeature" label="For Compose new badge feature"/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b49c3a3f..b88cd57 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3886,7 +3886,7 @@
   <int value="2" label="destination missing"/>
   <int value="3" label="destination has wrong type"/>
   <int value="4" label="destination not potentially trustworthy"/>
-  <int value="5" label="number of destinations exceeds limit"/>
+  <int value="5" label="destination list value not potentially trustworthy"/>
   <int value="6" label="filter_data key too long"/>
   <int value="7" label="filter_data key reserved"/>
   <int value="8" label="filter_data not a valid dictionary"/>
@@ -3914,18 +3914,20 @@
   <int value="26" label="event_report_windows' end time value is invalid"/>
   <int value="27" label="trigger_data_matching value is invalid"/>
   <int value="28" label="trigger_specs has wrong type"/>
-  <int value="29" label="trigger spec has wrong type"/>
-  <int value="30" label="trigger spec's trigger_data missing"/>
-  <int value="31" label="trigger_spec's trigger_data not a valid list"/>
-  <int value="32" label="trigger_spec's trigger_data value invalid"/>
-  <int value="33" label="excessive trigger data"/>
-  <int value="34" label="invalid trigger data for matching mode"/>
-  <int value="35"
+  <int value="29" label="trigger spec's trigger_data missing"/>
+  <int value="30" label="trigger_spec's trigger_data not a valid list"/>
+  <int value="31" label="trigger_data not a valid list"/>
+  <int value="32" label="duplicate trigger data"/>
+  <int value="33" label="trigger_specs duplicate trigger data"/>
+  <int value="34" label="excessive trigger data"/>
+  <int value="35" label="trigger_specs excessive trigger data"/>
+  <int value="36" label="invalid trigger data for matching mode"/>
+  <int value="37"
       label="top-level trigger_data and specs are mutually exclusive"/>
-  <int value="36" label="summary_window_operator value is invalid"/>
-  <int value="37" label="summary_buckets not a valid list"/>
-  <int value="38" label="summary_buckets value invalid"/>
-  <int value="39" label="event_level_epsilon value is invalid"/>
+  <int value="38" label="summary_window_operator value is invalid"/>
+  <int value="39" label="summary_buckets not a valid list"/>
+  <int value="40" label="summary_buckets value invalid"/>
+  <int value="41" label="event_level_epsilon value is invalid"/>
 </enum>
 
 <enum name="ConversionsRegistrationMethod">
@@ -4015,33 +4017,33 @@
   <int value="0" label="Invalid JSON"/>
   <int value="1" label="Root JSON value has wrong type"/>
   <int value="2" label="Filters has wrong type"/>
-  <int value="3" label="Filters value has wrong type"/>
-  <int value="4" label="Filters list value has wrong type"/>
+  <int value="3" label="Filters value is invalid"/>
+  <int value="4" label="Filters lookback window value is invalid"/>
   <int value="5"
       label="Filters is using a reserved key (starts with &quot;_&quot;)"/>
-  <int value="6" label="aggregatable_values has wrong type"/>
-  <int value="7" label="aggregatable_values key too long"/>
-  <int value="8" label="aggregatable_values list value has wrong type"/>
-  <int value="9"
+  <int value="6" label="Filters list value is invalid"/>
+  <int value="7" label="Filters list lookback window value is invalid"/>
+  <int value="8"
+      label="Filters list is using a reserved key (starts with &quot;_&quot;)"/>
+  <int value="9" label="aggregatable_values has wrong type"/>
+  <int value="10" label="aggregatable_values key too long"/>
+  <int value="11"
       label="aggregatable_values list value is missing values field"/>
-  <int value="10" label="aggregatable_values value is invalid"/>
-  <int value="11" label="aggregatable_trigger_data has wrong type"/>
-  <int value="12" label="aggregatable_trigger_data value has wrong type"/>
-  <int value="13" label="aggregatable_trigger_data value key_piece missing"/>
-  <int value="14"
-      label="aggregatable_trigger_data value source_keys has wrong type"/>
-  <int value="15" label="aggregatable_trigger_data value key_piece is invalid"/>
-  <int value="16"
-      label="aggregatable_trigger_data value source_keys key is invalid"/>
-  <int value="17" label="event_trigger_data has wrong type"/>
-  <int value="18" label="event_trigger_data value is invalid"/>
-  <int value="19" label="event_trigger_data value has wrong type"/>
-  <int value="20" label="event_trigger priority value is invalid"/>
-  <int value="21" label="event_trigger dedup key value is invalid"/>
-  <int value="22" label="aggregation_coordinator_identifier value is invalid"/>
-  <int value="23" label="aggregatable_deduplication_keys has wrong type"/>
+  <int value="12" label="aggregatable_values value is invalid"/>
+  <int value="13" label="aggregatable_values list key too long"/>
+  <int value="14" label="aggregatable_values list value is invalid"/>
+  <int value="15" label="aggregatable_trigger_data has wrong type"/>
+  <int value="16" label="aggregatable_trigger_data value key_piece missing"/>
+  <int value="17"
+      label="aggregatable_tsrigger_data value source_keys is invalid"/>
+  <int value="18" label="aggregatable_trigger_data value key_piece is invalid"/>
+  <int value="19" label="event_trigger_data has wrong type"/>
+  <int value="20" label="event_trigger_data value is invalid"/>
+  <int value="21" label="event_trigger priority value is invalid"/>
+  <int value="22" label="event_trigger dedup key value is invalid"/>
+  <int value="23" label="aggregation_coordinator_identifier value is invalid"/>
   <int value="24" label="aggregatable_deduplication_keys value is invalid"/>
-  <int value="25" label="aggregatable_deduplication_keys value has wrong type"/>
+  <int value="25" label="aggregatable_deduplication_keys has wrong type"/>
   <int value="26"
       label="aggregatable_source_registration_time valid is invalid"/>
   <int value="27" label="trigger_context_id value is invalid"/>
@@ -11366,6 +11368,9 @@
   <int value="4915" label="WebPrintJobAttributesFunction"/>
   <int value="4916" label="WebPrintJobCancelFunction"/>
   <int value="4917" label="WebPrintJobOnJobStateChangeEvent"/>
+  <int value="4918" label="FledgeAuctionReportBuyers"/>
+  <int value="4919" label="FledgeAuctionReportBuyerDebugModeConfig"/>
+  <int value="4920" label="FedCmButtonMode"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -18194,6 +18199,7 @@
   <int value="-1217425153" label="ChromeHomeClearUrlOnOpen:enabled"/>
   <int value="-1216837777" label="clear-data-reduction-proxy-data-savings"/>
   <int value="-1216570731" label="OfflineHome:enabled"/>
+  <int value="-1215746151" label="ForestFeature:enabled"/>
   <int value="-1215340781" label="CrOSSystemVoiceIsolationOption:enabled"/>
   <int value="-1215163628"
       label="AccessibilityUnserializeOptimizations:disabled"/>
@@ -22760,6 +22766,7 @@
   <int value="852703179" label="OptimizeNetworkBuffers:enabled"/>
   <int value="853279705"
       label="DesktopPWAsAdditionalWindowingControls:disabled"/>
+  <int value="853695274" label="ForestFeature:disabled"/>
   <int value="854294457" label="EnableOmniboxRichEntities:disabled"/>
   <int value="854391022" label="SmartLockUIRevamp:enabled"/>
   <int value="854436130" label="MultiCalendarSupport:enabled"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index ea4443a..ec86dd8 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1178,7 +1178,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.OpenFileTime.AllExpired" units="ms"
-    expires_after="2024-04-28">
+    expires_after="2024-08-25">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1198,7 +1198,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.OpenFileTime.FirstAttempt" units="ms"
-    expires_after="2024-04-28">
+    expires_after="2024-08-25">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1212,7 +1212,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.OpenFileTime.FirstExpired" units="ms"
-    expires_after="2024-04-28">
+    expires_after="2024-08-25">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1231,7 +1231,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.OpenFileTime.LastAttempt" units="ms"
-    expires_after="2024-04-28">
+    expires_after="2024-08-25">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1247,7 +1247,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.Size" units="KB"
-    expires_after="2024-06-30">
+    expires_after="2024-08-25">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1258,7 +1258,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.UriCreatedInterval" units="ms"
-    expires_after="2024-04-28">
+    expires_after="2024-08-25">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -5961,7 +5961,7 @@
 </histogram>
 
 <histogram name="Android.WebView.UserAgentClientHintsMetadata.AvailableType"
-    enum="UserAgentMetadataAvailableType" expires_after="2024-05-01">
+    enum="UserAgentMetadataAvailableType" expires_after="2024-12-01">
   <owner>victortan@chromium.org</owner>
   <owner>torne@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index c001f31b..77fded75 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -2397,9 +2397,21 @@
   </summary>
 </histogram>
 
-<histogram name="Apps.PromiseApp.IconType" enum="PromiseAppIconType"
+<histogram name="Apps.PromiseApp.LifecycleEvent"
+    enum="PromiseAppLifecycleEvent" expires_after="2024-10-20">
+  <owner>tsergeant@chromium.org</owner>
+  <owner>chromeos-apps-foundation-team@google.com</owner>
+  <summary>
+    Records how often each key lifecycle event occurs across all promise apps. A
+    promise app is a temporary item in the Launcher/ Shelf which represents an
+    app installation, and critical app installation stages will trigger an
+    update to this histogram under their relevant event category.
+  </summary>
+</histogram>
+
+<histogram name="Apps.PromiseApp.PromiseAppIconType" enum="PromiseAppIconType"
     expires_after="2024-10-20">
-  <owner>vpao@google.com</owner>
+  <owner>tsergeant@chromium.org</owner>
   <owner>chromeos-apps-foundation-team@google.com</owner>
   <summary>
     Records how often placeholder icons (as opposed to real app icons) are used
@@ -2410,21 +2422,9 @@
   </summary>
 </histogram>
 
-<histogram name="Apps.PromiseApp.LifecycleEvent"
-    enum="PromiseAppLifecycleEvent" expires_after="2024-10-20">
-  <owner>vpao@google.com</owner>
-  <owner>chromeos-apps-foundation-team@google.com</owner>
-  <summary>
-    Records how often each key lifecycle event occurs across all promise apps. A
-    promise app is a temporary item in the Launcher/ Shelf which represents an
-    app installation, and critical app installation stages will trigger an
-    update to this histogram under their relevant event category.
-  </summary>
-</histogram>
-
 <histogram name="Apps.PromiseApp.PromiseAppType" enum="PromiseAppType"
     expires_after="2024-10-20">
-  <owner>vpao@google.com</owner>
+  <owner>tsergeant@chromium.org</owner>
   <owner>chromeos-apps-foundation-team@google.com</owner>
   <summary>
     Records what app type a promise app is when the app installation
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 7fb3aef..0203808 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -2246,17 +2246,6 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.Desks.CloseAllZombieWindowsFound" units="windows"
-    expires_after="2024-04-28">
-  <owner>benbecker@google.com</owner>
-  <owner>chromeos-wms@google.com</owner>
-  <summary>
-    Emitted one minute after the DesksController synchronously closes all
-    windows with widgets remaining in a desk. Reports the number of windows with
-    null widgets that are still open after that time.
-  </summary>
-</histogram>
-
 <histogram name="Ash.Desks.ConsecutiveDailyVisits" units="days"
     expires_after="2024-09-03">
   <owner>afakhry@chromium.org</owner>
@@ -3939,17 +3928,6 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.Glanceables.TimeManagement.TotalShowTime" units="ms"
-    expires_after="2024-12-31">
-  <owner>anasalazar@google.com</owner>
-  <owner>chromeos-launcher@google.com</owner>
-  <summary>
-    The amount of time that the user kept the glanceables time management bubble
-    open. The time is measured from the moment the glanceables bubble is created
-    until said bubble is destroyed.
-  </summary>
-</histogram>
-
 <histogram
     name="Ash.Glanceables.TimeManagement.{GlanceableBubble}.ChangeListToLoadTime.{Status}"
     units="ms" expires_after="2024-12-31">
@@ -3990,6 +3968,24 @@
   </token>
 </histogram>
 
+<histogram
+    name="Ash.Glanceables.TimeManagement{GlanceableBubble}.TotalShowTime"
+    units="ms" expires_after="2024-12-31">
+  <owner>anasalazar@google.com</owner>
+  <owner>chromeos-launcher@google.com</owner>
+  <summary>
+    The amount of time that the user kept the glanceables time management bubble
+    open. The time is measured from the moment the glanceables bubble is created
+    until said bubble is destroyed. Recorded when {GlanceableBubble} glanceables
+    bubble is shown.
+  </summary>
+  <token key="GlanceableBubble">
+    <variant name="" summary="either"/>
+    <variant name=".Classroom" summary="Google Classroom"/>
+    <variant name=".Tasks" summary="Google Tasks"/>
+  </token>
+</histogram>
+
 <histogram name="Ash.Growth.CampaignsComponent.DownloadDurationInOobe"
     units="ms" expires_after="2024-10-17">
   <owner>llin@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/enums.xml b/tools/metrics/histograms/metadata/autofill/enums.xml
index bf35fedb..7ec3d26 100644
--- a/tools/metrics/histograms/metadata/autofill/enums.xml
+++ b/tools/metrics/histograms/metadata/autofill/enums.xml
@@ -70,6 +70,14 @@
              not have contained email addresses."/>
 </enum>
 
+<enum name="AutofillAddNewAddressPromptOutcome">
+  <summary>
+    Describes the user's decision on the 'And new address' prompt.
+  </summary>
+  <int value="0" label="Accepted and saved a new address."/>
+  <int value="1" label="Canceled explicitly or ignored"/>
+</enum>
+
 <enum name="AutofillAddressProfileImportCountrySpecificFieldRequirement">
   <int value="0" label="All required fields present"/>
   <int value="1" label="Missing: ZIP"/>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 50df16c..b228412 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -2935,6 +2935,17 @@
   </token>
 </histogram>
 
+<histogram name="Autofill.ManualFallback.AddNewAddressPromptShown"
+    enum="AutofillAddNewAddressPromptOutcome" expires_after="2025-03-18">
+  <owner>brunobraga@google.com</owner>
+  <owner>vykochko@google.com</owner>
+  <owner>chrome-autofill-alerts@google.com</owner>
+  <summary>
+    Logs the outcome of the 'Add new address' popup prompt interaction (new
+    address added or prompt canceled/ignored).
+  </summary>
+</histogram>
+
 <histogram
     name="Autofill.ManualFallback.ExplicitlyTriggered.{AutocompleteState}.Address"
     enum="BooleanSelectManualFallback" expires_after="2024-08-25">
diff --git a/tools/metrics/histograms/metadata/commerce/histograms.xml b/tools/metrics/histograms/metadata/commerce/histograms.xml
index a2380496..2b08b4aa58 100644
--- a/tools/metrics/histograms/metadata/commerce/histograms.xml
+++ b/tools/metrics/histograms/metadata/commerce/histograms.xml
@@ -366,7 +366,7 @@
 </histogram>
 
 <histogram name="Commerce.PriceDrops.{ManagementType}.NotificationCount"
-    units="notifications" expires_after="2023-04-25">
+    units="notifications" expires_after="2025-03-17">
   <owner>zhiyuancai@chromium.org</owner>
   <owner>ayman@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
@@ -380,7 +380,7 @@
 </histogram>
 
 <histogram name="Commerce.PriceDrops.{ManagementType}.NotificationReachedCap"
-    enum="Boolean" expires_after="2023-04-25">
+    enum="Boolean" expires_after="2025-03-17">
   <owner>zhiyuancai@chromium.org</owner>
   <owner>ayman@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
@@ -395,7 +395,7 @@
 
 <histogram
     name="Commerce.PriceDrops.{TabUsageStatus}{LocationIdentifier}.ContainsPrice"
-    enum="BooleanContainsPrice" expires_after="2024-08-18">
+    enum="BooleanContainsPrice" expires_after="2025-03-17">
   <owner>davidjm@chromium.org</owner>
   <owner>ayman@chromium.org</owner>
   <owner>dtrainor@chromium.org</owner>
@@ -411,7 +411,7 @@
 
 <histogram
     name="Commerce.PriceDrops.{TabUsageStatus}{LocationIdentifier}.ContainsPriceDrop"
-    enum="BooleanContainsPriceDrop" expires_after="2024-08-18">
+    enum="BooleanContainsPriceDrop" expires_after="2025-03-17">
   <owner>davidjm@chromium.org</owner>
   <owner>ayman@chromium.org</owner>
   <owner>dtrainor@chromium.org</owner>
@@ -428,7 +428,7 @@
 
 <histogram
     name="Commerce.PriceDrops.{TabUsageStatus}{LocationIdentifier}.IsProductDetailPage"
-    enum="BooleanIsProductDetailPage" expires_after="2024-04-15">
+    enum="BooleanIsProductDetailPage" expires_after="2025-03-17">
   <owner>davidjm@chromium.org</owner>
   <owner>ayman@chromium.org</owner>
   <owner>dtrainor@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/crostini/histograms.xml b/tools/metrics/histograms/metadata/crostini/histograms.xml
index c09194a..0de8942 100644
--- a/tools/metrics/histograms/metadata/crostini/histograms.xml
+++ b/tools/metrics/histograms/metadata/crostini/histograms.xml
@@ -494,9 +494,9 @@
 </histogram>
 
 <histogram name="Crostini.TerminalSettingsChanged"
-    enum="CrostiniTerminalSetting" expires_after="2024-04-28">
+    enum="CrostiniTerminalSetting" expires_after="2025-04-28">
   <owner>joelhockey@chromium.org</owner>
-  <owner>lxj@google.com</owner>
+  <owner>clumptini@google.com</owner>
   <summary>
     Record which settings in terminal are changed by users. This is captured
     each time terminal is launched and fetches the current settings, and not
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index 4b9fd0b3..42a66fe 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -99,8 +99,6 @@
   <variant name="IPH_CompanionSidePanelRegionSearch"
       summary="prompting users to use the region search feature in the
                companion side panel"/>
-  <variant name="IPH_ComposeMenuNewBadgeFeature"
-      summary="New badge shows on compose context menu item"/>
   <variant name="IPH_ComposeMSBBSettingsFeature"
       summary="Prompting users to enable MSBB to use the compose feature."/>
   <variant name="IPH_ComposeNewBadgeFeature"
diff --git a/tools/metrics/histograms/metadata/media/enums.xml b/tools/metrics/histograms/metadata/media/enums.xml
index 0ce8999..47a01c2 100644
--- a/tools/metrics/histograms/metadata/media/enums.xml
+++ b/tools/metrics/histograms/metadata/media/enums.xml
@@ -1847,6 +1847,11 @@
   <int value="3" label="Allowed"/>
 </enum>
 
+<enum name="TextureOwnerCodecType">
+  <int value="0" label="kMediaCodec"/>
+  <int value="1" label="kStreamTexture"/>
+</enum>
+
 <enum name="URLSchemeForHistogram">
   <int value="0" label="kUnknown"/>
   <int value="1" label="kMissing"/>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index b826f25d..bdcdf34 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -328,6 +328,17 @@
   </summary>
 </histogram>
 
+<histogram name="Media.AImageReaderGLOwner.CodecType"
+    enum="TextureOwnerCodecType" expires_after="2024-08-21">
+  <owner>vasilyt@chromium.org</owner>
+  <owner>media-dev-uma@chromium.org</owner>
+  <summary>
+    Logs whether we use MediaCodec or MediaPlayer (aka StreamTexture path). This
+    is recorded when AImageReader acquireNextImage or acquireLatestImage is
+    called.
+  </summary>
+</histogram>
+
 <histogram name="Media.AImageReaderGLOwner.HasFence" enum="Boolean"
     expires_after="2024-09-01">
   <owner>vasilyt@chromium.org</owner>
@@ -6665,6 +6676,16 @@
   </summary>
 </histogram>
 
+<histogram name="MediaPreviews.UI.PageInfo.Camera.PixelHeight" units="pixels"
+    expires_after="2025-04-01">
+  <owner>atadres@chromium.org</owner>
+  <owner>openscreen-eng@google.com</owner>
+  <summary>
+    Records the height in pixels of the camera frames rendered in the PageInfo
+    preview (not the actual height of the preview itself).
+  </summary>
+</histogram>
+
 <histogram name="MediaPreviews.UI.PageInfo.Mic.NumInUseDevices" units="devices"
     expires_after="2025-04-01">
   <owner>atadres@chromium.org</owner>
@@ -6675,6 +6696,16 @@
   </summary>
 </histogram>
 
+<histogram name="MediaPreviews.UI.Permissions.Camera.PixelHeight"
+    units="pixels" expires_after="2025-04-01">
+  <owner>atadres@chromium.org</owner>
+  <owner>openscreen-eng@google.com</owner>
+  <summary>
+    Records the height in pixels of the camera frames rendered in the
+    Permissions preview (not the actual height of the preview itself).
+  </summary>
+</histogram>
+
 <histogram base="true" name="MediaRouter.Cast.App.Availability" units="ms"
     expires_after="2024-07-14">
 <!-- Name completed by histogram_suffixes name="MediaRouterSuccess" -->
@@ -6823,14 +6854,14 @@
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Launch" units="ms"
-    expires_after="2024-09-01">
+    expires_after="2024-09-18">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Total time to launch a Cast Streaming mirror session.</summary>
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Length" units="ms"
-    expires_after="2024-09-01">
+    expires_after="2024-09-18">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -6840,7 +6871,7 @@
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Length.AccessCode"
-    units="ms" expires_after="2024-09-04">
+    units="ms" expires_after="2024-09-18">
   <owner>bzielinski@google.com</owner>
   <owner>cros-edu-eng@google.com</owner>
   <summary>
@@ -6850,7 +6881,7 @@
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Length.OffscreenTab"
-    units="ms" expires_after="2024-05-01">
+    units="ms" expires_after="2024-09-18">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -6859,14 +6890,14 @@
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Length.Screen" units="ms"
-    expires_after="2024-05-01">
+    expires_after="2024-09-18">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Total length of a Cast Streaming Screen mirror session.</summary>
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Length.Tab" units="ms"
-    expires_after="2024-05-01">
+    expires_after="2024-09-18">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -7064,7 +7095,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Provider.TerminateRoute.Result"
-    enum="MediaRouteProviderResult" expires_after="2024-05-01">
+    enum="MediaRouteProviderResult" expires_after="2024-09-18">
 <!-- Name completed by histogram_suffixes name="MediaRouteProvider" -->
 
   <owner>takumif@chromium.org</owner>
@@ -7136,7 +7167,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Ui.Android.DialogAction"
-    enum="MediaRouterAndroidDialogAction" expires_after="2024-05-01">
+    enum="MediaRouterAndroidDialogAction" expires_after="2024-09-18">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -7148,7 +7179,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Ui.Android.DialogType"
-    enum="MediaRouterAndroidDialogType" expires_after="2024-05-01">
+    enum="MediaRouterAndroidDialogType" expires_after="2024-09-18">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -7202,7 +7233,7 @@
 </histogram>
 
 <histogram name="MediaRouter.WiredDisplay.AvailableDevicesCount" units="units"
-    expires_after="2024-04-28">
+    expires_after="2024-09-18">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/network/enums.xml b/tools/metrics/histograms/metadata/network/enums.xml
index 4e0f8e0..d9a7705 100644
--- a/tools/metrics/histograms/metadata/network/enums.xml
+++ b/tools/metrics/histograms/metadata/network/enums.xml
@@ -55,6 +55,13 @@
   <int value="1" label="Managed"/>
 </enum>
 
+<enum name="CAPPORTQueryResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="Request failed"/>
+  <int value="2" label="Response failed"/>
+  <int value="3" label="Invalid response"/>
+</enum>
+
 <enum name="CaptivePortalNetworkState">
   <int value="0" label="UNKNOWN"/>
   <int value="1" label="ONLINE"/>
@@ -1327,14 +1334,23 @@
   <int value="2" label="Suppress"/>
 </enum>
 
+<enum name="PortalDetectorAggregateCAPPORTResult">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Captive"/>
+  <int value="2" label="Open, no verified Internet"/>
+  <int value="3" label="Open, with verified Internet"/>
+</enum>
+
 <enum name="PortalDetectorAggregateResult">
   <int value="0" label="Unknown"/>
   <int value="1" label="No Connectivity"/>
   <int value="2" label="Partial Connectivity"/>
-  <int value="3" label="Redirect"/>
+  <int value="3" label="Portal-Redirect (302, 307)"/>
   <int value="4" label="Internet After Partial Connectivity"/>
-  <int value="5" label="Internet After Redirect"/>
+  <int value="5" label="Internet After Portal-Redirect"/>
   <int value="6" label="Internet"/>
+  <int value="7" label="Portal-Suspected (200)"/>
+  <int value="8" label="Internet After Portal-Suspected"/>
 </enum>
 
 <enum name="PortalDetectorResult">
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index 1c8b72f..62f5479 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -2241,6 +2241,64 @@
   </summary>
 </histogram>
 
+<histogram name="Network.Shill.CAPPORT.ContainsBytesRemaining"
+    enum="BooleanSuccess" expires_after="2024-12-31">
+  <owner>akahuang@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
+  <summary>
+    ChromeOS network metric indicating if a CAPPORT status response contains the
+    &quot;bytes-remaining&quot; field defined in RFC8908. This metric is only
+    recorded once per CAPPORT session and only if the CAPPORT server started
+    advertising &quot;captive&quot; as false.
+  </summary>
+</histogram>
+
+<histogram name="Network.Shill.CAPPORT.ContainsSecondsRemaining"
+    enum="BooleanSuccess" expires_after="2024-12-31">
+  <owner>akahuang@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
+  <summary>
+    ChromeOS network metric indicating if a CAPPORT status response contains the
+    &quot;seconds-remaining&quot; field defined in RFC8908. This metric is only
+    recorded once per CAPPORT session and only if the CAPPORT server started
+    advertising &quot;captive&quot; as false.
+  </summary>
+</histogram>
+
+<histogram name="Network.Shill.CAPPORT.ContainsVenueInfoURL"
+    enum="BooleanSuccess" expires_after="2024-12-31">
+  <owner>akahuang@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
+  <summary>
+    ChromeOS network metric indicating if a CAPPORT status response contains the
+    &quot;venue-info-url&quot; field defined in RFC8908. This metric is only
+    recorded once per CAPPORT session and only if the CAPPORT server started
+    advertising &quot;captive&quot; as false.
+  </summary>
+</histogram>
+
+<histogram name="Network.Shill.CAPPORT.MaxSecondsRemaining" units="seconds"
+    expires_after="2024-12-31">
+  <owner>akahuang@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
+  <summary>
+    ChromeOS network metric indicating if the maximum value advertised in
+    CAPPORT status responses for the &quot;seconds-remaining&quot; field defined
+    in RFC8908. This metric is only recorded once per CAPPORT session (the
+    maximum value).
+  </summary>
+</histogram>
+
+<histogram name="Network.Shill.CAPPORT.QueryResult" enum="CAPPORTQueryResult"
+    expires_after="2024-12-31">
+  <owner>akahuang@chromium.org</owner>
+  <owner>cros-network-metrics@google.com</owner>
+  <summary>
+    ChromeOS network metric indicating the results for a CAPPORT query as
+    defined in RFC8908.
+  </summary>
+</histogram>
+
 <histogram name="Network.Shill.Cellular.3GPPRegistrationDelayedDrop"
     enum="NetworkCellular3GPPRegistrationDelayedDrop"
     expires_after="2024-09-01">
@@ -2677,6 +2735,19 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Network.Shill.PortalDetector.AggregateCAPPORTResult.{Technology}"
+    enum="PortalDetectorAggregateCAPPORTResult" expires_after="2024-12-31">
+  <owner>hugobenichi@google.com</owner>
+  <owner>cros-network-metrics@google.com</owner>
+  <summary>
+    Result of ChromeOS network validation CAPPORT queries aggregated over
+    multiple queries until Internet connectivity has been validated or until
+    disconnection.
+  </summary>
+  <token key="Technology" variants="PhysicalTechnology"/>
+</histogram>
+
 <histogram name="Network.Shill.PortalDetector.AggregateResult.{Technology}"
     enum="PortalDetectorAggregateResult" expires_after="2024-12-31">
   <owner>hugobenichi@google.com</owner>
@@ -2912,12 +2983,17 @@
   <owner>cros-network-metrics@google.com</owner>
   <summary>
     Total time it takes for ChromeOS network validation to find a captive portal
-    redirection or to validate Internet access starting from the initial network
-    connection (&quot;TimeToRedirect, &quot;TimeToInternet&quot;) or starting
-    from the first discovery of a captive portal redirection
-    (&quot;TimeToInternetAfterRedirect&quot;).
+    redirection (&quot;TimeToRedirect&quot;), or to find a captive portal
+    sign-in URL after a CAPPORT query as defined in RFC8908
+    (&quot;TimeToCAPPORTUserPortalURL&quot;), or to validate Internet access
+    starting from the initial network connection (&quot;TimeToInternet&quot;),
+    or starting from the first discovery of a captive portal redirection
+    (&quot;TimeToInternetAfterRedirect&quot;), or after interacting with a
+    CAPPORT server as defined in RFC8908 (&quot;TimeToCAPPORTNotCaptive&quot;),
   </summary>
   <token key="Transition">
+    <variant name="TimeToCAPPORTNotCaptive"/>
+    <variant name="TimeToCAPPORTUserPortalURL"/>
     <variant name="TimeToInternet"/>
     <variant name="TimeToInternetAfterRedirect"/>
     <variant name="TimeToRedirect"/>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index eb1e849bb..916c60c 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -2865,31 +2865,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContextMenu.TimeToTakeAction.Abandoned" units="ms"
-    expires_after="2024-04-28">
-  <owner>twellington@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
-  <summary>
-    The time it takes for the user to abandon the context menu after it's shown
-    by pressing back or touching outside the menu. This is only logged if the
-    menu is abandoned. See ContextMenu.TimeToTakeAction.SelectedItem for the
-    cases where it is closed as a result of user interaction with the menu.
-    Android only.
-  </summary>
-</histogram>
-
-<histogram name="ContextMenu.TimeToTakeAction.SelectedItem" units="ms"
-    expires_after="2024-07-14">
-  <owner>twellington@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
-  <summary>
-    The time it takes for the user to select an item. This is only logged if the
-    menu is closed as a result of the user clicking a menu item or a DirectShare
-    button. See ContextMenu.TimeToTakeAction.Abandoned for the cases where it is
-    abandoned by the user. Android only.
-  </summary>
-</histogram>
-
 <histogram name="ContextMenu.ViewsTextServices.Emoji" enum="Boolean"
     expires_after="2023-10-08">
   <owner>yyushkina@chromium.org</owner>
@@ -3890,7 +3865,7 @@
   </summary>
 </histogram>
 
-<histogram name="Conversions.SourceRegistrationError12"
+<histogram name="Conversions.SourceRegistrationError13"
     enum="ConversionSourceRegistrationError" expires_after="2024-06-16">
   <owner>tquintanilla@chromium.org</owner>
   <owner>linnan@chromium.org</owner>
@@ -4054,7 +4029,7 @@
   </summary>
 </histogram>
 
-<histogram name="Conversions.TriggerRegistrationError10"
+<histogram name="Conversions.TriggerRegistrationError11"
     enum="ConversionTriggerRegistrationError" expires_after="2024-07-14">
   <owner>tquintanilla@chromium.org</owner>
   <owner>linnan@chromium.org</owner>
@@ -11055,7 +11030,7 @@
 
 <histogram name="UpdateClient.BackgroundDownloaderMac.StartDownloadOutcome"
     enum="UpdateClientBackgroundDownloaderMacStartDownloadOutcome"
-    expires_after="2024-05-01">
+    expires_after="2024-09-01">
   <owner>noahrose@google.com</owner>
   <owner>sorin@chromium.org</owner>
   <owner>waffles@chromium.org</owner>
@@ -11163,7 +11138,7 @@
 </histogram>
 
 <histogram name="UpgradeDetector.HoursBeforeUpgrade" units="hours"
-    expires_after="2024-05-01">
+    expires_after="2024-09-01">
   <owner>emshack@chromium.org</owner>
   <owner>pbos@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/settings/histograms.xml b/tools/metrics/histograms/metadata/settings/histograms.xml
index ade1d8cc8..4dd4bf5 100644
--- a/tools/metrics/histograms/metadata/settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/settings/histograms.xml
@@ -312,16 +312,6 @@
   </summary>
 </histogram>
 
-<histogram name="Settings.PrivacySandbox.ConsentCheckIsMismatched"
-    enum="Boolean" expires_after="2024-08-28">
-  <owner>boujane@google.com</owner>
-  <owner>koilos@google.com</owner>
-  <summary>
-    Records mismatches between the calculated consent requirement and any
-    overrides applied to the Privacy Sandbox consent param.
-  </summary>
-</histogram>
-
 <histogram name="Settings.PrivacySandbox.DialogDisplayHost"
     enum="SettingsPrivacySandboxDialogDisplayHostHash"
     expires_after="2024-09-01">
@@ -396,16 +386,6 @@
   </summary>
 </histogram>
 
-<histogram name="Settings.PrivacySandbox.NoticeCheckIsMismatched"
-    enum="Boolean" expires_after="2024-08-28">
-  <owner>boujane@google.com</owner>
-  <owner>koilos@google.com</owner>
-  <summary>
-    Records mismatches between the calculated notice requirement and any
-    overrides applied to the Privacy Sandbox notice param.
-  </summary>
-</histogram>
-
 <histogram name="Settings.PrivacySandbox.PrivacySandboxReferrer"
     enum="PrivacySandboxReferrer" expires_after="2023-02-12">
   <owner>andzaytsev@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/start_surface/histograms.xml b/tools/metrics/histograms/metadata/start_surface/histograms.xml
index 3166d80..4207e112 100644
--- a/tools/metrics/histograms/metadata/start_surface/histograms.xml
+++ b/tools/metrics/histograms/metadata/start_surface/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="StartSurface.ColdStartup.IsLastActiveTabNtp" enum="Boolean"
-    expires_after="2024-03-17">
+    expires_after="2024-07-21">
   <owner>hanxi@chromium.org</owner>
   <owner>xinyiji@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index 8e9373c1..e45982b 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -522,7 +522,7 @@
 </histogram>
 
 <histogram name="Sync.DataTypeActiveForSyncToSigninMigration{SyncModelType}"
-    enum="BooleanActive" expires_after="2024-07-07">
+    enum="BooleanActive" expires_after="2024-09-01">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -649,7 +649,7 @@
 </histogram>
 
 <histogram name="Sync.FeatureStatusForSyncToSigninMigration"
-    enum="SyncFeatureStatusForSyncToSigninMigration" expires_after="2024-07-07">
+    enum="SyncFeatureStatusForSyncToSigninMigration" expires_after="2024-09-01">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1626,7 +1626,7 @@
 </histogram>
 
 <histogram name="Sync.SyncToSigninMigration.ReadingListMigrationStep"
-    enum="SyncToSigninMigrationReadingListStep" expires_after="2024-08-04">
+    enum="SyncToSigninMigrationReadingListStep" expires_after="2024-09-01">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1656,7 +1656,7 @@
 
 <histogram
     name="Sync.SyncToSigninMigrationDecision{SyncToSigninMigrationMode}{SyncModelTypeForSyncToSigninMigration}"
-    enum="SyncToSigninMigrationDataTypeDecision" expires_after="2024-08-04">
+    enum="SyncToSigninMigrationDataTypeDecision" expires_after="2024-09-01">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1672,7 +1672,7 @@
 </histogram>
 
 <histogram name="Sync.SyncToSigninMigrationOutcome" enum="BooleanSuccess"
-    expires_after="2024-08-04">
+    expires_after="2024-09-01">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1686,7 +1686,7 @@
 </histogram>
 
 <histogram name="Sync.SyncToSigninMigrationOutcome.BookmarksFileMove"
-    enum="PlatformFileError" expires_after="2024-05-01">
+    enum="PlatformFileError" expires_after="2024-09-01">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1699,7 +1699,7 @@
 </histogram>
 
 <histogram name="Sync.SyncToSigninMigrationOutcome.PasswordsFileMove"
-    enum="PlatformFileError" expires_after="2024-08-04">
+    enum="PlatformFileError" expires_after="2024-09-01">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1712,7 +1712,7 @@
 </histogram>
 
 <histogram name="Sync.SyncToSigninMigrationTime" units="ms"
-    expires_after="2024-08-04">
+    expires_after="2024-09-01">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
diff --git a/tools/metrics/histograms/metadata/web_core/enums.xml b/tools/metrics/histograms/metadata/web_core/enums.xml
index 123497d3..e4fbb4f 100644
--- a/tools/metrics/histograms/metadata/web_core/enums.xml
+++ b/tools/metrics/histograms/metadata/web_core/enums.xml
@@ -382,6 +382,17 @@
   <int value="15" label="Second use of a script resource"/>
   <int value="16" label="Worker script"/>
   <int value="17" label="Module script"/>
+  <int value="18" label="No data pipe"/>
+  <int value="19" label="Loading cancelled"/>
+  <int value="20" label="Non JavaScript module"/>
+  <int value="21" label="Disabled by feature list"/>
+  <int value="22" label="Script type missmatch"/>
+  <int value="23" label="BackgroundResponseProcessor will be used"/>
+  <int value="24" label="Non JavaScript module (background)"/>
+  <int value="25" label="Code-cache available for script (background)"/>
+  <int value="26" label="Script too small (background)"/>
+  <int value="27" label="An error had occurred (background)"/>
+  <int value="28" label="Encoding not supported (background)"/>
 </enum>
 
 <enum name="ResourceHasClient">
diff --git a/tools/metrics/histograms/metadata/web_core/histograms.xml b/tools/metrics/histograms/metadata/web_core/histograms.xml
index 498c268..9535c474 100644
--- a/tools/metrics/histograms/metadata/web_core/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_core/histograms.xml
@@ -679,10 +679,15 @@
 </histogram>
 
 <histogram name="WebCore.Scripts.Async.NotStreamingReason"
-    enum="NotStreamingReason" expires_after="2023-12-10">
+    enum="NotStreamingReason" expires_after="2024-09-18">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
-  <summary>Reason for not streaming an async script.</summary>
+  <summary>
+    Reason for not streaming an async script.
+
+    Warning: This histogram was expired from 2023-12-10 to 2024-03-19. Data may
+    be missing.
+  </summary>
 </histogram>
 
 <histogram name="WebCore.Scripts.Async.StartedStreaming" enum="BooleanStreamed"
@@ -693,10 +698,15 @@
 </histogram>
 
 <histogram name="WebCore.Scripts.Deferred.NotStreamingReason"
-    enum="NotStreamingReason" expires_after="2023-12-10">
+    enum="NotStreamingReason" expires_after="2024-09-18">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
-  <summary>Reason for not streaming a deferred script.</summary>
+  <summary>
+    Reason for not streaming a deferred script.
+
+    Warning: This histogram was expired from 2023-12-10 to 2024-03-19. Data may
+    be missing.
+  </summary>
 </histogram>
 
 <histogram name="WebCore.Scripts.Deferred.StartedStreaming"
@@ -731,12 +741,15 @@
 </histogram>
 
 <histogram name="WebCore.Scripts.Other.NotStreamingReason"
-    enum="NotStreamingReason" expires_after="2023-12-10">
+    enum="NotStreamingReason" expires_after="2024-09-18">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
   <summary>
     Reason for not streaming an other (not async, deferred or parser blocking)
     script.
+
+    Warning: This histogram was expired from 2023-12-10 to 2024-03-19. Data may
+    be missing.
   </summary>
 </histogram>
 
@@ -751,10 +764,15 @@
 </histogram>
 
 <histogram name="WebCore.Scripts.ParsingBlocking.NotStreamingReason"
-    enum="NotStreamingReason" expires_after="2023-10-08">
+    enum="NotStreamingReason" expires_after="2024-09-18">
   <owner>leszeks@chromium.org</owner>
   <owner>v8-runtime@google.com</owner>
-  <summary>Reason for not streaming a parsing blocking script.</summary>
+  <summary>
+    Reason for not streaming a parsing blocking script.
+
+    Warning: This histogram was expired from 2023-10-08 to 2024-03-19. Data may
+    be missing.
+  </summary>
 </histogram>
 
 <histogram name="WebCore.Scripts.ParsingBlocking.StartedStreaming"
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index dfb70db..8adb26d5 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -13896,6 +13896,16 @@
       were predicted, this will be capped at 100.
     </summary>
   </metric>
+  <metric
+      name="OptimizationGuidePredictionCorrectlyPredictedLowPrioritySubresources.CrossOrigin">
+    <summary>
+      The number of predicted cross-origin subresources that were only used by
+      the page for low-priority subresource loads before it unloaded. This will
+      be recorded if the Optimization Guide was consulted for the page load and
+      the Optimization Guide had a prediction for it. If more than 100
+      subresources were predicted, this will be capped at 100.
+    </summary>
+  </metric>
   <metric name="OptimizationGuidePredictionCorrectlyPredictedOrigins">
     <summary>
       The number of subresource origins that were correctly predicted by the
@@ -13909,7 +13919,18 @@
   <metric name="OptimizationGuidePredictionCorrectlyPredictedSubresources">
     <summary>
       The number of subresources that were correctly predicted by the
-      Optimization Guide. This only counts high-priority subresources used
+      Optimiazation Guide. This only counts high-priority subresources used
+      before the page finishes loading. This will be recorded if the
+      Optimization Guide was consulted for the page load and the Optimization
+      Guide had a prediction for it. If more than 100 subresources were
+      predicted, this will be capped at 100.
+    </summary>
+  </metric>
+  <metric
+      name="OptimizationGuidePredictionCorrectlyPredictedSubresources.CrossOrigin">
+    <summary>
+      The number of cross-origin subresources that were correctly predicted by
+      the Optimization Guide. This only counts high-priority subresources used
       before the page finishes loading. This will be recorded if the
       Optimization Guide was consulted for the page load and the Optimization
       Guide had a prediction for it. If more than 100 subresources were
@@ -13940,6 +13961,15 @@
       subresources were predicted, this will be capped at 100.
     </summary>
   </metric>
+  <metric name="OptimizationGuidePredictionSubresources.CrossOrigin">
+    <summary>
+      The number of cross-origin subresources that were predicted by the
+      Optimization Guide. This will be recorded if the Optimization Guide was
+      consulted for the page load and the Optimization Guide had a prediction
+      for it. If more than 100 subresources were predicted, this will be capped
+      at 100.
+    </summary>
+  </metric>
   <metric name="SubresourceOriginPreconnectsInitiated">
     <summary>
       The number of subresource origins that were initiated by the Loading
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index aedcb8d..de6aba34 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -70,6 +70,7 @@
 speedometer2,"cbruni@chromium.org, vahl@chromium.org",Blink>JavaScript,https://browserbench.org/Speedometer2.1,all
 speedometer2-future,"cbruni@chromium.org, vahl@chromium.org",Blink>JavaScript,https://browserbench.org/Speedometer2.1,all
 speedometer2-nominorms,omerkatz@chromium.org,Blink>JavaScript>GarbageCollection,https://browserbench.org/Speedometer2.1,all
+speedometer2-predictable,"agarwaltushar@google.com, wnwen@google.com",Blink>JavaScript,https://browserbench.org/Speedometer2.1,all
 speedometer3,"cbruni@chromium.org, vahl@chromium.org",Blink>JavaScript,https://github.com/WebKit/Speedometer,all
 speedometer3-future,"cbruni@chromium.org, vahl@chromium.org",Blink>JavaScript,https://github.com/WebKit/Speedometer,all
 speedometer3-nominorms,omerkatz@chromium.org,Blink>JavaScript>GarbageCollection,https://github.com/WebKit/Speedometer,all
diff --git a/tools/perf/benchmarks/speedometer2.py b/tools/perf/benchmarks/speedometer2.py
index cfef21f3..4d1334b 100644
--- a/tools/perf/benchmarks/speedometer2.py
+++ b/tools/perf/benchmarks/speedometer2.py
@@ -206,3 +206,20 @@
 
   def SetExtraBrowserOptions(self, options):
     options.AppendExtraBrowserArgs('--js-flags=--no-minor-ms')
+
+
+@benchmark.Info(emails=['agarwaltushar@google.com', 'wnwen@google.com'],
+                component='Blink>JavaScript',
+                documentation_url='https://browserbench.org/Speedometer2.1')
+class Speedometer2Predictable(Speedometer2):
+  """The latest Speedometer2 benchmark with V8's `predictable` mode.
+
+  This should (hopefully) help reduce variance in the score.
+  """
+
+  @classmethod
+  def Name(cls):
+    return 'speedometer2-predictable'
+
+  def SetExtraBrowserOptions(self, options):
+    options.AppendExtraBrowserArgs('--js-flags=--predictable')
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index bd363465a..c7821f7d 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -246,6 +246,7 @@
     'jetstream2-nominorms',
     'octane-nominorms',
     'speedometer2-nominorms',
+    'speedometer2-predictable',
     'speedometer3-nominorms',
 ])
 # TODO(crbug.com/965158): Remove OFFICIAL_BENCHMARK_NAMES once sharding
@@ -401,11 +402,12 @@
     'blink_perf.svg',
     'blink_perf.paint',
 ])
-_LINUX_BENCHMARK_CONFIGS_WITH_NOMINORMS = PerfSuite(
+_LINUX_BENCHMARK_CONFIGS_WITH_NOMINORMS_PREDICTABLE = PerfSuite(
     _LINUX_BENCHMARK_CONFIGS).Add([
         'jetstream2-nominorms',
         'octane-nominorms',
         'speedometer2-nominorms',
+        'speedometer2-predictable',
         'speedometer3-nominorms',
     ])
 _LINUX_EXECUTABLE_CONFIGS = frozenset([
@@ -536,6 +538,7 @@
     _GetBenchmarkConfig('jetstream2'),
     _GetBenchmarkConfig('rendering.mobile'),
     _GetBenchmarkConfig('speedometer2'),
+    _GetBenchmarkConfig('speedometer2-predictable'),
     _GetBenchmarkConfig('speedometer3'),
 ])
 _ANDROID_PIXEL6_PRO_BENCHMARK_CONFIGS = PerfSuite(
@@ -596,7 +599,7 @@
 # Linux
 LINUX = PerfPlatform('linux-perf',
                      'Ubuntu-18.04, 8 core, NVIDIA Quadro P400',
-                     _LINUX_BENCHMARK_CONFIGS_WITH_NOMINORMS,
+                     _LINUX_BENCHMARK_CONFIGS_WITH_NOMINORMS_PREDICTABLE,
                      26,
                      'linux',
                      executables=_LINUX_EXECUTABLE_CONFIGS)
@@ -780,7 +783,7 @@
                               'android',
                               executables=_ANDROID_PIXEL6_EXECUTABLE_CONFIGS)
 ANDROID_PIXEL6_PGO = PerfPlatform('android-pixel6-perf-pgo', 'Android T',
-                                  _ANDROID_PIXEL6_PGO_BENCHMARK_CONFIGS, 4,
+                                  _ANDROID_PIXEL6_PGO_BENCHMARK_CONFIGS, 28,
                                   'android')
 ANDROID_PIXEL6_PRO = PerfPlatform(
     'android-pixel6-pro-perf',
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 9329bb3..9e4dcb0 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -220,7 +220,6 @@
         'linux',
         'dimension': {
             'gpu': '10de',
-            'id': 'build186-b7',
             'os': 'Ubuntu-18.04',
             'pool': 'chrome.tests.perf-fyi',
         },
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 658bd499..e335090 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/v43.2/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "62fc1bf75382e1baf67c8922603dd0843e307f60",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/7e63f42d755ca255162f1f327126f90e34465581/trace_processor_shell.exe"
+            "hash": "c771d056f40cab3aa9ea8f6abfdd3abc6e989cc9",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/a66020f87046d8934c22e20acb2bb8a91901ce8e/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "aa33f55a1523af4f0fa33f09a2d7ec44276e6b18",
@@ -14,7 +14,7 @@
         },
         "mac": {
             "hash": "f2a5ce0a81f47f6386e9560f6291da78e1c82fe0",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/7e63f42d755ca255162f1f327126f90e34465581/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/dfcd22426a245ba1a1a5e1e47b17fd27d3409bbb/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "efba8361ce036a2249cdf34180ac8c671a1caf69",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "ad33541876d824c897dfc65e8422e1c91b8149ea",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/7e63f42d755ca255162f1f327126f90e34465581/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/dfcd22426a245ba1a1a5e1e47b17fd27d3409bbb/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json
index 0653cfcb..ebb9b34 100644
--- a/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json
@@ -5,64 +5,418 @@
                 "abridged": false
             },
             "rendering.mobile": {
-                "end": 124,
+                "end": 11,
                 "abridged": false
             },
             "speedometer2": {
                 "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
             }
         }
     },
     "1": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 124,
-                "end": 249,
+                "begin": 11,
+                "end": 32,
                 "abridged": false
             },
             "speedometer2": {
                 "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
             }
         }
     },
     "2": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 249,
-                "end": 368,
+                "begin": 32,
+                "end": 49,
                 "abridged": false
             },
             "speedometer2": {
                 "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
             }
         }
     },
     "3": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 368,
+                "begin": 49,
+                "end": 66,
                 "abridged": false
             },
             "speedometer2": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "4": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 66,
+                "end": 83,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "5": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 83,
+                "end": 101,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "6": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 101,
+                "end": 120,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "7": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 120,
+                "end": 138,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "8": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 138,
+                "end": 157,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "9": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 157,
+                "end": 174,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "10": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 174,
+                "end": 190,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "11": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 190,
+                "end": 207,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "12": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 207,
+                "end": 229,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "13": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 229,
+                "end": 240,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "14": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 240,
+                "end": 258,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "15": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 258,
+                "end": 280,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "16": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 280,
+                "end": 300,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "17": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 300,
+                "end": 313,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "18": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 313,
+                "end": 331,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "19": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 331,
+                "end": 344,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            }
+        }
+    },
+    "20": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 344,
+                "end": 362,
+                "abridged": false
+            }
+        }
+    },
+    "21": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 362,
+                "end": 383,
+                "abridged": false
+            }
+        }
+    },
+    "22": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 383,
+                "end": 405,
+                "abridged": false
+            }
+        }
+    },
+    "23": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 405,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
             "system_health.common_mobile": {
+                "end": 7,
+                "abridged": false
+            }
+        }
+    },
+    "24": {
+        "benchmarks": {
+            "system_health.common_mobile": {
+                "begin": 7,
+                "end": 18,
+                "abridged": false
+            }
+        }
+    },
+    "25": {
+        "benchmarks": {
+            "system_health.common_mobile": {
+                "begin": 18,
+                "end": 32,
+                "abridged": false
+            }
+        }
+    },
+    "26": {
+        "benchmarks": {
+            "system_health.common_mobile": {
+                "begin": 32,
+                "end": 56,
+                "abridged": false
+            }
+        }
+    },
+    "27": {
+        "benchmarks": {
+            "system_health.common_mobile": {
+                "begin": 56,
                 "abridged": false
             }
         }
     },
     "extra_infos": {
-        "num_stories": 491,
-        "predicted_min_shard_time": 2631.0,
-        "predicted_min_shard_index": 1,
-        "predicted_max_shard_time": 2641.0,
-        "predicted_max_shard_index": 2,
-        "shard #0": 2640.0,
-        "shard #1": 2631.0,
-        "shard #2": 2641.0,
-        "shard #3": 2640.0
+        "num_stories": 536,
+        "predicted_min_shard_time": 388.0,
+        "predicted_min_shard_index": 24,
+        "predicted_max_shard_time": 477.0,
+        "predicted_max_shard_index": 27,
+        "shard #0": 423.0,
+        "shard #1": 420.0,
+        "shard #2": 413.0,
+        "shard #3": 409.0,
+        "shard #4": 411.0,
+        "shard #5": 421.0,
+        "shard #6": 415.0,
+        "shard #7": 420.0,
+        "shard #8": 420.0,
+        "shard #9": 422.0,
+        "shard #10": 421.0,
+        "shard #11": 418.0,
+        "shard #12": 395.0,
+        "shard #13": 416.0,
+        "shard #14": 416.0,
+        "shard #15": 421.0,
+        "shard #16": 431.0,
+        "shard #17": 426.0,
+        "shard #18": 402.0,
+        "shard #19": 425.0,
+        "shard #20": 409.0,
+        "shard #21": 423.0,
+        "shard #22": 412.0,
+        "shard #23": 427.0,
+        "shard #24": 388.0,
+        "shard #25": 420.0,
+        "shard #26": 412.0,
+        "shard #27": 477.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/linux-perf_map.json b/tools/perf/core/shard_maps/linux-perf_map.json
index fc24d31..3a829f4 100644
--- a/tools/perf/core/shard_maps/linux-perf_map.json
+++ b/tools/perf/core/shard_maps/linux-perf_map.json
@@ -37,6 +37,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -61,10 +64,7 @@
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "abridged": false
-            },
-            "blink_perf.css": {
-                "end": 2,
+                "end": 52,
                 "abridged": false
             },
             "jetstream2": {
@@ -96,6 +96,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -106,12 +109,15 @@
     },
     "2": {
         "benchmarks": {
+            "blink_perf.bindings": {
+                "begin": 52,
+                "abridged": false
+            },
             "blink_perf.css": {
-                "begin": 2,
                 "abridged": false
             },
             "blink_perf.dom": {
-                "end": 6,
+                "end": 5,
                 "abridged": false
             },
             "jetstream2": {
@@ -143,6 +149,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -154,7 +163,7 @@
     "3": {
         "benchmarks": {
             "blink_perf.dom": {
-                "begin": 6,
+                "begin": 5,
                 "abridged": false
             },
             "blink_perf.events": {
@@ -164,7 +173,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 26,
+                "end": 23,
                 "abridged": false
             },
             "jetstream2": {
@@ -196,6 +205,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -207,8 +219,8 @@
     "4": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 26,
-                "end": 109,
+                "begin": 23,
+                "end": 107,
                 "abridged": false
             },
             "jetstream2": {
@@ -240,6 +252,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -251,7 +266,7 @@
     "5": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 109,
+                "begin": 107,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
@@ -267,7 +282,7 @@
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
-                "end": 27,
+                "end": 22,
                 "abridged": false
             },
             "system_health.common_desktop": {
@@ -293,6 +308,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -304,7 +322,7 @@
     "6": {
         "benchmarks": {
             "blink_perf.shadow_dom": {
-                "begin": 27,
+                "begin": 22,
                 "abridged": false
             },
             "blink_perf.svg": {
@@ -323,7 +341,7 @@
                 "abridged": false
             },
             "desktop_ui": {
-                "end": 6,
+                "end": 5,
                 "abridged": false
             },
             "system_health.common_desktop": {
@@ -349,6 +367,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -360,7 +381,7 @@
     "7": {
         "benchmarks": {
             "desktop_ui": {
-                "begin": 6,
+                "begin": 5,
                 "abridged": false
             },
             "dummy_benchmark.noisy_benchmark_1": {
@@ -376,7 +397,7 @@
                 "abridged": false
             },
             "media.desktop": {
-                "end": 11,
+                "end": 7,
                 "abridged": false
             },
             "system_health.common_desktop": {
@@ -402,6 +423,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -418,11 +442,11 @@
     "8": {
         "benchmarks": {
             "media.desktop": {
-                "begin": 11,
+                "begin": 7,
                 "abridged": false
             },
             "memory.desktop": {
-                "end": 3,
+                "end": 2,
                 "abridged": false
             },
             "system_health.common_desktop": {
@@ -448,6 +472,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -459,8 +486,8 @@
     "9": {
         "benchmarks": {
             "memory.desktop": {
-                "begin": 3,
-                "end": 8,
+                "begin": 2,
+                "end": 7,
                 "abridged": false
             },
             "system_health.common_desktop": {
@@ -486,6 +513,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -497,7 +527,7 @@
     "10": {
         "benchmarks": {
             "memory.desktop": {
-                "begin": 8,
+                "begin": 7,
                 "abridged": false
             },
             "octane": {
@@ -507,7 +537,7 @@
                 "abridged": false
             },
             "power.desktop": {
-                "end": 12,
+                "end": 8,
                 "abridged": false
             },
             "speedometer2": {
@@ -516,6 +546,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -541,14 +574,14 @@
     "11": {
         "benchmarks": {
             "power.desktop": {
-                "begin": 12,
+                "begin": 8,
                 "abridged": false
             },
             "rasterize_and_record_micro.top_25": {
                 "abridged": false
             },
             "rendering.desktop": {
-                "end": 33,
+                "end": 25,
                 "abridged": false
             },
             "speedometer2": {
@@ -557,6 +590,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -568,8 +604,8 @@
     "12": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 33,
-                "end": 85,
+                "begin": 25,
+                "end": 78,
                 "abridged": false
             },
             "speedometer2": {
@@ -578,6 +614,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -589,8 +628,8 @@
     "13": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 85,
-                "end": 142,
+                "begin": 78,
+                "end": 136,
                 "abridged": false
             },
             "speedometer2": {
@@ -599,6 +638,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -610,8 +652,8 @@
     "14": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 142,
-                "end": 188,
+                "begin": 136,
+                "end": 182,
                 "abridged": false
             },
             "speedometer2": {
@@ -620,6 +662,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -631,8 +676,8 @@
     "15": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 188,
-                "end": 240,
+                "begin": 182,
+                "end": 234,
                 "abridged": false
             },
             "speedometer2": {
@@ -641,6 +686,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -652,8 +700,8 @@
     "16": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 240,
-                "end": 279,
+                "begin": 234,
+                "end": 281,
                 "abridged": false
             },
             "speedometer2": {
@@ -662,6 +710,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -673,8 +724,8 @@
     "17": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 279,
-                "end": 327,
+                "begin": 281,
+                "end": 328,
                 "abridged": false
             },
             "speedometer2": {
@@ -683,6 +734,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -694,7 +748,7 @@
     "18": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 327,
+                "begin": 328,
                 "abridged": false
             },
             "rendering.desktop.notracing": {
@@ -715,6 +769,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -725,7 +782,7 @@
                 "abridged": false
             },
             "system_health.common_desktop": {
-                "end": 19,
+                "end": 16,
                 "abridged": false
             }
         }
@@ -733,8 +790,8 @@
     "19": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 19,
-                "end": 69,
+                "begin": 16,
+                "end": 66,
                 "abridged": false
             },
             "speedometer2": {
@@ -743,6 +800,9 @@
             "speedometer2-nominorms": {
                 "abridged": false
             },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
             "speedometer3": {
                 "abridged": false
             },
@@ -754,11 +814,11 @@
     "20": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 69,
+                "begin": 66,
                 "abridged": false
             },
             "system_health.memory_desktop": {
-                "end": 17,
+                "end": 16,
                 "abridged": false
             }
         }
@@ -766,8 +826,8 @@
     "21": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 17,
-                "end": 34,
+                "begin": 16,
+                "end": 32,
                 "abridged": false
             }
         }
@@ -775,8 +835,8 @@
     "22": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 34,
-                "end": 60,
+                "begin": 32,
+                "end": 59,
                 "abridged": false
             }
         }
@@ -784,8 +844,8 @@
     "23": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 60,
-                "end": 77,
+                "begin": 59,
+                "end": 76,
                 "abridged": false
             }
         }
@@ -793,7 +853,7 @@
     "24": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 77,
+                "begin": 76,
                 "abridged": false
             },
             "v8.browsing_desktop": {
@@ -825,36 +885,36 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1255,
-        "predicted_min_shard_time": 1200.0,
-        "predicted_min_shard_index": 23,
+        "num_stories": 1286,
+        "predicted_min_shard_time": 1157.0,
+        "predicted_min_shard_index": 6,
         "predicted_max_shard_time": 1543.0,
         "predicted_max_shard_index": 25,
-        "shard #0": 1229.0,
-        "shard #1": 1237.0,
-        "shard #2": 1249.0,
-        "shard #3": 1234.0,
-        "shard #4": 1242.0,
-        "shard #5": 1237.0,
-        "shard #6": 1346.0,
-        "shard #7": 1290.0,
-        "shard #8": 1224.0,
-        "shard #9": 1225.0,
-        "shard #10": 1227.0,
-        "shard #11": 1225.0,
-        "shard #12": 1240.0,
-        "shard #13": 1230.0,
-        "shard #14": 1232.0,
-        "shard #15": 1239.0,
-        "shard #16": 1218.0,
-        "shard #17": 1231.0,
-        "shard #18": 1248.0,
-        "shard #19": 1230.0,
-        "shard #20": 1224.0,
-        "shard #21": 1236.0,
-        "shard #22": 1242.0,
-        "shard #23": 1200.0,
-        "shard #24": 1233.0,
+        "shard #0": 1239.0,
+        "shard #1": 1246.0,
+        "shard #2": 1246.0,
+        "shard #3": 1251.0,
+        "shard #4": 1252.0,
+        "shard #5": 1248.0,
+        "shard #6": 1157.0,
+        "shard #7": 1262.0,
+        "shard #8": 1317.0,
+        "shard #9": 1245.0,
+        "shard #10": 1249.0,
+        "shard #11": 1260.0,
+        "shard #12": 1253.0,
+        "shard #13": 1250.0,
+        "shard #14": 1256.0,
+        "shard #15": 1258.0,
+        "shard #16": 1262.0,
+        "shard #17": 1255.0,
+        "shard #18": 1256.0,
+        "shard #19": 1223.0,
+        "shard #20": 1286.0,
+        "shard #21": 1266.0,
+        "shard #22": 1260.0,
+        "shard #23": 1242.0,
+        "shard #24": 1239.0,
         "shard #25": 1543.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel6-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel6-perf-pgo_timing.json
index 0584728..261a2c2 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel6-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel6-perf-pgo_timing.json
@@ -1,6 +1,6 @@
 [
     {
-        "duration": "120.0",
+        "duration": "119.0",
         "name": "jetstream2/JetStream2"
     },
     {
@@ -16,7 +16,7 @@
         "name": "rendering.mobile/amazon_2018"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "rendering.mobile/amazon_mobile_2018"
     },
     {
@@ -32,7 +32,7 @@
         "name": "rendering.mobile/animometer_webgl"
     },
     {
-        "duration": "23.0",
+        "duration": "24.0",
         "name": "rendering.mobile/animometer_webgl_attrib_arrays"
     },
     {
@@ -80,11 +80,11 @@
         "name": "rendering.mobile/balls_css_transition_all_properties"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/balls_javascript_canvas"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/balls_javascript_css"
     },
     {
@@ -128,7 +128,7 @@
         "name": "rendering.mobile/booking.com_mobile_2018"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/bouncing_balls_15"
     },
     {
@@ -160,11 +160,11 @@
         "name": "rendering.mobile/canvas2d_to_texture.html"
     },
     {
-        "duration": "32.0",
+        "duration": "33.0",
         "name": "rendering.mobile/canvas_05000_pixels_per_second"
     },
     {
-        "duration": "32.0",
+        "duration": "33.0",
         "name": "rendering.mobile/canvas_10000_pixels_per_second"
     },
     {
@@ -172,7 +172,7 @@
         "name": "rendering.mobile/canvas_20000_pixels_per_second"
     },
     {
-        "duration": "27.0",
+        "duration": "28.0",
         "name": "rendering.mobile/canvas_40000_pixels_per_second"
     },
     {
@@ -180,7 +180,7 @@
         "name": "rendering.mobile/canvas_60000_pixels_per_second"
     },
     {
-        "duration": "27.0",
+        "duration": "28.0",
         "name": "rendering.mobile/canvas_75000_pixels_per_second"
     },
     {
@@ -200,15 +200,15 @@
         "name": "rendering.mobile/canvas_font_cycler"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/canvas_globalAlpha"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/canvas_lines"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/canvas_to_blob"
     },
     {
@@ -220,7 +220,7 @@
         "name": "rendering.mobile/capitolvolkswagen_mobile_2018"
     },
     {
-        "duration": "15.0",
+        "duration": "16.0",
         "name": "rendering.mobile/card_expansion"
     },
     {
@@ -252,11 +252,11 @@
         "name": "rendering.mobile/cnn_2018"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "rendering.mobile/cnn_article_mobile_2018"
     },
     {
-        "duration": "14.0",
+        "duration": "13.0",
         "name": "rendering.mobile/cnn_mobile_2018"
     },
     {
@@ -312,7 +312,7 @@
         "name": "rendering.mobile/css_animations_staggered_new_element"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_staggered_style_element"
     },
     {
@@ -320,11 +320,11 @@
         "name": "rendering.mobile/css_animations_staggered_updating_class"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_triggered_inline_style"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_triggered_new_element"
     },
     {
@@ -332,7 +332,7 @@
         "name": "rendering.mobile/css_animations_triggered_style_element"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_triggered_updating_class"
     },
     {
@@ -356,7 +356,7 @@
         "name": "rendering.mobile/css_transitions_new_element"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_staggered_inline_style"
     },
     {
@@ -380,7 +380,7 @@
         "name": "rendering.mobile/css_transitions_triggered_inline_style"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_triggered_new_element"
     },
     {
@@ -420,7 +420,7 @@
         "name": "rendering.mobile/css_value_type_path"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_value_type_shadow"
     },
     {
@@ -464,7 +464,7 @@
         "name": "rendering.mobile/dynamic_canvas_to_hw_accelerated_canvas.html"
     },
     {
-        "duration": "23.0",
+        "duration": "24.0",
         "name": "rendering.mobile/dynamic_cube_map"
     },
     {
@@ -520,7 +520,7 @@
         "name": "rendering.mobile/facebook_mobile_2018"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/falling_particle_simulation_cpu.html"
     },
     {
@@ -548,11 +548,11 @@
         "name": "rendering.mobile/flickr_scroll_2018"
     },
     {
-        "duration": "17.0",
+        "duration": "18.0",
         "name": "rendering.mobile/font_wipe"
     },
     {
-        "duration": "17.0",
+        "duration": "18.0",
         "name": "rendering.mobile/forecast.io_mobile_2018"
     },
     {
@@ -560,15 +560,15 @@
         "name": "rendering.mobile/get_image_data_cpu.html"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/get_image_data_gpu.html"
     },
     {
-        "duration": "16.0",
+        "duration": "17.0",
         "name": "rendering.mobile/gmail_2018"
     },
     {
-        "duration": "11.0",
+        "duration": "12.0",
         "name": "rendering.mobile/google_calendar_2018"
     },
     {
@@ -576,7 +576,7 @@
         "name": "rendering.mobile/google_docs_2018"
     },
     {
-        "duration": "18.0",
+        "duration": "19.0",
         "name": "rendering.mobile/google_docs_mobile_2022"
     },
     {
@@ -592,15 +592,15 @@
         "name": "rendering.mobile/google_news_ios"
     },
     {
-        "duration": "11.0",
+        "duration": "12.0",
         "name": "rendering.mobile/google_news_mobile_2018"
     },
     {
-        "duration": "15.0",
+        "duration": "16.0",
         "name": "rendering.mobile/google_plus_2018"
     },
     {
-        "duration": "17.0",
+        "duration": "18.0",
         "name": "rendering.mobile/google_plus_mobile_2018"
     },
     {
@@ -608,7 +608,7 @@
         "name": "rendering.mobile/google_search_mobile_pinch_2018"
     },
     {
-        "duration": "12.0",
+        "duration": "13.0",
         "name": "rendering.mobile/google_web_search_2018"
     },
     {
@@ -620,19 +620,19 @@
         "name": "rendering.mobile/gpu_bound_shader.html"
     },
     {
-        "duration": "18.0",
+        "duration": "19.0",
         "name": "rendering.mobile/gsp.ro_mobile_2018"
     },
     {
-        "duration": "18.0",
+        "duration": "19.0",
         "name": "rendering.mobile/guardian_pathological_2018"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/guimark_vector_chart"
     },
     {
-        "duration": "17.0",
+        "duration": "18.0",
         "name": "rendering.mobile/gws_boogie_expansion"
     },
     {
@@ -640,7 +640,7 @@
         "name": "rendering.mobile/gws_google_expansion"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "rendering.mobile/hakim"
     },
     {
@@ -648,7 +648,7 @@
         "name": "rendering.mobile/horizontal_vertical_expansion"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/hw_accelerated_canvas_to_sw_canvas.html"
     },
     {
@@ -664,7 +664,7 @@
         "name": "rendering.mobile/idle_power_css_animation"
     },
     {
-        "duration": "36.0",
+        "duration": "37.0",
         "name": "rendering.mobile/idle_power_request_animation_frame"
     },
     {
@@ -692,7 +692,7 @@
         "name": "rendering.mobile/infinite_scroll_element_n_layers_75"
     },
     {
-        "duration": "22.0",
+        "duration": "23.0",
         "name": "rendering.mobile/infinite_scroll_element_n_layers_99"
     },
     {
@@ -744,27 +744,27 @@
         "name": "rendering.mobile/js_opacity_plus_n_layers_0"
     },
     {
-        "duration": "23.0",
+        "duration": "24.0",
         "name": "rendering.mobile/js_opacity_plus_n_layers_75"
     },
     {
-        "duration": "23.0",
+        "duration": "24.0",
         "name": "rendering.mobile/js_opacity_plus_n_layers_99"
     },
     {
-        "duration": "23.0",
+        "duration": "24.0",
         "name": "rendering.mobile/js_paint_plus_n_layers_0"
     },
     {
-        "duration": "23.0",
+        "duration": "24.0",
         "name": "rendering.mobile/js_paint_plus_n_layers_75"
     },
     {
-        "duration": "23.0",
+        "duration": "24.0",
         "name": "rendering.mobile/js_paint_plus_n_layers_99"
     },
     {
-        "duration": "23.0",
+        "duration": "24.0",
         "name": "rendering.mobile/js_poster_circle"
     },
     {
@@ -772,7 +772,7 @@
         "name": "rendering.mobile/js_scroll_text_only"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/kevs_3d"
     },
     {
@@ -788,19 +788,19 @@
         "name": "rendering.mobile/latimes_pathological_2018"
     },
     {
-        "duration": "16.0",
+        "duration": "17.0",
         "name": "rendering.mobile/linkedin_2018"
     },
     {
-        "duration": "31.0",
+        "duration": "32.0",
         "name": "rendering.mobile/linkedin_mobile_2018"
     },
     {
-        "duration": "28.0",
+        "duration": "29.0",
         "name": "rendering.mobile/linkedin_mobile_pinch_2018"
     },
     {
-        "duration": "26.0",
+        "duration": "27.0",
         "name": "rendering.mobile/linkedin_pathological_2018"
     },
     {
@@ -812,7 +812,7 @@
         "name": "rendering.mobile/list_recycle_transform"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_0fps_impl_60fps"
     },
     {
@@ -840,15 +840,15 @@
         "name": "rendering.mobile/main_30fps_impl_0fps"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_30fps_impl_60fps"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_60fps_impl_0fps"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_60fps_impl_60fps"
     },
     {
@@ -860,23 +860,23 @@
         "name": "rendering.mobile/main_60fps_impl_60fps_no_update_jank"
     },
     {
-        "duration": "36.0",
+        "duration": "37.0",
         "name": "rendering.mobile/main_60fps_with_extreme_jank_impl_0fps"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_60fps_with_jank_and_delay_impl_60fps"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_60fps_with_jank_impl_0fps"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_animations_half_presented"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/man_in_blue"
     },
     {
@@ -884,7 +884,7 @@
         "name": "rendering.mobile/many_images"
     },
     {
-        "duration": "23.0",
+        "duration": "24.0",
         "name": "rendering.mobile/many_planets_deep"
     },
     {
@@ -892,7 +892,7 @@
         "name": "rendering.mobile/maps_perf_test"
     },
     {
-        "duration": "16.0",
+        "duration": "17.0",
         "name": "rendering.mobile/mask_transition_animation"
     },
     {
@@ -908,11 +908,11 @@
         "name": "rendering.mobile/megi_dish"
     },
     {
-        "duration": "58.0",
+        "duration": "59.0",
         "name": "rendering.mobile/microgame_fps"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/microsoft_asteroid_belt"
     },
     {
@@ -920,15 +920,15 @@
         "name": "rendering.mobile/microsoft_fireflies"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/microsoft_fish_ie_tank"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/microsoft_performance"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/microsoft_snow"
     },
     {
@@ -944,11 +944,11 @@
         "name": "rendering.mobile/microsoft_video_city"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/microsoft_worker_fountains"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/mix_10k"
     },
     {
@@ -968,7 +968,7 @@
         "name": "rendering.mobile/mix_blend_mode_animation_screen"
     },
     {
-        "duration": "18.0",
+        "duration": "19.0",
         "name": "rendering.mobile/mlb_mobile_2018"
     },
     {
@@ -976,11 +976,11 @@
         "name": "rendering.mobile/mobile_news_sandbox"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/motion_mark_canvas_fill_shapes"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/motion_mark_canvas_stroke_shapes"
     },
     {
@@ -1016,11 +1016,11 @@
         "name": "rendering.mobile/motionmark_fixed_2_seconds_suits"
     },
     {
-        "duration": "40.0",
+        "duration": "41.0",
         "name": "rendering.mobile/motionmark_ramp_canvas_arcs"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.mobile/motionmark_ramp_canvas_lines"
     },
     {
@@ -1028,11 +1028,11 @@
         "name": "rendering.mobile/motionmark_ramp_design"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.mobile/motionmark_ramp_images"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.mobile/motionmark_ramp_leaves"
     },
     {
@@ -1040,11 +1040,11 @@
         "name": "rendering.mobile/motionmark_ramp_multiply"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "rendering.mobile/motionmark_ramp_paths"
     },
     {
-        "duration": "39.0",
+        "duration": "40.0",
         "name": "rendering.mobile/motionmark_ramp_suits"
     },
     {
@@ -1064,11 +1064,11 @@
         "name": "rendering.mobile/no_op_settimeout"
     },
     {
-        "duration": "17.0",
+        "duration": "18.0",
         "name": "rendering.mobile/no_op_touch_handler"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/no_update_compositor_animation_with_janky_main_animation"
     },
     {
@@ -1088,7 +1088,7 @@
         "name": "rendering.mobile/nyc_gov_scroll_2018"
     },
     {
-        "duration": "18.0",
+        "duration": "19.0",
         "name": "rendering.mobile/nytimes_mobile_2018"
     },
     {
@@ -1104,7 +1104,7 @@
         "name": "rendering.mobile/off_screen_main_60fps_jank"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "rendering.mobile/offscreen_animation_no_damage"
     },
     {
@@ -1124,7 +1124,7 @@
         "name": "rendering.mobile/particles"
     },
     {
-        "duration": "17.0",
+        "duration": "16.0",
         "name": "rendering.mobile/pbs_pathological_2018"
     },
     {
@@ -1208,7 +1208,7 @@
         "name": "rendering.mobile/silk_finance"
     },
     {
-        "duration": "17.0",
+        "duration": "18.0",
         "name": "rendering.mobile/simple_text_page"
     },
     {
@@ -1216,7 +1216,7 @@
         "name": "rendering.mobile/simple_touch_drag"
     },
     {
-        "duration": "58.0",
+        "duration": "59.0",
         "name": "rendering.mobile/skelebuddies_wasm_2020"
     },
     {
@@ -1228,7 +1228,7 @@
         "name": "rendering.mobile/small_texture_uploads"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "rendering.mobile/smash_cat"
     },
     {
@@ -1280,7 +1280,7 @@
         "name": "rendering.mobile/text_10000_pixels_per_second"
     },
     {
-        "duration": "29.0",
+        "duration": "30.0",
         "name": "rendering.mobile/text_20000_pixels_per_second"
     },
     {
@@ -1296,11 +1296,11 @@
         "name": "rendering.mobile/text_75000_pixels_per_second"
     },
     {
-        "duration": "26.0",
+        "duration": "27.0",
         "name": "rendering.mobile/text_90000_pixels_per_second"
     },
     {
-        "duration": "31.0",
+        "duration": "32.0",
         "name": "rendering.mobile/text_constant_full_page_raster_05000_pixels_per_second"
     },
     {
@@ -1320,7 +1320,7 @@
         "name": "rendering.mobile/text_constant_full_page_raster_60000_pixels_per_second"
     },
     {
-        "duration": "26.0",
+        "duration": "27.0",
         "name": "rendering.mobile/text_constant_full_page_raster_75000_pixels_per_second"
     },
     {
@@ -1332,7 +1332,7 @@
         "name": "rendering.mobile/text_fling_05000_pixels_per_second"
     },
     {
-        "duration": "26.0",
+        "duration": "27.0",
         "name": "rendering.mobile/text_fling_10000_pixels_per_second"
     },
     {
@@ -1352,7 +1352,7 @@
         "name": "rendering.mobile/text_hover_20000_pixels_per_second"
     },
     {
-        "duration": "27.0",
+        "duration": "28.0",
         "name": "rendering.mobile/text_hover_40000_pixels_per_second"
     },
     {
@@ -1412,11 +1412,11 @@
         "name": "rendering.mobile/throughput_scrolling_uncomposited"
     },
     {
-        "duration": "54.0",
+        "duration": "55.0",
         "name": "rendering.mobile/tiny_racing_v3_wasm_2020"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/toBlob_duration.html"
     },
     {
@@ -1432,7 +1432,7 @@
         "name": "rendering.mobile/touch_handler_scrolling"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "rendering.mobile/transfer_from_imageBitmap.html"
     },
     {
@@ -1452,11 +1452,11 @@
         "name": "rendering.mobile/twitch_mobile_pinch_2018"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "rendering.mobile/twitter_2018"
     },
     {
-        "duration": "19.0",
+        "duration": "17.0",
         "name": "rendering.mobile/twitter_mobile_2018"
     },
     {
@@ -1464,7 +1464,7 @@
         "name": "rendering.mobile/update_history_state"
     },
     {
-        "duration": "11.0",
+        "duration": "12.0",
         "name": "rendering.mobile/usatoday_mobile_2018"
     },
     {
@@ -1564,7 +1564,7 @@
         "name": "rendering.mobile/webp_decoding_yuv_and_gpu_rasterization"
     },
     {
-        "duration": "19.0",
+        "duration": "21.0",
         "name": "rendering.mobile/wikipedia_2018"
     },
     {
@@ -1596,7 +1596,7 @@
         "name": "rendering.mobile/wowwiki_mobile_2018"
     },
     {
-        "duration": "19.0",
+        "duration": "21.0",
         "name": "rendering.mobile/wsj_mobile_2018"
     },
     {
@@ -1616,7 +1616,7 @@
         "name": "rendering.mobile/yahoo_news_mobile_2018"
     },
     {
-        "duration": "16.0",
+        "duration": "17.0",
         "name": "rendering.mobile/yahoo_sports_2018"
     },
     {
@@ -1628,7 +1628,7 @@
         "name": "rendering.mobile/youtube_2018"
     },
     {
-        "duration": "11.0",
+        "duration": "10.0",
         "name": "rendering.mobile/youtube_mobile_2018"
     },
     {
@@ -1640,11 +1640,11 @@
         "name": "rendering.mobile/zoom_in_animation"
     },
     {
-        "duration": "41.0",
+        "duration": "45.0",
         "name": "speedometer2/Speedometer2"
     },
     {
-        "duration": "50.0",
+        "duration": "51.0",
         "name": "speedometer3/Speedometer3"
     },
     {
@@ -1652,7 +1652,7 @@
         "name": "system_health.common_mobile/background:media:imgur:2019"
     },
     {
-        "duration": "29.0",
+        "duration": "28.0",
         "name": "system_health.common_mobile/background:news:nytimes:2019"
     },
     {
@@ -1676,7 +1676,7 @@
         "name": "system_health.common_mobile/browse:chrome:omnibox:2019"
     },
     {
-        "duration": "67.0",
+        "duration": "68.0",
         "name": "system_health.common_mobile/browse:media:facebook_photos:2019"
     },
     {
@@ -1684,7 +1684,7 @@
         "name": "system_health.common_mobile/browse:media:flickr_infinite_scroll:2019"
     },
     {
-        "duration": "37.0",
+        "duration": "38.0",
         "name": "system_health.common_mobile/browse:media:googleplaystore:2019"
     },
     {
@@ -1700,7 +1700,7 @@
         "name": "system_health.common_mobile/browse:media:youtube:2019"
     },
     {
-        "duration": "62.0",
+        "duration": "61.0",
         "name": "system_health.common_mobile/browse:news:businessinsider:2021"
     },
     {
@@ -1728,7 +1728,7 @@
         "name": "system_health.common_mobile/browse:news:reddit:2019"
     },
     {
-        "duration": "37.0",
+        "duration": "36.0",
         "name": "system_health.common_mobile/browse:news:toi:2019"
     },
     {
@@ -1744,7 +1744,7 @@
         "name": "system_health.common_mobile/browse:search:amp:sxg:2019"
     },
     {
-        "duration": "48.0",
+        "duration": "47.0",
         "name": "system_health.common_mobile/browse:shopping:amazon:2019"
     },
     {
@@ -1764,7 +1764,7 @@
         "name": "system_health.common_mobile/browse:social:facebook:2019"
     },
     {
-        "duration": "67.0",
+        "duration": "68.0",
         "name": "system_health.common_mobile/browse:social:facebook_infinite_scroll:2018"
     },
     {
@@ -1816,7 +1816,7 @@
         "name": "system_health.common_mobile/load:media:facebook_feed:mobile:2020"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "system_health.common_mobile/load:media:facebook_photos:2019"
     },
     {
@@ -1836,11 +1836,11 @@
         "name": "system_health.common_mobile/load:media:imgur:2018"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "system_health.common_mobile/load:media:soundcloud:2018"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "system_health.common_mobile/load:media:youtube:2018"
     },
     {
@@ -1848,7 +1848,7 @@
         "name": "system_health.common_mobile/load:news:bbc:2019"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "system_health.common_mobile/load:news:cnn:2020"
     },
     {
@@ -1892,7 +1892,7 @@
         "name": "system_health.common_mobile/load:search:google:2018"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "system_health.common_mobile/load:search:naver:2023"
     },
     {
@@ -1900,7 +1900,7 @@
         "name": "system_health.common_mobile/load:search:taobao:2019"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "system_health.common_mobile/load:search:yahoo:2018"
     },
     {
diff --git a/tools/perf/cross_device_test_config.py b/tools/perf/cross_device_test_config.py
index 1bec1d8..8e63e57 100644
--- a/tools/perf/cross_device_test_config.py
+++ b/tools/perf/cross_device_test_config.py
@@ -92,6 +92,9 @@
         'speedometer2': {
             'Speedometer2': 20,
         },
+        'speedometer2-predictable': {
+            'Speedometer2': 20,
+        },
     },
     'linux-perf': {
         'jetstream2': {
@@ -112,6 +115,9 @@
         'speedometer2-nominorms': {
             'Speedometer2': 20,
         },
+        'speedometer2-predictable': {
+            'Speedometer2': 20,
+        },
         'speedometer3': {
             'Speedometer3': 20,
         },
diff --git a/tools/perf/page_sets/rendering/tough_scrolling_cases.py b/tools/perf/page_sets/rendering/tough_scrolling_cases.py
index 6e76811..f6e94b28 100644
--- a/tools/perf/page_sets/rendering/tough_scrolling_cases.py
+++ b/tools/perf/page_sets/rendering/tough_scrolling_cases.py
@@ -18,6 +18,8 @@
   SYNTHETIC_GESTURE_SOURCE = page_action.GESTURE_SOURCE_DEFAULT
   TAGS = [story_tags.GPU_RASTERIZATION, story_tags.TOUGH_SCROLLING]
   USE_FLING_SCROLL = False
+  VSYNC_OFFSET_US = 0
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_DEFAULT
 
   def __init__(self,
                page_set,
@@ -53,7 +55,9 @@
             action_runner.ScrollPage(
                 direction=direction,
                 speed_in_pixels_per_second=self.SPEED_IN_PIXELS_PER_SECOND,
-                synthetic_gesture_source=self.SYNTHETIC_GESTURE_SOURCE)
+                synthetic_gesture_source=self.SYNTHETIC_GESTURE_SOURCE,
+                vsync_offset_ms=self.VSYNC_OFFSET_US * 0.001,
+                input_event_pattern=self.INPUT_EVENT_PATTERN)
           else:
             # When there is a `selector` specified, scroll just that particular
             # element, rather than the entire page.
@@ -61,7 +65,9 @@
                 selector=selector,
                 direction=direction,
                 speed_in_pixels_per_second=self.SPEED_IN_PIXELS_PER_SECOND,
-                synthetic_gesture_source=self.SYNTHETIC_GESTURE_SOURCE)
+                synthetic_gesture_source=self.SYNTHETIC_GESTURE_SOURCE,
+                vsync_offset_ms=self.VSYNC_OFFSET_US * 0.001,
+                input_event_pattern=self.INPUT_EVENT_PATTERN)
         direction = 'up' if direction == 'down' else 'down'
 
 
@@ -280,3 +286,76 @@
   URL = 'file://../tough_scrolling_cases/text_on_non_opaque_background.html'
   SELECTOR = '#scroll'
   SPEED_IN_PIXELS_PER_SECOND = 50
+
+
+class ScrollingTextInputOnePerVsyncPlus0Us(ToughFastScrollingPage):
+  BASE_NAME = 'text_scroll_input_one_per_vsync_plus_0us'
+  URL = 'file://../tough_scrolling_cases/text.html'
+  SPEED_IN_PIXELS_PER_SECOND = 1000
+  VSYNC_OFFSET_US = 0
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_ONE_PER_VSYNC
+
+
+# Test scrolling with different input event timings.
+class ScrollingTextInputOnePerVsyncMinus300UsPage(ToughFastScrollingPage):
+  BASE_NAME = 'text_scroll_input_one_per_vsync_minus_300us'
+  URL = 'file://../tough_scrolling_cases/text.html'
+  SPEED_IN_PIXELS_PER_SECOND = 1000
+  VSYNC_OFFSET_US = -300
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_ONE_PER_VSYNC
+
+
+class ScrollingTextInputOnePerVsyncMinus1000UsPage(ToughFastScrollingPage):
+  BASE_NAME = 'text_scroll_input_one_per_vsync_minus_1000us'
+  URL = 'file://../tough_scrolling_cases/text.html'
+  SPEED_IN_PIXELS_PER_SECOND = 1000
+  VSYNC_OFFSET_US = -1000
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_ONE_PER_VSYNC
+
+
+class ScrollingTextInputOnePerVsyncMinus3000UsPage(ToughFastScrollingPage):
+  BASE_NAME = 'text_scroll_input_one_per_vsync_minus_3000us'
+  URL = 'file://../tough_scrolling_cases/text.html'
+  SPEED_IN_PIXELS_PER_SECOND = 1000
+  VSYNC_OFFSET_US = -3000
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_ONE_PER_VSYNC
+
+
+class ScrollingTextInputOnePerVsyncPlus300UsPage(ToughFastScrollingPage):
+  BASE_NAME = 'text_scroll_input_one_per_vsync_plus_300us'
+  URL = 'file://../tough_scrolling_cases/text.html'
+  SPEED_IN_PIXELS_PER_SECOND = 1000
+  VSYNC_OFFSET_US = 300
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_ONE_PER_VSYNC
+
+
+class ScrollingTextInputOnePerVsyncPlus1000UsPage(ToughFastScrollingPage):
+  BASE_NAME = 'text_scroll_input_one_per_vsync_plus_1000us'
+  URL = 'file://../tough_scrolling_cases/text.html'
+  SPEED_IN_PIXELS_PER_SECOND = 1000
+  VSYNC_OFFSET_US = 1000
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_ONE_PER_VSYNC
+
+
+class ScrollingTextInputOnePerVsyncPlus3000UsPage(ToughFastScrollingPage):
+  BASE_NAME = 'text_scroll_input_one_per_vsync_plus_3000us'
+  URL = 'file://../tough_scrolling_cases/text.html'
+  SPEED_IN_PIXELS_PER_SECOND = 1000
+  VSYNC_OFFSET_US = 3000
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_ONE_PER_VSYNC
+
+
+class ScrollingTextInputTwoPerVsyncPlus0Us(ToughFastScrollingPage):
+  BASE_NAME = 'text_scroll_input_two_per_vsync_plus_0us'
+  URL = 'file://../tough_scrolling_cases/text.html'
+  SPEED_IN_PIXELS_PER_SECOND = 1000
+  VSYNC_OFFSET_US = 0
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_TWO_PER_VSYNC
+
+
+class ScrollingTextInputEveryOtherVsyncPlus0Us(ToughFastScrollingPage):
+  BASE_NAME = 'text_scroll_input_every_other_vsync_plus_0us'
+  URL = 'file://../tough_scrolling_cases/text.html'
+  SPEED_IN_PIXELS_PER_SECOND = 1000
+  VSYNC_OFFSET_US = 0
+  INPUT_EVENT_PATTERN = page_action.INPUT_EVENT_PATTERN_EVERY_OTHER_VSYNC
diff --git a/tools/typescript/path_mappings.py b/tools/typescript/path_mappings.py
index 442b480..9750d19 100644
--- a/tools/typescript/path_mappings.py
+++ b/tools/typescript/path_mappings.py
@@ -32,8 +32,8 @@
       "cr_components/managed_dialog",
       "cr_components/managed_footnote",
       "cr_components/most_visited",
-      "cr_components/omnibox",
       "cr_components/page_image_service",
+      "cr_components/searchbox",
       "cr_components/settings_prefs",
       "cr_components/theme_color_picker",
   ]
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index e00a4623..e3785671 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -45,7 +45,7 @@
 
 BASE_FEATURE(kAugmentExistingImageLabels,
              "AugmentExistingImageLabels",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 bool IsAugmentExistingImageLabelsEnabled() {
   return base::FeatureList::IsEnabled(::features::kAugmentExistingImageLabels);
 }
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
index bdd81f02..40e25957 100644
--- a/ui/accessibility/ax_event_generator.cc
+++ b/ui/accessibility/ax_event_generator.cc
@@ -488,7 +488,6 @@
       AddEvent(node, Event::CHECKED_STATE_DESCRIPTION_CHANGED);
       break;
     case ax::mojom::StringAttribute::kClassName:
-      AddEvent(node, Event::CLASS_NAME_CHANGED);
       break;
     case ax::mojom::StringAttribute::kDescription:
       AddEvent(node, Event::DESCRIPTION_CHANGED);
@@ -1320,8 +1319,6 @@
       return "checkedStateDescriptionChanged";
     case AXEventGenerator::Event::CHILDREN_CHANGED:
       return "childrenChanged";
-    case AXEventGenerator::Event::CLASS_NAME_CHANGED:
-      return "classNameChanged";
     case AXEventGenerator::Event::COLLAPSED:
       return "collapsed";
     case AXEventGenerator::Event::CONTROLS_CHANGED:
diff --git a/ui/accessibility/ax_event_generator.h b/ui/accessibility/ax_event_generator.h
index 0be1046..e0ba9b5 100644
--- a/ui/accessibility/ax_event_generator.h
+++ b/ui/accessibility/ax_event_generator.h
@@ -49,7 +49,6 @@
     CHECKED_STATE_CHANGED,
     CHECKED_STATE_DESCRIPTION_CHANGED,
     CHILDREN_CHANGED,
-    CLASS_NAME_CHANGED,
     COLLAPSED,
     CONTROLS_CHANGED,
     DETAILS_CHANGED,
diff --git a/ui/accessibility/ax_event_generator_unittest.cc b/ui/accessibility/ax_event_generator_unittest.cc
index 9b5aa417..3dce2b97 100644
--- a/ui/accessibility/ax_event_generator_unittest.cc
+++ b/ui/accessibility/ax_event_generator_unittest.cc
@@ -1991,7 +1991,6 @@
       event_generator,
       UnorderedElementsAre(
           HasEventAtNode(AXEventGenerator::Event::ACCESS_KEY_CHANGED, 2),
-          HasEventAtNode(AXEventGenerator::Event::CLASS_NAME_CHANGED, 3),
           HasEventAtNode(AXEventGenerator::Event::KEY_SHORTCUTS_CHANGED, 4),
           HasEventAtNode(AXEventGenerator::Event::LANGUAGE_CHANGED, 5),
           HasEventAtNode(AXEventGenerator::Event::PLACEHOLDER_CHANGED, 6)));
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index fc84b5f..19ae0880 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -2219,12 +2219,13 @@
             ax::mojom::IntAttribute::kActivedescendantId)) {
       return true;
     }
-    // Check for an ancestor listbox that is controlled by a textfield combobox
-    // that also has an aria-activedescendant.
-    // Note: blink will map aria-owns to aria-controls in the textfield combobox
-    // case as it was the older technique, but treating as an actual aria-owns
-    // makes no sense as a textfield cannot have children.
-    if (ancestor_node->GetRole() == ax::mojom::Role::kListBox) {
+    // Check for an ancestor listbox/tree/grid/treegrid/dialog that is
+    // controlled by a textfield combobox that also has an
+    // aria-activedescendant. Note: blink will map aria-owns to aria-controls in
+    // the textfield combobox case as it was the older technique, but treating
+    // as an actual aria-owns makes no sense as a textfield cannot have
+    // children.
+    if (ui::IsComboBoxContainer(ancestor_node->GetRole())) {
       std::set<AXNodeID> nodes_that_control_this_list =
           tree()->GetReverseRelations(ax::mojom::IntListAttribute::kControlsIds,
                                       ancestor_node->id());
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc
index 54d6a83..25ab5c1 100644
--- a/ui/accessibility/ax_role_properties.cc
+++ b/ui/accessibility/ax_role_properties.cc
@@ -179,6 +179,7 @@
     case ax::mojom::Role::kGrid:
     case ax::mojom::Role::kListBox:
     case ax::mojom::Role::kTree:
+    case ax::mojom::Role::kTreeGrid:
       return true;
     default:
       return false;
diff --git a/ui/accessibility/platform/automation/automation_api_util.cc b/ui/accessibility/platform/automation/automation_api_util.cc
index dcff9df..83e59da 100644
--- a/ui/accessibility/platform/automation/automation_api_util.cc
+++ b/ui/accessibility/platform/automation/automation_api_util.cc
@@ -113,7 +113,6 @@
     case AXEventGenerator::Event::CHECKED_STATE_CHANGED:
     case AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
     case AXEventGenerator::Event::CHILDREN_CHANGED:
-    case AXEventGenerator::Event::CLASS_NAME_CHANGED:
     case AXEventGenerator::Event::COLLAPSED:
     case AXEventGenerator::Event::CONTROLS_CHANGED:
     case AXEventGenerator::Event::DETAILS_CHANGED:
diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
index 36bc114..71361f6b 100644
--- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
@@ -672,7 +672,8 @@
 
     // Helper to get the android Window. Always null for application context. Need to null check
     // result returning value.
-    Window getWindow() {
+    @Nullable
+    public Window getWindow() {
         Activity activity = ContextUtils.activityFromContext(mContextRef.get());
         if (activity == null || activity.isFinishing()) return null;
         return activity.getWindow();
diff --git a/ui/aura/OWNERS b/ui/aura/OWNERS
index f9d655a1..11972f4 100644
--- a/ui/aura/OWNERS
+++ b/ui/aura/OWNERS
@@ -1,5 +1,6 @@
-sky@chromium.org
+edcourtney@chromium.org
 oshima@chromium.org
+sky@chromium.org
 
 per-file *x11.cc=thomasanderson@chromium.org
 per-file *x11.h=thomasanderson@chromium.org
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc
index 33b86e5..f20ae99 100644
--- a/ui/aura/window_tree_host.cc
+++ b/ui/aura/window_tree_host.cc
@@ -541,9 +541,7 @@
       ui::IsPixelCanvasRecordingEnabled(), use_external_begin_frame_control,
       force_software_compositor, enable_compositing_based_throttling,
       memory_limit_when_visible_mb);
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   compositor_->AddObserver(this);
-#endif
   if (!dispatcher()) {
     window()->Init(ui::LAYER_NOT_DRAWN);
     window()->set_host(this);
@@ -792,18 +790,26 @@
 }
 
 void WindowTreeHost::OnCompositingEnded(ui::Compositor* compositor) {
+  // Currently, input is only throttled on ash and is not well supported on
+  // other platforms. See crbug.com/41359082.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   if (!holding_pointer_moves_)
     return;
 
   dispatcher_->ReleasePointerMoves();
   holding_pointer_moves_ = false;
+#endif
 }
 
 void WindowTreeHost::OnCompositingChildResizing(ui::Compositor* compositor) {
+  // Currently, input is only throttled on ash and is not well supported on
+  // other platforms. See crbug.com/41359082.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   if (!Env::GetInstance()->throttle_input_on_resize() || holding_pointer_moves_)
     return;
   dispatcher_->HoldPointerMoves();
   holding_pointer_moves_ = true;
+#endif
 }
 
 void WindowTreeHost::OnFrameSinksToThrottleUpdated(
diff --git a/ui/base/clipboard/clipboard.cc b/ui/base/clipboard/clipboard.cc
index 8aee368..0eaf1a21 100644
--- a/ui/base/clipboard/clipboard.cc
+++ b/ui/base/clipboard/clipboard.cc
@@ -387,8 +387,6 @@
   return false;
 }
 
-void Clipboard::MarkAsConfidential() {}
-
 void Clipboard::ReadAvailableTypes(ClipboardBuffer buffer,
                                    const DataTransferEndpoint* data_dst,
                                    ReadAvailableTypesCallback callback) const {
diff --git a/ui/base/clipboard/clipboard.h b/ui/base/clipboard/clipboard.h
index 0282ae1..5dffa3a 100644
--- a/ui/base/clipboard/clipboard.h
+++ b/ui/base/clipboard/clipboard.h
@@ -178,11 +178,6 @@
   // manipulated.
   virtual bool IsMarkedByOriginatorAsConfidential() const;
 
-  // Mark the data on the clipboard as being confidential. This isn't
-  // implemented for all platforms yet, but this call should be made on every
-  // platform so that when it is implemented on other platforms it is picked up.
-  virtual void MarkAsConfidential();
-
   // Clear the clipboard data.
   virtual void Clear(ClipboardBuffer buffer) = 0;
 
diff --git a/ui/base/clipboard/clipboard_mac.h b/ui/base/clipboard/clipboard_mac.h
index cf78e46..266cf8c 100644
--- a/ui/base/clipboard/clipboard_mac.h
+++ b/ui/base/clipboard/clipboard_mac.h
@@ -46,7 +46,6 @@
                          ClipboardBuffer buffer,
                          const DataTransferEndpoint* data_dst) const override;
   bool IsMarkedByOriginatorAsConfidential() const override;
-  void MarkAsConfidential() override;
   void Clear(ClipboardBuffer buffer) override;
   void ReadAvailableTypes(ClipboardBuffer buffer,
                           const DataTransferEndpoint* data_dst,
@@ -119,7 +118,8 @@
       const ObjectMap& objects,
       std::vector<Clipboard::PlatformRepresentation> platform_representations,
       std::unique_ptr<DataTransferEndpoint> data_src,
-      NSPasteboard* pasteboard);
+      NSPasteboard* pasteboard,
+      uint32_t privacy_types);
 
   // Mapping of OS-provided sequence number to a unique token.
   mutable struct {
diff --git a/ui/base/clipboard/clipboard_mac.mm b/ui/base/clipboard/clipboard_mac.mm
index de04e59..88cbcd0d1 100644
--- a/ui/base/clipboard/clipboard_mac.mm
+++ b/ui/base/clipboard/clipboard_mac.mm
@@ -200,12 +200,6 @@
   return false;
 }
 
-void ClipboardMac::MarkAsConfidential() {
-  DCHECK(CalledOnValidThread());
-
-  [GetPasteboard() setData:nil forType:kUTTypeConfidentialData];
-}
-
 void ClipboardMac::Clear(ClipboardBuffer buffer) {
   ClearInternal(buffer, GetPasteboard());
 }
@@ -448,7 +442,7 @@
     uint32_t privacy_types) {
   WritePortableAndPlatformRepresentationsInternal(
       buffer, objects, std::move(platform_representations), std::move(data_src),
-      GetPasteboard());
+      GetPasteboard(), privacy_types);
 }
 
 void ClipboardMac::WritePortableAndPlatformRepresentationsInternal(
@@ -456,7 +450,8 @@
     const ObjectMap& objects,
     std::vector<Clipboard::PlatformRepresentation> platform_representations,
     std::unique_ptr<DataTransferEndpoint> data_src,
-    NSPasteboard* pasteboard) {
+    NSPasteboard* pasteboard,
+    uint32_t privacy_types) {
   DCHECK(CalledOnValidThread());
   DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
 
@@ -470,6 +465,9 @@
     [pasteboard setString:base::SysUTF8ToNSString(data_src->GetURL()->spec())
                   forType:kUTTypeChromiumSourceURL];
   }
+  if (privacy_types & Clipboard::PrivacyTypes::kNoDisplay) {
+    WriteConfidentialDataForPassword();
+  }
 }
 
 void ClipboardMac::WriteText(base::StringPiece text) {
@@ -529,7 +527,9 @@
 }
 
 void ClipboardMac::WriteConfidentialDataForPassword() {
-  // TODO(crbug.com/40945200): Add support for this.
+  DCHECK(CalledOnValidThread());
+
+  [GetPasteboard() setData:nil forType:kUTTypeConfidentialData];
 }
 
 // Write an extra flavor that signifies WebKit was the last to modify the
diff --git a/ui/base/clipboard/clipboard_mac_unittest.mm b/ui/base/clipboard/clipboard_mac_unittest.mm
index 29a3b43..39d13c2 100644
--- a/ui/base/clipboard/clipboard_mac_unittest.mm
+++ b/ui/base/clipboard/clipboard_mac_unittest.mm
@@ -109,7 +109,8 @@
       NSPasteboard* pasteboard) {
     clipboard_mac->WritePortableAndPlatformRepresentationsInternal(
         ClipboardBuffer::kCopyPaste, /*objects=*/{},
-        /*platform_representations=*/{}, std::move(data_src), pasteboard);
+        /*platform_representations=*/{}, std::move(data_src), pasteboard,
+        /*privacy_types=*/0);
   }
 
  private:
diff --git a/ui/base/clipboard/scoped_clipboard_writer.cc b/ui/base/clipboard/scoped_clipboard_writer.cc
index 905a182a..6022a0af 100644
--- a/ui/base/clipboard/scoped_clipboard_writer.cc
+++ b/ui/base/clipboard/scoped_clipboard_writer.cc
@@ -56,23 +56,10 @@
   }
 
   if (!objects_.empty() || !platform_representations_.empty()) {
-    uint32_t privacy_types = 0;
-    if (confidential_) {
-      privacy_types |= Clipboard::PrivacyTypes::kNoDisplay;
-    }
-    if (off_the_record_) {
-      privacy_types |= Clipboard::PrivacyTypes::kNoLocalClipboardHistory;
-      privacy_types |= Clipboard::PrivacyTypes::kNoCloudClipboard;
-    }
     Clipboard::GetForCurrentThread()->WritePortableAndPlatformRepresentations(
         buffer_, objects_, std::move(platform_representations_),
-        std::move(data_src_), privacy_types);
+        std::move(data_src_), privacy_types_);
   }
-
-  // TODO(snianu): Remove this code and move this
-  // to `WritePortableAndPlatformRepresentations`.
-  if (confidential_)
-    Clipboard::GetForCurrentThread()->MarkAsConfidential();
 }
 
 void ScopedClipboardWriter::SetDataSource(
@@ -182,11 +169,12 @@
 }
 
 void ScopedClipboardWriter::MarkAsConfidential() {
-  confidential_ = true;
+  privacy_types_ |= Clipboard::PrivacyTypes::kNoDisplay;
 }
 
 void ScopedClipboardWriter::MarkAsOffTheRecord() {
-  off_the_record_ = true;
+  privacy_types_ |= Clipboard::PrivacyTypes::kNoLocalClipboardHistory;
+  privacy_types_ |= Clipboard::PrivacyTypes::kNoCloudClipboard;
 }
 
 void ScopedClipboardWriter::WritePickledData(
@@ -245,7 +233,7 @@
   objects_.clear();
   platform_representations_.clear();
   registered_formats_.clear();
-  confidential_ = false;
+  privacy_types_ = 0;
   counter_ = 0;
 }
 
diff --git a/ui/base/clipboard/scoped_clipboard_writer.h b/ui/base/clipboard/scoped_clipboard_writer.h
index c480c18..08fce03 100644
--- a/ui/base/clipboard/scoped_clipboard_writer.h
+++ b/ui/base/clipboard/scoped_clipboard_writer.h
@@ -119,8 +119,10 @@
   // The type is set at construction, and can be changed before committing.
   const ClipboardBuffer buffer_;
 
-  bool confidential_ = false;
-  bool off_the_record_ = false;
+  // Contains the `Clipboard::PrivacyTypes` based on whether the content was
+  // marked as confidential or off the record. e.g. password is considered as
+  // confidential that should be concealed.
+  uint32_t privacy_types_ = 0;
 
   // The source of the data written in ScopedClipboardWriter, nullptr means it's
   // not set, or the source of the data can't be represented by
diff --git a/ui/compositor/BUILD.gn b/ui/compositor/BUILD.gn
index 1d26297..b38019d2 100644
--- a/ui/compositor/BUILD.gn
+++ b/ui/compositor/BUILD.gn
@@ -31,6 +31,8 @@
     "debug_utils.h",
     "float_animation_curve_adapter.cc",
     "float_animation_curve_adapter.h",
+    "host_begin_frame_observer.cc",
+    "host_begin_frame_observer.h",
     "layer.cc",
     "layer.h",
     "layer_animation_delegate.h",
diff --git a/ui/compositor/DEPS b/ui/compositor/DEPS
index 28acae1..95a7b103 100644
--- a/ui/compositor/DEPS
+++ b/ui/compositor/DEPS
@@ -3,7 +3,7 @@
   "+components/viz/common",
   "+components/viz/host",
   "+gpu/command_buffer/client/gles2_interface.h",
-  "+mojo/public/cpp/bindings/pending_remote.h",
+  "+mojo/public/cpp/bindings",
   "+services/viz/privileged/mojom/compositing",
   "+skia/ext",
   "+third_party/khronos/GLES2/gl2.h",
@@ -17,12 +17,6 @@
 ]
 
 specific_include_rules = {
-  "compositor.cc": [
-    "+mojo/public/cpp/bindings",
-  ],
-  "compositor.h": [
-    "+mojo/public/cpp/bindings",
-  ],
   "run_all_unittests\.cc": [
     "+mojo/core/embedder",
   ],
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 58c898e..d2c2b79 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -528,11 +528,25 @@
 }
 
 void Compositor::SetVisible(bool visible) {
+  const bool changed = visible != IsVisible();
+  if (changed) {
+    for (auto& observer : observer_list_) {
+      observer.OnCompositorVisibilityChanging(this, visible);
+    }
+  }
+
   host_->SetVisible(visible);
   // Visibility is reset when the output surface is lost, so this must also be
-  // updated then.
+  // updated then. We need to call this even if the visibility hasn't changed,
+  // for the same reason.
   if (display_private_)
     display_private_->SetDisplayVisible(visible);
+
+  if (changed) {
+    for (auto& observer : observer_list_) {
+      observer.OnCompositorVisibilityChanged(this, visible);
+    }
+  }
 }
 
 bool Compositor::IsVisible() {
diff --git a/ui/compositor/compositor_observer.h b/ui/compositor/compositor_observer.h
index 9399d5f..8bfff66d 100644
--- a/ui/compositor/compositor_observer.h
+++ b/ui/compositor/compositor_observer.h
@@ -79,6 +79,15 @@
 
   // Called at the end of the BeginMainFrame.
   virtual void OnDidBeginMainFrame(Compositor* compositor) {}
+
+  // Called when the compositor visibility is about to change, but before it is
+  // changed.
+  virtual void OnCompositorVisibilityChanging(Compositor* compositor,
+                                              bool visible) {}
+
+  // Called when the compositor visibility has changed.
+  virtual void OnCompositorVisibilityChanged(Compositor* compositor,
+                                             bool visible) {}
 };
 
 }  // namespace ui
diff --git a/ui/compositor/host_begin_frame_observer.cc b/ui/compositor/host_begin_frame_observer.cc
new file mode 100644
index 0000000..db66cb8
--- /dev/null
+++ b/ui/compositor/host_begin_frame_observer.cc
@@ -0,0 +1,71 @@
+// 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/compositor/host_begin_frame_observer.h"
+
+#include "base/logging.h"
+#include "base/task/common/task_annotator.h"
+#include "base/time/time.h"
+
+namespace ui {
+
+HostBeginFrameObserver::HostBeginFrameObserver(
+    const base::flat_set<raw_ptr<SimpleBeginFrameObserver, CtnExperimental>>&
+        observers,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : simple_begin_frame_observers_(observers),
+      task_runner_(std::move(task_runner)) {}
+
+HostBeginFrameObserver::~HostBeginFrameObserver() = default;
+
+void HostBeginFrameObserver::OnStandaloneBeginFrame(
+    const viz::BeginFrameArgs& args) {
+  // Mark the current task as interesting, as it maybe be responsible for
+  // handling input events for flings.
+  base::TaskAnnotator::MarkCurrentTaskAsInterestingForTracing();
+  if (args.type == viz::BeginFrameArgs::MISSED) {
+    return;
+  }
+
+  if (pending_coalesce_callback_) {
+    begin_frame_args_ = args;
+    return;
+  }
+
+  if ((base::TimeTicks::Now() - args.frame_time) > args.interval) {
+    begin_frame_args_ = args;
+    pending_coalesce_callback_ = true;
+    task_runner_->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&HostBeginFrameObserver::CoalescedBeginFrame,
+                       weak_factory_.GetWeakPtr()),
+        base::Microseconds(1));
+    return;
+  }
+
+  CallObservers(args);
+}
+
+mojo::PendingRemote<viz::mojom::BeginFrameObserver>
+HostBeginFrameObserver::GetBoundRemote() {
+  return receiver_.BindNewPipeAndPassRemote(task_runner_);
+}
+
+void HostBeginFrameObserver::CoalescedBeginFrame() {
+  DCHECK(begin_frame_args_.IsValid());
+  pending_coalesce_callback_ = false;
+  viz::BeginFrameArgs args = begin_frame_args_;
+  begin_frame_args_ = viz::BeginFrameArgs();
+  CallObservers(args);
+}
+
+// This may be deleted as part of `CallObservers`.
+void HostBeginFrameObserver::CallObservers(const viz::BeginFrameArgs& args) {
+  auto observers_copy = *simple_begin_frame_observers_;
+  for (SimpleBeginFrameObserver* simple_observer : observers_copy) {
+    simple_observer->OnBeginFrame(args.frame_time, args.interval);
+  }
+}
+
+}  // namespace ui
diff --git a/ui/compositor/host_begin_frame_observer.h b/ui/compositor/host_begin_frame_observer.h
new file mode 100644
index 0000000..db801c0
--- /dev/null
+++ b/ui/compositor/host_begin_frame_observer.h
@@ -0,0 +1,63 @@
+// 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 UI_COMPOSITOR_HOST_BEGIN_FRAME_OBSERVER_H_
+#define UI_COMPOSITOR_HOST_BEGIN_FRAME_OBSERVER_H_
+
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "services/viz/privileged/mojom/compositing/begin_frame_observer.mojom.h"
+#include "ui/compositor/compositor_export.h"
+
+namespace base {
+class TimeTicks;
+class TimeDelta;
+}  // namespace base
+
+namespace ui {
+
+// A BeginFrameObserver implementation that forwards begin frame messages to
+// registered sub-SimpleBeginFrameObservers.
+//
+// In case the host thread is slow, frame message scan get queued up.
+// Provides coalescing of begin frame messages in this case, by gathering
+// pending messages into a single message call.
+class COMPOSITOR_EXPORT HostBeginFrameObserver
+    : public viz::mojom::BeginFrameObserver {
+ public:
+  class SimpleBeginFrameObserver {
+   public:
+    virtual ~SimpleBeginFrameObserver() = default;
+    virtual void OnBeginFrame(base::TimeTicks frame_begin_time,
+                              base::TimeDelta frame_interval) = 0;
+    virtual void OnBeginFrameSourceShuttingDown() = 0;
+  };
+
+  HostBeginFrameObserver(
+      const base::flat_set<raw_ptr<SimpleBeginFrameObserver, CtnExperimental>>&
+          observers,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  ~HostBeginFrameObserver() override;
+
+  void OnStandaloneBeginFrame(const viz::BeginFrameArgs& args) override;
+  mojo::PendingRemote<viz::mojom::BeginFrameObserver> GetBoundRemote();
+
+ private:
+  void CoalescedBeginFrame();
+  void CallObservers(const viz::BeginFrameArgs& args);
+
+  const raw_ref<
+      const base::flat_set<raw_ptr<SimpleBeginFrameObserver, CtnExperimental>>>
+      simple_begin_frame_observers_;
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  bool pending_coalesce_callback_ = false;
+  viz::BeginFrameArgs begin_frame_args_;
+
+  mojo::Receiver<viz::mojom::BeginFrameObserver> receiver_{this};
+  base::WeakPtrFactory<HostBeginFrameObserver> weak_factory_{this};
+};
+
+}  // namespace ui
+
+#endif  // UI_COMPOSITOR_HOST_BEGIN_FRAME_OBSERVER_H_
diff --git a/ui/gfx/font_render_params_win_unittest.cc b/ui/gfx/font_render_params_win_unittest.cc
index 0ac06df..e43914c 100644
--- a/ui/gfx/font_render_params_win_unittest.cc
+++ b/ui/gfx/font_render_params_win_unittest.cc
@@ -71,12 +71,10 @@
     EXPECT_FLOAT_EQ(params.text_gamma * kGammaMultiplier,
                     FontUtilWin::ClampGamma(contrast));
   } else {
-    // If the registry keys aren't set, `IDWriteRenderingParams` defaults
-    // to the following hard-coded values:
-    constexpr float default_contrast = 0.5f;
-    constexpr float default_gamma = 1.8f;
-    EXPECT_FLOAT_EQ(params.text_contrast, default_contrast);
-    EXPECT_FLOAT_EQ(params.text_gamma, default_gamma);
+    // If the registry keys aren't set, we should be using default Skia values
+    // for contrast and gamma.
+    EXPECT_FLOAT_EQ(params.text_contrast, SK_GAMMA_CONTRAST);
+    EXPECT_FLOAT_EQ(params.text_gamma, SK_GAMMA_EXPONENT);
   }
 
   // Values from `LegacyDisplayGlobals` should match `FontRenderParams`.
diff --git a/ui/gfx/font_util_win.cc b/ui/gfx/font_util_win.cc
index 507eb86..d323c36 100644
--- a/ui/gfx/font_util_win.cc
+++ b/ui/gfx/font_util_win.cc
@@ -24,18 +24,25 @@
   static TextParameters text_parameters;
   static std::once_flag flag;
   std::call_once(flag, [&] {
-    Microsoft::WRL::ComPtr<IDWriteFactory> factory;
-    gfx::win::CreateDWriteFactory(&factory);
-    if (factory) {
-      // We only support the primary device currently.
-      Microsoft::WRL::ComPtr<IDWriteRenderingParams> textParams;
-      if (SUCCEEDED(factory->CreateRenderingParams(&textParams))) {
-        text_parameters.contrast =
-            FontUtilWin::ClampContrast(textParams->GetEnhancedContrast());
-        text_parameters.gamma = FontUtilWin::ClampGamma(textParams->GetGamma());
-      } else {
-        text_parameters.contrast = SK_GAMMA_CONTRAST;
-        text_parameters.gamma = SK_GAMMA_EXPONENT;
+    text_parameters.contrast = SK_GAMMA_CONTRAST;
+    text_parameters.gamma = SK_GAMMA_EXPONENT;
+    // Only apply values from `IDWriteRenderingParams` if the user has
+    // the appropriate registry keys set. Otherwise, `IDWriteRenderingParams`
+    // values will use DirectWrite's default values, which do no match Skia's
+    // defaults.
+    base::win::RegKey key = FontUtilWin::GetTextSettingsRegistryKey();
+    if (key.Valid()) {
+      Microsoft::WRL::ComPtr<IDWriteFactory> factory;
+      gfx::win::CreateDWriteFactory(&factory);
+      if (factory) {
+        // We only support the primary device currently.
+        Microsoft::WRL::ComPtr<IDWriteRenderingParams> text_params;
+        if (SUCCEEDED(factory->CreateRenderingParams(&text_params))) {
+          text_parameters.contrast =
+              FontUtilWin::ClampContrast(text_params->GetEnhancedContrast());
+          text_parameters.gamma =
+              FontUtilWin::ClampGamma(text_params->GetGamma());
+        }
       }
     }
   });
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
index 2e0502f3..d466e72d 100644
--- a/ui/native_theme/native_theme.cc
+++ b/ui/native_theme/native_theme.cc
@@ -12,6 +12,7 @@
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/observer_list.h"
+#include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
 #include "build/build_config.h"
 #include "ui/base/ui_base_switches.h"
@@ -25,6 +26,11 @@
 
 namespace ui {
 
+namespace {
+static constexpr base::TimeDelta kDefaultCaretBlinkInterval =
+    base::Milliseconds(500);
+}
+
 NativeTheme::MenuListExtraParams::MenuListExtraParams() = default;
 NativeTheme::TextFieldExtraParams::TextFieldExtraParams() = default;
 
@@ -178,6 +184,18 @@
   return border_radius;
 }
 
+base::TimeDelta NativeTheme::GetCaretBlinkInterval() const {
+  if (caret_blink_interval_.has_value()) {
+    return caret_blink_interval_.value();
+  }
+  std::optional<base::TimeDelta> platform_interval =
+      GetPlatformCaretBlinkInterval();
+  if (platform_interval.has_value()) {
+    return platform_interval.value();
+  }
+  return kDefaultCaretBlinkInterval;
+}
+
 NativeTheme::NativeTheme(bool should_use_dark_colors,
                          ui::SystemTheme system_theme)
     : should_use_dark_colors_(should_use_dark_colors || IsForcedDarkMode()),
@@ -222,6 +240,11 @@
                                : NativeTheme::PreferredColorScheme::kLight;
 }
 
+std::optional<base::TimeDelta> NativeTheme::GetPlatformCaretBlinkInterval()
+    const {
+  return std::nullopt;
+}
+
 NativeTheme::PreferredColorScheme NativeTheme::GetPreferredColorScheme() const {
   return preferred_color_scheme_;
 }
@@ -324,6 +347,8 @@
       observed_theme->GetPreferredColorScheme();
   PreferredContrast preferred_contrast = observed_theme->GetPreferredContrast();
   bool inverted_colors = observed_theme->GetInvertedColors();
+  base::TimeDelta caret_blink_interval =
+      observed_theme->GetCaretBlinkInterval();
   bool notify_observers = false;
 
   const auto default_page_colors =
@@ -382,6 +407,10 @@
     theme_to_update_->set_inverted_colors(inverted_colors);
     notify_observers = true;
   }
+  if (theme_to_update_->GetCaretBlinkInterval() != caret_blink_interval) {
+    theme_to_update_->set_caret_blink_interval(caret_blink_interval);
+    notify_observers = true;
+  }
 
   if (notify_observers) {
     DCHECK(theme_to_update_->UserHasContrastPreference() ||
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 399e914..3385e9e 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -586,6 +586,17 @@
                                  float border_width,
                                  float zoom_level) const;
 
+  // Returns the rate at which the text caret should blink. If 0, the caret
+  // will not blink.
+  base::TimeDelta GetCaretBlinkInterval() const;
+
+  // Sets the rate at which the text caret should blink. Overrides any
+  // platform values.
+  void set_caret_blink_interval(
+      std::optional<base::TimeDelta> caret_blink_interval) {
+    caret_blink_interval_ = std::move(caret_blink_interval);
+  }
+
   // Whether high contrast is forced via command-line flag.
   static bool IsForcedHighContrast();
 
@@ -606,6 +617,9 @@
   // platform behaviors.
   virtual void ConfigureWebInstance() {}
 
+  // Gets the platform caret blink interval if it exists.
+  virtual std::optional<base::TimeDelta> GetPlatformCaretBlinkInterval() const;
+
   // Allows one native theme to observe changes in another. For example, the
   // web native theme for Windows observes the corresponding ui native theme in
   // order to receive changes regarding the state of dark mode, forced colors
@@ -657,6 +671,7 @@
   bool inverted_colors_ = false;
   PreferredColorScheme preferred_color_scheme_ = PreferredColorScheme::kLight;
   PreferredContrast preferred_contrast_ = PreferredContrast::kNoPreference;
+  std::optional<base::TimeDelta> caret_blink_interval_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 };
diff --git a/ui/native_theme/native_theme_fluent.cc b/ui/native_theme/native_theme_fluent.cc
index 54abae2..f2bf1b4 100644
--- a/ui/native_theme/native_theme_fluent.cc
+++ b/ui/native_theme/native_theme_fluent.cc
@@ -25,10 +25,6 @@
 NativeThemeFluent::NativeThemeFluent(bool should_only_use_dark_colors)
     : NativeThemeBase(should_only_use_dark_colors) {
   scrollbar_width_ = kFluentScrollbarThickness;
-
-  const sk_sp<SkFontMgr> font_manager(skia::DefaultFontMgr());
-  typeface_ = sk_sp<SkTypeface>(
-      font_manager->matchFamilyStyle(kFluentScrollbarFont, SkFontStyle()));
 }
 
 NativeThemeFluent::~NativeThemeFluent() = default;
@@ -258,6 +254,11 @@
   cc::PaintFlags flags;
   flags.setColor(arrow_color);
 
+  if (!typeface_.has_value()) {
+    const sk_sp<SkFontMgr> font_manager(skia::DefaultFontMgr());
+    typeface_ = sk_sp<SkTypeface>(
+        font_manager->matchFamilyStyle(kFluentScrollbarFont, SkFontStyle()));
+  }
   if (!ArrowIconsAvailable()) {
     // Paint regular triangular arrows if the font with arrow icons is not
     // available. GetArrowRect() returns the float rect but it is expected to be
@@ -271,8 +272,8 @@
   const gfx::RectF bounding_rect = GetArrowRect(rect, part, state);
   // The bounding rect for an arrow is a square, so that we can use the width
   // despite the arrow direction.
-  DCHECK(typeface_);
-  SkFont font(typeface_, bounding_rect.width());
+  CHECK(typeface_.has_value());
+  SkFont font(typeface_.value(), bounding_rect.width());
   font.setEdging(SkFont::Edging::kAntiAlias);
   font.setSubpixel(true);
   flags.setAntiAlias(true);
diff --git a/ui/native_theme/native_theme_fluent.h b/ui/native_theme/native_theme_fluent.h
index 33d9b31b..9f9e4384 100644
--- a/ui/native_theme/native_theme_fluent.h
+++ b/ui/native_theme/native_theme_fluent.h
@@ -5,6 +5,8 @@
 #ifndef UI_NATIVE_THEME_NATIVE_THEME_FLUENT_H_
 #define UI_NATIVE_THEME_NATIVE_THEME_FLUENT_H_
 
+#include <optional>
+
 #include "ui/native_theme/native_theme_base.h"
 
 namespace gfx {
@@ -96,7 +98,9 @@
                        int max_arrow_rect_side) const;
 
   // Returns true if the font with arrow icons is present on the device.
-  bool ArrowIconsAvailable() const { return typeface_.get(); }
+  bool ArrowIconsAvailable() const {
+    return typeface_.has_value() && typeface_.value().get();
+  }
 
   const char* GetArrowCodePointForScrollbarPart(Part part) const;
 
@@ -107,8 +111,9 @@
                           NativeTheme::Part direction) const;
 
   // The value stores a shared pointer to SkTypeface with the font family, which
-  // contains arrow icons.
-  sk_sp<SkTypeface> typeface_;
+  // contains arrow icons. The typeface is lazily loaded the first time
+  // PaintArrow is called.
+  mutable std::optional<sk_sp<SkTypeface>> typeface_;
 };
 
 }  // namespace ui
diff --git a/ui/native_theme/native_theme_mac.h b/ui/native_theme/native_theme_mac.h
index 886da7c..af064fdc 100644
--- a/ui/native_theme/native_theme_mac.h
+++ b/ui/native_theme/native_theme_mac.h
@@ -98,6 +98,9 @@
   NativeThemeMac(bool configure_web_instance, bool should_only_use_dark_colors);
   ~NativeThemeMac() override;
 
+  // NativeTheme:
+  std::optional<base::TimeDelta> GetPlatformCaretBlinkInterval() const override;
+
  private:
   // Paint the selected menu item background, and a border for emphasis when in
   // high contrast.
diff --git a/ui/native_theme/native_theme_mac.mm b/ui/native_theme/native_theme_mac.mm
index e06aa51..d8ff8697 100644
--- a/ui/native_theme/native_theme_mac.mm
+++ b/ui/native_theme/native_theme_mac.mm
@@ -14,6 +14,7 @@
 #include "base/mac/mac_util.h"
 #include "base/no_destructor.h"
 #include "cc/paint/paint_shader.h"
+#include "ui/base/cocoa/defaults_utils.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/color/color_provider.h"
@@ -23,7 +24,7 @@
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/skia_conversions.h"
-#include "ui/native_theme/common_theme.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/native_theme/native_theme_aura.h"
 #include "ui/native_theme/native_theme_features.h"
 
@@ -561,6 +562,13 @@
       removeObserver:display_accessibility_notification_token_];
 }
 
+std::optional<base::TimeDelta> NativeThemeMac::GetPlatformCaretBlinkInterval()
+    const {
+  // If there's insertion point flash rate info in NSUserDefaults, use the
+  // blink period derived from that.
+  return ui::TextInsertionCaretBlinkPeriodFromDefaults();
+}
+
 void NativeThemeMac::PaintSelectedMenuItem(
     cc::PaintCanvas* canvas,
     const ColorProvider* color_provider,
diff --git a/ui/native_theme/native_theme_mac_unittest.mm b/ui/native_theme/native_theme_mac_unittest.mm
index b9af389f..4d013b0 100644
--- a/ui/native_theme/native_theme_mac_unittest.mm
+++ b/ui/native_theme/native_theme_mac_unittest.mm
@@ -4,7 +4,9 @@
 
 #include "ui/native_theme/native_theme_mac.h"
 
+#include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/cocoa/defaults_utils.h"
 
 namespace ui {
 
@@ -58,4 +60,24 @@
   EXPECT_EQ(gfx::Size(36.0, 12.0), mac->GetThumbMinSize(false, 2.0));
 }
 
+TEST(NativeThemeMacTest, GetCaretBlinkInterval) {
+  TestNativeThemeMac theme;
+  std::optional<base::TimeDelta> system_value(
+      ui::TextInsertionCaretBlinkPeriodFromDefaults());
+  base::TimeDelta actual_interval = theme.GetCaretBlinkInterval();
+
+  if (system_value.has_value()) {
+    // Uses system value.
+    EXPECT_EQ(system_value.value(), actual_interval);
+  } else {
+    // Uses default value.
+    EXPECT_EQ(base::Milliseconds(500), actual_interval);
+  }
+  // The setter overrides the system value or the default value.
+  base::TimeDelta new_interval = base::Milliseconds(42);
+  theme.set_caret_blink_interval(new_interval);
+  actual_interval = theme.GetCaretBlinkInterval();
+  EXPECT_EQ(new_interval, actual_interval);
+}
+
 }  // namespace ui
diff --git a/ui/native_theme/native_theme_unittest.cc b/ui/native_theme/native_theme_unittest.cc
index 433acd7..fd92843 100644
--- a/ui/native_theme/native_theme_unittest.cc
+++ b/ui/native_theme/native_theme_unittest.cc
@@ -5,6 +5,7 @@
 #include "ui/native_theme/native_theme.h"
 
 #include "base/test/metrics/histogram_tester.h"
+#include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ui {
@@ -97,4 +98,18 @@
   EXPECT_EQ(theme.GetForcedColorsKey(), ColorProviderKey::ForcedColors::kNone);
 }
 
+TEST(NativeThemeTest, TestCaretBlinkInterval) {
+  TestNativeTheme theme;
+
+  EXPECT_EQ(base::Milliseconds(500), theme.GetCaretBlinkInterval());
+
+  base::TimeDelta new_interval = base::Milliseconds(42);
+  theme.set_caret_blink_interval(new_interval);
+  EXPECT_EQ(new_interval, theme.GetCaretBlinkInterval());
+
+  new_interval = base::Milliseconds(0);
+  theme.set_caret_blink_interval(new_interval);
+  EXPECT_EQ(new_interval, theme.GetCaretBlinkInterval());
+}
+
 }  // namespace ui
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
index 9830931..566230f 100644
--- a/ui/native_theme/native_theme_win.cc
+++ b/ui/native_theme/native_theme_win.cc
@@ -21,6 +21,7 @@
 #include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
 #include "base/win/dark_mode_support.h"
 #include "base/win/scoped_gdi_object.h"
 #include "base/win/scoped_hdc.h"
@@ -48,6 +49,7 @@
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/native_theme/common_theme.h"
+#include "ui/native_theme/native_theme.h"
 
 // This was removed from Winvers.h but is still used.
 #if !defined(COLOR_MENUHIGHLIGHT)
@@ -360,6 +362,16 @@
       should_use_system_accent_color());
 }
 
+std::optional<base::TimeDelta> NativeThemeWin::GetPlatformCaretBlinkInterval()
+    const {
+  static const size_t system_value = ::GetCaretBlinkTime();
+  if (system_value != 0) {
+    return (system_value == INFINITE) ? base::TimeDelta()
+                                      : base::Milliseconds(system_value);
+  }
+  return std::nullopt;
+}
+
 NativeThemeWin::~NativeThemeWin() {
   // TODO(https://crbug.com/787692): Calling CloseHandles() here breaks
   // certain tests and the reliability bots.
diff --git a/ui/native_theme/native_theme_win.h b/ui/native_theme/native_theme_win.h
index 714a8c5..277b4f9 100644
--- a/ui/native_theme/native_theme_win.h
+++ b/ui/native_theme/native_theme_win.h
@@ -91,6 +91,7 @@
 
   // NativeTheme:
   void ConfigureWebInstance() override;
+  std::optional<base::TimeDelta> GetPlatformCaretBlinkInterval() const override;
 
   NativeThemeWin(bool configure_web_instance, bool should_only_use_dark_colors);
   ~NativeThemeWin() override;
diff --git a/ui/native_theme/native_theme_win_unittest.cc b/ui/native_theme/native_theme_win_unittest.cc
index 722d2796..66f32fe 100644
--- a/ui/native_theme/native_theme_win_unittest.cc
+++ b/ui/native_theme/native_theme_win_unittest.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "ui/native_theme/native_theme_win.h"
+#include <cmath>
 
+#include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/color_palette.h"
 
@@ -154,4 +156,28 @@
   EXPECT_EQ(theme.GetColorMode(), ColorMode::kLight);
 }
 
+TEST(NativeThemeWinTest, GetCaretBlinkInterval) {
+  TestNativeThemeWin theme;
+  static const size_t system_value = ::GetCaretBlinkTime();
+  base::TimeDelta actual_interval = theme.GetCaretBlinkInterval();
+
+  if (system_value == 0) {
+    // Uses default value when there is no system value.
+    EXPECT_EQ(base::Milliseconds(500), actual_interval);
+  } else if (system_value == INFINITY) {
+    // 0 is the value meaning "don't blink" in Chromium, while Windows uses
+    // INFINITY.
+    EXPECT_EQ(base::Milliseconds(0), actual_interval);
+  } else {
+    // Uses system value without modification.
+    EXPECT_EQ(base::Milliseconds(system_value), actual_interval);
+  }
+
+  // The setter overrides the system value or the default value.
+  base::TimeDelta new_interval = base::Milliseconds(42);
+  theme.set_caret_blink_interval(new_interval);
+  actual_interval = theme.GetCaretBlinkInterval();
+  EXPECT_EQ(new_interval, actual_interval);
+}
+
 }  // namespace ui
diff --git a/ui/ozone/common/native_pixmap_egl_binding.cc b/ui/ozone/common/native_pixmap_egl_binding.cc
index 34d3142f..7e0766e 100644
--- a/ui/ozone/common/native_pixmap_egl_binding.cc
+++ b/ui/ozone/common/native_pixmap_egl_binding.cc
@@ -35,6 +35,8 @@
 #define DRM_FORMAT_YVU420 FOURCC('Y', 'V', '1', '2')
 #define DRM_FORMAT_NV12 FOURCC('N', 'V', '1', '2')
 #define DRM_FORMAT_P010 FOURCC('P', '0', '1', '0')
+/* Reserve 0 for the invalid format specifier */
+#define DRM_FORMAT_INVALID 0
 
 EGLint FourCC(gfx::BufferFormat format) {
   switch (format) {
@@ -70,11 +72,11 @@
       return DRM_FORMAT_ABGR16161616F;
     case gfx::BufferFormat::RGBA_4444:
     case gfx::BufferFormat::YUVA_420_TRIPLANAR:
-      return 0;
+      return DRM_FORMAT_INVALID;
   }
 
   NOTREACHED();
-  return 0;
+  return DRM_FORMAT_INVALID;
 }
 
 }  // namespace
@@ -116,7 +118,7 @@
     GLenum target,
     GLuint texture_id) {
   DCHECK(!pixmap_);
-  if (format_ == gfx::BufferFormat::YUVA_420_TRIPLANAR) {
+  if (FourCC(format_) == DRM_FORMAT_INVALID) {
     LOG(ERROR) << "Unsupported format: " << gfx::BufferFormatToString(format_);
     return false;
   }
diff --git a/ui/ozone/platform/drm/gpu/drm_framebuffer.cc b/ui/ozone/platform/drm/gpu/drm_framebuffer.cc
index fcd7ac0..d90456a 100644
--- a/ui/ozone/platform/drm/gpu/drm_framebuffer.cc
+++ b/ui/ozone/platform/drm/gpu/drm_framebuffer.cc
@@ -67,10 +67,9 @@
                                    params.handles, params.strides,
                                    params.offsets, modifiers, &framebuffer_id,
                                    params.flags)) {
-    DPLOG(WARNING) << "AddFramebuffer2:"
-                   << "size=" << params.width << "x" << params.height
-                   << " drm_format=" << (int)drm_format
-                   << " fb_id=" << framebuffer_id << " flags=" << params.flags;
+    VLOG(1) << "AddFramebuffer2:" << "size=" << params.width << "x"
+            << params.height << " drm_format=" << (int)drm_format
+            << " fb_id=" << framebuffer_id << " flags=" << params.flags;
     return nullptr;
   }
 
@@ -80,11 +79,9 @@
                                    params.handles, params.strides,
                                    params.offsets, modifiers,
                                    &opaque_framebuffer_id, params.flags)) {
-    DPLOG(WARNING) << "AddFramebuffer2:"
-                   << "size=" << params.width << "x" << params.height
-                   << " drm_format=" << (int)drm_format
-                   << " fb_id=" << opaque_framebuffer_id
-                   << " flags=" << params.flags;
+    VLOG(1) << "AddFramebuffer2:" << "size=" << params.width << "x"
+            << params.height << " drm_format=" << (int)drm_format
+            << " fb_id=" << opaque_framebuffer_id << " flags=" << params.flags;
     drm_device->RemoveFramebuffer(framebuffer_id);
     return nullptr;
   }
@@ -151,11 +148,14 @@
       modeset_sequence_id_at_allocation_(drm_device_->modeset_sequence_id()) {}
 
 DrmFramebuffer::~DrmFramebuffer() {
-  if (!drm_device_->RemoveFramebuffer(framebuffer_id_))
-    PLOG(WARNING) << "RemoveFramebuffer";
+  if (!drm_device_->RemoveFramebuffer(framebuffer_id_)) {
+    VLOG(1) << "RemoveFramebuffer";
+  }
+
   if (opaque_framebuffer_id_ &&
-      !drm_device_->RemoveFramebuffer(opaque_framebuffer_id_))
-    PLOG(WARNING) << "RemoveFramebuffer";
+      !drm_device_->RemoveFramebuffer(opaque_framebuffer_id_)) {
+    VLOG(1) << "RemoveFramebuffer";
+  }
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
index 52b45c8e..a030156 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
@@ -14,11 +14,13 @@
 #include <vector>
 
 #include "base/containers/contains.h"
+#include "base/containers/flat_map.h"
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
+#include "ui/display/display_features.h"
 #include "ui/display/types/display_mode.h"
 #include "ui/display/types/display_snapshot.h"
 #include "ui/display/types/gamma_ramp_rgb_entry.h"
@@ -124,6 +126,38 @@
   return std::string(it->second);
 }
 
+ControllerConfigParams* FindConfigParamsForConnector(
+    std::vector<ControllerConfigParams>& config_list,
+    uint32_t connector_id) {
+  for (auto& config : config_list) {
+    if (config.connector == connector_id) {
+      return &config;
+    }
+  }
+  return nullptr;
+}
+
+std::string ConfigRequestToString(
+    const std::vector<display::DisplayConfigurationParams>& config_requests) {
+  std::string signature;
+  for (const auto& config : config_requests) {
+    if (config.id <= 0) {
+      LOG(WARNING) << __func__
+                   << ": potentially invalid display ID: " << config.id;
+    }
+
+    signature +=
+        base::NumberToString(config.id) + ":" + config.origin.ToString() + ":" +
+        (config.mode.has_value() ? (*config.mode)->ToString() : "Disabled") +
+        ":" + (config.enable_vrr ? "vrr" : "no_vrr") + ";";
+  }
+  if (signature.empty()) {
+    LOG(WARNING) << __func__ << ": empty return value with request of size: "
+                 << config_requests.size();
+  }
+  return signature;
+}
+
 }  // namespace
 
 DrmGpuDisplayManager::DrmGpuDisplayManager(ScreenManager* screen_manager,
@@ -139,6 +173,8 @@
 }
 
 MovableDisplaySnapshots DrmGpuDisplayManager::GetDisplays() {
+  successful_test_config_params_.clear();
+
   std::vector<std::unique_ptr<DrmDisplay>> old_displays;
   old_displays.swap(displays_);
   std::vector<DrmDisplayParams> displays_to_create;
@@ -162,6 +198,8 @@
         drm->plane_manager()->ResetConnectorsCacheAndGetValidIds(
             drm->GetResources());
 
+    // TODO: b/327011965 - Move assigning CRTCs to connectors from
+    // RefreshNativeDisplays() to before test modeset.
     // Create new DisplaySnapshots and resolve display ID collisions.
     auto display_infos = GetDisplayInfosAndUpdateCrtcs(*drm);
 
@@ -330,44 +368,66 @@
 bool DrmGpuDisplayManager::ConfigureDisplays(
     const std::vector<display::DisplayConfigurationParams>& config_requests,
     display::ModesetFlags modeset_flags) {
+  const bool is_commit =
+      modeset_flags.Has(display::ModesetFlag::kCommitModeset);
   std::vector<ControllerConfigParams> controllers_to_configure;
-  for (const auto& config : config_requests) {
-    int64_t display_id = config.id;
-    DrmDisplay* display = FindDisplay(display_id);
-    if (!display) {
-      LOG(WARNING) << __func__ << ": there is no display with ID "
-                   << display_id;
-      return false;
-    }
+  if (is_commit) {
+    controllers_to_configure = GetLatestModesetTestConfig(config_requests);
+  }
 
-    std::unique_ptr<drmModeModeInfo> mode_ptr =
-        config.mode ? std::make_unique<drmModeModeInfo>() : nullptr;
-    if (config.mode) {
-      if (!FindModeForDisplay(mode_ptr.get(), *config.mode.value(),
-                              display->modes(), displays_)) {
+  if (controllers_to_configure.empty()) {
+    for (const auto& config : config_requests) {
+      int64_t display_id = config.id;
+      DrmDisplay* display = FindDisplay(display_id);
+      if (!display) {
+        LOG(WARNING) << __func__ << ": there is no display with ID "
+                     << display_id;
         return false;
       }
-    }
 
-    scoped_refptr<DrmDevice> drm = display->drm();
-    ControllerConfigParams params(display->display_id(), drm, display->crtc(),
-                                  display->connector(), config.origin,
-                                  std::move(mode_ptr), config.enable_vrr,
-                                  display->base_connector_id());
-    controllers_to_configure.push_back(std::move(params));
+      std::unique_ptr<drmModeModeInfo> mode_ptr =
+          config.mode ? std::make_unique<drmModeModeInfo>() : nullptr;
+      if (config.mode) {
+        if (!FindModeForDisplay(mode_ptr.get(), *config.mode.value(),
+                                display->modes(), displays_)) {
+          return false;
+        }
+      }
+
+      scoped_refptr<DrmDevice> drm = display->drm();
+      ControllerConfigParams params(display->display_id(), drm, display->crtc(),
+                                    display->connector(), config.origin,
+                                    std::move(mode_ptr), config.enable_vrr,
+                                    display->base_connector_id());
+      controllers_to_configure.push_back(std::move(params));
+    }
   }
 
   bool config_success = screen_manager_->ConfigureDisplayControllers(
       controllers_to_configure, modeset_flags);
 
+  // Only attempt to fallback on using different CRTC-connector pairings if
+  // hardware mirroring is disabled as hardware mirroring has multiple
+  // connectors assigned to one CRTC, and the fallback assumes 1:1 pairing.
+  const bool should_try_test_fallback =
+      !is_commit && !config_success &&
+      !display::features::IsHardwareMirrorModeEnabled();
+  if (should_try_test_fallback) {
+    config_success = RetryTestConfigureDisplaysWithAlternateCrtcs(
+        config_requests, controllers_to_configure);
+  }
+
   if (displays_configured_callback_)
     displays_configured_callback_.Run();
 
-  const bool is_commit =
-      modeset_flags.Has(display::ModesetFlag::kCommitModeset);
-  if (is_commit && config_success) {
-    for (const auto& controller : controllers_to_configure)
-      FindDisplay(controller.display_id)->SetOrigin(controller.origin);
+  if (is_commit) {
+    successful_test_config_params_.clear();
+
+    if (config_success) {
+      for (const auto& controller : controllers_to_configure) {
+        FindDisplay(controller.display_id)->SetOrigin(controller.origin);
+      }
+    }
   }
 
   return config_success;
@@ -515,6 +575,17 @@
   return nullptr;
 }
 
+DrmDisplay* DrmGpuDisplayManager::FindDisplayByConnectorId(
+    uint32_t connector_id) const {
+  for (const auto& display : displays_) {
+    if (display->connector() == connector_id) {
+      return display.get();
+    }
+  }
+
+  return nullptr;
+}
+
 void DrmGpuDisplayManager::NotifyScreenManager(
     const std::vector<std::unique_ptr<DrmDisplay>>& new_displays,
     const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) const {
@@ -538,4 +609,178 @@
   }
 }
 
+// TODO: b/327015722 - Move test modeset fallback with alternate CRTCs
+// from DrmGpuDisplayManager to ScreenManager.
+// The kernel can sometimes silently reallocate the resources of one CRTC to
+// another, making the other ineffective. One such case is when i915's Bigjoiner
+// takes the underlying pipe of a secondary CRTC for high bandwidth displays
+// (DP 2.1+). Attempting modeset with a stolen CRTC will result in failure. The
+// only way for userspace to overcome a stolen CRTC is to dynamically assign
+// other CRTC configurations via test modesets.
+bool DrmGpuDisplayManager::RetryTestConfigureDisplaysWithAlternateCrtcs(
+    const std::vector<display::DisplayConfigurationParams>& config_requests,
+    const std::vector<ControllerConfigParams>& controllers_to_configure) {
+  // Separate individual params of |controllers_to_configure| into multiple
+  // std::vector<ControllerConfigParams> by their DrmDevice.
+  base::flat_map<scoped_refptr<DrmDevice>, std::vector<ControllerConfigParams>>
+      drm_device_controllers_to_configure;
+  for (const auto& config : controllers_to_configure) {
+    scoped_refptr<DrmDevice> drm = config.drm;
+    drm_device_controllers_to_configure[drm].emplace_back(config);
+  }
+
+  // For each DrmDevice, try test modeset with all possible CRTC-connector
+  // combinations. Use the first successful one.
+  bool fallback_successful_for_all_devices = true;
+  std::vector<ControllerConfigParams> successful_config_list;
+  for (auto& [drm, configs_list] : drm_device_controllers_to_configure) {
+    std::vector<CrtcConnectorPairs> crtc_connector_permutations =
+        GetAllCrtcConnectorPermutations(*drm, configs_list);
+
+    VLOG(1) << "Number of possible fallback CRTC-connector permutations: "
+            << crtc_connector_permutations.size();
+
+    bool has_successful_permutation = false;
+    for (const auto& permutation : crtc_connector_permutations) {
+      // Set up the display abstractions according to the current |permutation|.
+      for (const auto& crtc_connector_pair : permutation) {
+        uint32_t crtc_id = crtc_connector_pair.crtc_id;
+        uint32_t connector_id = crtc_connector_pair.connector_id;
+
+        ControllerConfigParams* param =
+            FindConfigParamsForConnector(configs_list, connector_id);
+        if (!param) {
+          LOG(ERROR) << __func__
+                     << ": Could not find ControllerConfigParams for connector "
+                        "with ID: "
+                     << connector_id;
+          continue;
+        }
+        param->crtc = crtc_id;
+      }
+
+      if (!UpdateDisplaysWithNewCrtcs(configs_list)) {
+        continue;
+      }
+
+      if (screen_manager_->ConfigureDisplayControllers(
+              configs_list, {display::ModesetFlag::kTestModeset})) {
+        has_successful_permutation = true;
+        for (auto& config : configs_list) {
+          successful_config_list.push_back(config);
+        }
+        // No need to try other permutations for the device if one is
+        // successful.
+        break;
+      }
+    }
+
+    fallback_successful_for_all_devices &= has_successful_permutation;
+    if (!fallback_successful_for_all_devices) {
+      LOG(WARNING) << __func__
+                   << ": No successful CRTC-connector pairing permutation "
+                      "found or DRM device: "
+                   << drm->device_path().value();
+
+      // TODO: b/329078793 - Stop reverting to the original config once
+      // pageflips are deferred/skipped during configuration.
+      // Revert ozone abstractions back to the original CRTC-controller pairings
+      // before the fallback attempt. The original CRTC-connector pairings are
+      // usually stable across display changes, and has better chances for a
+      // successful pageflip if one manages to happen between
+      // ConfigureDisplays() calls.
+      if (!UpdateDisplaysWithNewCrtcs(controllers_to_configure)) {
+        LOG(ERROR)
+            << __func__
+            << ": Failed to revert to the original CRTC-conector pairings.";
+      }
+      return false;
+    }
+  }
+
+  if (fallback_successful_for_all_devices) {
+    const std::string config_reques_string =
+        ConfigRequestToString(config_requests);
+    successful_test_config_params_.insert(
+        {config_reques_string, successful_config_list});
+  }
+
+  // TODO: b/329078793 - Stop reverting to the original config once
+  // pageflips are deferred/skipped during configuration.
+  // Revert ozone abstractions back to the original CRTC-controller pairings
+  // before the fallback attempt. The original CRTC-connector pairings are
+  // usually stable across display changes, and has better chances for a
+  // successful pageflip if one manages to happen between
+  // ConfigureDisplays() calls.
+  if (!UpdateDisplaysWithNewCrtcs(controllers_to_configure)) {
+    LOG(ERROR) << __func__
+               << ": Failed to revert to the original CRTC-conector pairings.";
+  }
+
+  return fallback_successful_for_all_devices;
+}
+
+bool DrmGpuDisplayManager::UpdateDisplaysWithNewCrtcs(
+    const std::vector<ControllerConfigParams>& controllers_to_configure) {
+  base::flat_map<scoped_refptr<DrmDevice>, std::vector<ControllerConfigParams>>
+      drm_device_to_configs;
+  for (const auto& config : controllers_to_configure) {
+    scoped_refptr<DrmDevice> drm = config.drm;
+    drm_device_to_configs[drm].emplace_back(config);
+  }
+
+  // TODO: b/327015722 - handle ReplaceDisplayControllersCrtcs() inside
+  // ScreenManager.
+  std::vector<std::pair<DrmDisplay*, uint32_t /*new_crtc_id*/>>
+      display_to_new_crtcs_pairs;
+  for (const auto& [drm, config_list] : drm_device_to_configs) {
+    ConnectorCrtcMap current_connector_to_crtc_pairings;
+    ConnectorCrtcMap new_connector_to_crtc_pairings;
+    for (const auto& config_param : controllers_to_configure) {
+      const uint32_t connector_id = config_param.connector;
+      DrmDisplay* display = FindDisplayByConnectorId(connector_id);
+      if (!display) {
+        LOG(DFATAL) << "DrmDisplay with connector ID " << connector_id
+                    << " not found.";
+        return false;
+      }
+
+      display_to_new_crtcs_pairs.push_back({display, config_param.crtc});
+      current_connector_to_crtc_pairings[connector_id] = display->crtc();
+      new_connector_to_crtc_pairings[connector_id] = config_param.crtc;
+    }
+
+    if (!screen_manager_->ReplaceDisplayControllersCrtcs(
+            drm, current_connector_to_crtc_pairings,
+            new_connector_to_crtc_pairings)) {
+      return false;
+    }
+  }
+
+  for (auto& [display, crtc_id] : display_to_new_crtcs_pairs) {
+    display->set_crtc(crtc_id);
+  }
+
+  return true;
+}
+
+std::vector<ControllerConfigParams>
+DrmGpuDisplayManager::GetLatestModesetTestConfig(
+    const std::vector<display::DisplayConfigurationParams>& config_requests) {
+  const std::string config_reques_string =
+      ConfigRequestToString(config_requests);
+  const auto& config_param_it =
+      successful_test_config_params_.find(config_reques_string);
+
+  if (config_param_it == successful_test_config_params_.end()) {
+    return {};
+  }
+
+  if (!UpdateDisplaysWithNewCrtcs(config_param_it->second)) {
+    LOG(ERROR) << __func__ << ": Unable to restore CRTC-connector pairings.";
+  }
+
+  return config_param_it->second;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
index 0e47fce..84d26a6 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
@@ -16,6 +16,7 @@
 #include "ui/display/types/display_configuration_params.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/ozone/platform/drm/common/display_types.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_util.h"
 
 using drmModeModeInfo = struct _drmModeModeInfo;
 
@@ -90,6 +91,7 @@
   friend class DrmGpuDisplayManagerTest;
 
   DrmDisplay* FindDisplay(int64_t display_id) const;
+  DrmDisplay* FindDisplayByConnectorId(uint32_t connector_id) const;
 
   // Notify ScreenManager of all the displays that were present before the
   // update but are gone after the update.
@@ -97,12 +99,35 @@
       const std::vector<std::unique_ptr<DrmDisplay>>& new_displays,
       const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) const;
 
+  // Test modesets with |controllers_to_configure|, but with all
+  // possible permutations of CRTC-connector pairings. Returns true if one of
+  // the permutation leads to a successful test modest.
+  bool RetryTestConfigureDisplaysWithAlternateCrtcs(
+      const std::vector<display::DisplayConfigurationParams>& config_requests,
+      const std::vector<ControllerConfigParams>& controllers_to_configure);
+
+  // Replace the CRTC of all displays and display controllers specified in
+  // |controllers_to_configure| by its connector with their new CRTC.
+  bool UpdateDisplaysWithNewCrtcs(
+      const std::vector<ControllerConfigParams>& controllers_to_configure);
+
+  // Get the display state associated with |config_requests| if there was a
+  // successful test configuration before the commit modeset call.
+  std::vector<ControllerConfigParams> GetLatestModesetTestConfig(
+      const std::vector<display::DisplayConfigurationParams>& config_requests);
+
   const raw_ptr<ScreenManager> screen_manager_;         // Not owned.
   const raw_ptr<DrmDeviceManager> drm_device_manager_;  // Not owned.
 
   std::vector<std::unique_ptr<DrmDisplay>> displays_;
 
   base::RepeatingClosure displays_configured_callback_;
+
+  // A map of successful test display config request and the config params. The
+  // map is cleared on every commit modeset, or whenever the displays and
+  // controllers are recreated in GetDisplays().
+  base::flat_map<std::string, std::vector<ControllerConfigParams>>
+      successful_test_config_params_;
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc
index 9bb19a5d..4a4c03e 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc
@@ -8,9 +8,12 @@
 #include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
 
 #include "base/files/file_path.h"
+#include "base/test/scoped_feature_list.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/display_features.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/linux/test/mock_gbm_device.h"
 #include "ui/ozone/platform/drm/common/display_types.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
@@ -23,6 +26,11 @@
 
 namespace {
 
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::Exactly;
+using ::testing::Return;
+
 // HP z32x monitor.
 constexpr unsigned char kHPz32x[] =
     "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x32\x01\x01\x01\x01"
@@ -80,6 +88,68 @@
     ResolutionAndRefreshRate{gfx::Size(1920, 1080), 50u},
     ResolutionAndRefreshRate{gfx::Size(1920, 1080), 30u}};
 
+// arg: drmModeAtomicReq*, pairings: CrtcConnectorPairs
+MATCHER_P(AtomicRequestHasCrtcConnectorPairs, pairings, "") {
+  if (arg == nullptr) {
+    return false;
+  }
+
+  std::vector<drmModeAtomicReqItem> arg_items;
+  for (uint32_t i = 0; i < arg->cursor; ++i) {
+    arg_items.push_back(arg->items[i]);
+  }
+
+  for (const auto& [crtc, connector] : pairings) {
+    if (std::find_if(arg_items.begin(), arg_items.end(),
+                     [crtc, connector](const drmModeAtomicReqItem& item) {
+                       return item.object_id == connector && item.value == crtc;
+                     }) == arg_items.end()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// MockDrmDevice is not set up to provide a finer grain mocking behavior for
+// CommitProperties. GmockDrmDevice provides gmockable interface for DrmDevice,
+// but also defaults to MockDrmDevice unless a mock is specified.
+class GmockDrmDevice : public MockDrmDevice {
+ public:
+  explicit GmockDrmDevice(std::unique_ptr<GbmDevice> gbm_device)
+      : MockDrmDevice(std::move(gbm_device)) {}
+
+  GmockDrmDevice(const base::FilePath& path,
+                 std::unique_ptr<GbmDevice> gbm_device,
+                 bool is_primary_device)
+      : MockDrmDevice(path, std::move(gbm_device), is_primary_device) {}
+
+  MOCK_METHOD(bool,
+              CommitProperties,
+              (drmModeAtomicReq * request,
+               uint32_t flags,
+               uint32_t crtc_count,
+               scoped_refptr<PageFlipRequest> callback),
+              (override));
+
+ protected:
+  ~GmockDrmDevice() override = default;
+};
+
+class GmockDrmDeviceGenerator : public DrmDeviceGenerator {
+  scoped_refptr<DrmDevice> CreateDevice(const base::FilePath& path,
+                                        base::ScopedFD fd,
+                                        bool is_primary_device) override {
+    auto gbm_device = std::make_unique<MockGbmDevice>();
+    if (path.empty()) {
+      return base::MakeRefCounted<GmockDrmDevice>(std::move(gbm_device));
+    }
+
+    return base::MakeRefCounted<GmockDrmDevice>(
+        std::move(path), std::move(gbm_device), is_primary_device);
+  }
+};
+
 }  // namespace
 
 class DrmGpuDisplayManagerTest : public testing::Test {
@@ -118,8 +188,9 @@
     return scoped_refptr<MockDrmDevice>(mock_drm);
   }
 
-  void AddCrtcAndPlanes(MockDrmDevice::MockDrmState& drm_state,
-                        size_t num_of_planes = 1u) {
+  // Returns the CRTC ID.
+  uint32_t AddPlaneOnCrtcAndGetCrtcId(MockDrmDevice::MockDrmState& drm_state,
+                                      size_t num_of_planes = 1u) {
     const auto& crtc = drm_state.AddCrtc();
     for (size_t i = 0; i < num_of_planes; ++i) {
       drm_state.AddPlane(crtc.id, DRM_PLANE_TYPE_PRIMARY);
@@ -128,23 +199,29 @@
       }
       drm_state.AddPlane(crtc.id, DRM_PLANE_TYPE_CURSOR);
     }
+
+    return crtc.id;
   }
 
-  bool ConfigureDisplays(const MovableDisplaySnapshots& display_snapshots) {
+  bool ConfigureDisplays(const MovableDisplaySnapshots& display_snapshots,
+                         display::ModesetFlags modeset_flag) {
     std::vector<display::DisplayConfigurationParams> config_requests;
     for (const auto& snapshot : display_snapshots) {
       config_requests.emplace_back(snapshot->display_id(), snapshot->origin(),
                                    snapshot->native_mode());
     }
-    return drm_gpu_display_manager_->ConfigureDisplays(
-        config_requests, {display::ModesetFlag::kTestModeset,
-                          display::ModesetFlag::kCommitModeset});
+    return drm_gpu_display_manager_->ConfigureDisplays(config_requests,
+                                                       modeset_flag);
   }
 
   DrmDisplay* FindDisplay(int64_t display_id) {
     return drm_gpu_display_manager_->FindDisplay(display_id);
   }
 
+  DrmDisplay* FindDisplayByConnectorId(uint32_t connector) {
+    return drm_gpu_display_manager_->FindDisplayByConnectorId(connector);
+  }
+
   size_t next_drm_device_number_ = 0u;
 
   std::unique_ptr<DrmDeviceManager> device_manager_;
@@ -159,7 +236,7 @@
         MockDrmDevice::MockDrmState::CreateStateWithAllProperties();
 
     // Add 1 CRTC
-    AddCrtcAndPlanes(drm_state);
+    AddPlaneOnCrtcAndGetCrtcId(drm_state);
 
     // Add one encoder
     auto& encoder = drm_state.AddEncoder();
@@ -186,7 +263,7 @@
   // Add |kMaxDrmConnectors| + 1 connector, each with one active display.
   for (size_t i = 0; i < kMaxDrmConnectors + 1; ++i) {
     // Add 1 CRTC
-    AddCrtcAndPlanes(drm_state);
+    AddPlaneOnCrtcAndGetCrtcId(drm_state);
 
     // Add one encoder
     auto& encoder = drm_state.AddEncoder();
@@ -220,7 +297,7 @@
 
   // Add 3 connectors, each with one active display.
   for (size_t i = 0; i < 3; ++i) {
-    AddCrtcAndPlanes(drm_state);
+    AddPlaneOnCrtcAndGetCrtcId(drm_state);
 
     auto& encoder = drm_state.AddEncoder();
     encoder.possible_crtcs = 1 << i;
@@ -255,7 +332,9 @@
   ASSERT_EQ(display3->drm().get(), drm.get());
 
   // Make sure configuration on the returned snapshots is possible.
-  ASSERT_TRUE(ConfigureDisplays(display_snapshots));
+  ASSERT_TRUE(ConfigureDisplays(display_snapshots,
+                                {display::ModesetFlag::kTestModeset,
+                                 display::ModesetFlag::kCommitModeset}));
 }
 
 // This case tests scenarios in which a display ID is searched across multiple
@@ -275,7 +354,7 @@
     auto drm_state =
         MockDrmDevice::MockDrmState::CreateStateWithAllProperties();
 
-    AddCrtcAndPlanes(drm_state);
+    AddPlaneOnCrtcAndGetCrtcId(drm_state);
 
     auto& encoder = drm_state.AddEncoder();
     encoder.possible_crtcs = 0b1;
@@ -311,7 +390,9 @@
   ASSERT_NE(display2->drm().get(), display3->drm().get());
 
   // Make sure configuration on the returned snapshots is possible.
-  ASSERT_TRUE(ConfigureDisplays(display_snapshots));
+  ASSERT_TRUE(ConfigureDisplays(display_snapshots,
+                                {display::ModesetFlag::kTestModeset,
+                                 display::ModesetFlag::kCommitModeset}));
 }
 
 // TODO(crbug.com/1431767): Re-enable this test
@@ -330,7 +411,7 @@
   // Add three connectors, each with one active display.
   for (size_t i = 0; i < 3; ++i) {
     // Add 1 CRTC
-    AddCrtcAndPlanes(drm_state);
+    AddPlaneOnCrtcAndGetCrtcId(drm_state);
 
     // Add one encoder
     auto& encoder = drm_state.AddEncoder();
@@ -374,7 +455,9 @@
   display_snapshots[2]->set_origin(display3_origin);
   display3->SetOrigin(display_snapshots[2]->origin());
 
-  ASSERT_TRUE(ConfigureDisplays(display_snapshots));
+  ASSERT_TRUE(ConfigureDisplays(display_snapshots,
+                                {display::ModesetFlag::kTestModeset,
+                                 display::ModesetFlag::kCommitModeset}));
 
   // "Switch" to mirror mode - so nothing changes as far as display resources
   // go.
@@ -396,7 +479,9 @@
   ASSERT_EQ(display3->origin(), display_snapshots[2]->origin());
   ASSERT_EQ(display3->origin(), display3_origin);
 
-  ASSERT_TRUE(ConfigureDisplays(display_snapshots));
+  ASSERT_TRUE(ConfigureDisplays(display_snapshots,
+                                {display::ModesetFlag::kTestModeset,
+                                 display::ModesetFlag::kCommitModeset}));
 }
 
 TEST_F(DrmGpuDisplayManagerTest, TestEdidIdConflictResolution) {
@@ -405,7 +490,7 @@
 
   // First, add the internal display.
   {
-    AddCrtcAndPlanes(drm_state);
+    AddPlaneOnCrtcAndGetCrtcId(drm_state);
 
     auto& encoder = drm_state.AddEncoder();
     encoder.possible_crtcs = 0b1;
@@ -421,7 +506,7 @@
   // Next, add two external displays that will produce an EDID-based ID
   // collision, since their EDIDs do not include viable serial numbers.
   {
-    AddCrtcAndPlanes(drm_state);
+    AddPlaneOnCrtcAndGetCrtcId(drm_state);
 
     auto& encoder = drm_state.AddEncoder();
     encoder.possible_crtcs = 0b10;
@@ -436,7 +521,7 @@
   }
 
   {
-    AddCrtcAndPlanes(drm_state);
+    AddPlaneOnCrtcAndGetCrtcId(drm_state);
 
     auto& encoder = drm_state.AddEncoder();
     encoder.possible_crtcs = 0b100;
@@ -473,6 +558,416 @@
   ASSERT_EQ(unresolved_display_id1, unresolved_display_id2);
 }
 
+class DrmGpuDisplayManagerMockedDeviceTest : public DrmGpuDisplayManagerTest {
+ protected:
+  void SetUp() override {
+    std::unique_ptr<GmockDrmDeviceGenerator> fake_device_generator =
+        std::make_unique<GmockDrmDeviceGenerator>();
+    device_manager_ =
+        std::make_unique<DrmDeviceManager>(std::move(fake_device_generator));
+    screen_manager_ = std::make_unique<ScreenManager>();
+    drm_gpu_display_manager_ = std::make_unique<DrmGpuDisplayManager>(
+        screen_manager_.get(), device_manager_.get());
+  }
+};
+
+// TODO: b/40263526 - Re-enable for ASan LSan builds after eliminiating circular
+// dependency causing the DrmDevice to leak.
+#if defined(LEAK_SANITIZER)
+#define MAYBE_ConfigureDisplaysAlternateCrtcFallbackSuccess \
+  DISABLED_ConfigureDisplaysAlternateCrtcFallbackSuccess
+#else
+#define MAYBE_ConfigureDisplaysAlternateCrtcFallbackSuccess \
+  ConfigureDisplaysAlternateCrtcFallbackSuccess
+#endif
+TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
+       MAYBE_ConfigureDisplaysAlternateCrtcFallbackSuccess) {
+  auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithAllProperties();
+
+  // Create a pool of 3 CRTCs
+  const uint32_t crtc_1 = AddPlaneOnCrtcAndGetCrtcId(drm_state);
+  AddPlaneOnCrtcAndGetCrtcId(drm_state);
+  const uint32_t crtc_3 = AddPlaneOnCrtcAndGetCrtcId(drm_state);
+
+  uint32_t primary_connector_id, secondary_connector_id;
+
+  // First, add a display with high bandwidth mode.
+  {
+    auto& encoder = drm_state.AddEncoder();
+    // Can use all 3 CRTCs
+    encoder.possible_crtcs = 0b111;
+
+    auto& connector = drm_state.AddConnector();
+    connector.connection = true;
+    connector.modes = std::vector<ResolutionAndRefreshRate>{
+        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
+    connector.encoders = std::vector<uint32_t>{encoder.id};
+
+    primary_connector_id = connector.id;
+  }
+  // Add a normal external display.
+  {
+    auto& encoder = drm_state.AddEncoder();
+    encoder.possible_crtcs = 0b111;
+
+    auto& connector = drm_state.AddConnector();
+    connector.connection = true;
+    connector.modes = kStandardModes;
+    connector.encoders = std::vector<uint32_t>{encoder.id};
+
+    secondary_connector_id = connector.id;
+  }
+
+  scoped_refptr<MockDrmDevice> drm =
+      AddAndInitializeDrmDeviceWithState(drm_state);
+  GmockDrmDevice* gmock_drm = static_cast<GmockDrmDevice*>(drm.get());
+
+  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
+  ASSERT_EQ(display_snapshots.size(), 2u);
+
+  const uint32_t original_primary_crtc =
+      FindDisplayByConnectorId(primary_connector_id)->crtc();
+  const uint32_t original_secondary_crtc =
+      FindDisplayByConnectorId(secondary_connector_id)->crtc();
+
+  // Modesets should fail by default, unless it is with CRTC-connector pairings
+  // specified with |desired_pairings|.
+  EXPECT_CALL(*gmock_drm, CommitProperties)
+      .Times(AtLeast(1))
+      .WillRepeatedly(Return(false));
+
+  CrtcConnectorPairs desired_pairings = {{crtc_1, primary_connector_id},
+                                         {crtc_3, secondary_connector_id}};
+  EXPECT_CALL(
+      *gmock_drm,
+      CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings), _,
+                       _, _))
+      .WillOnce(Return(true));
+
+  EXPECT_TRUE(ConfigureDisplays(display_snapshots,
+                                {display::ModesetFlag::kTestModeset}));
+
+  // Even if there is a successful fallback configuration, ozone abstractions
+  // should not change for test modeset request.
+  DrmDisplay* primary_display = FindDisplayByConnectorId(primary_connector_id);
+  EXPECT_EQ(primary_display->crtc(), original_primary_crtc);
+  DrmDisplay* secondary_display =
+      FindDisplayByConnectorId(secondary_connector_id);
+  EXPECT_EQ(secondary_display->crtc(), original_secondary_crtc);
+
+  // Check that commit modeset after fallback changes the CRTC assignment for
+  // displays.
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(gmock_drm));
+  EXPECT_CALL(
+      *gmock_drm,
+      CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings), _,
+                       _, _))
+      .WillOnce(Return(true));
+
+  EXPECT_TRUE(ConfigureDisplays(display_snapshots,
+                                {display::ModesetFlag::kCommitModeset}));
+  EXPECT_EQ(primary_display->crtc(), crtc_1);
+  EXPECT_EQ(secondary_display->crtc(), crtc_3);
+
+  // DrmDevice seems to leak on successful configure in tests. Manually
+  // checking for mock calls and allowing leak for now.
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(gmock_drm));
+  testing::Mock::AllowLeak(gmock_drm);
+}
+
+TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
+       ConfigureDisplaysFallbackTestSuccessButCommitFailiure) {
+  auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithAllProperties();
+
+  // Create a pool of 3 CRTCs
+  const uint32_t crtc_1 = AddPlaneOnCrtcAndGetCrtcId(drm_state);
+  AddPlaneOnCrtcAndGetCrtcId(drm_state);
+  const uint32_t crtc_3 = AddPlaneOnCrtcAndGetCrtcId(drm_state);
+
+  uint32_t primary_connector_id, secondary_connector_id;
+
+  // First, add a display with high bandwidth mode.
+  {
+    auto& encoder = drm_state.AddEncoder();
+    // Can use all 3 CRTCs
+    encoder.possible_crtcs = 0b111;
+
+    auto& connector = drm_state.AddConnector();
+    connector.connection = true;
+    connector.modes = std::vector<ResolutionAndRefreshRate>{
+        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
+    connector.encoders = std::vector<uint32_t>{encoder.id};
+
+    primary_connector_id = connector.id;
+  }
+  // Add a normal external display.
+  {
+    auto& encoder = drm_state.AddEncoder();
+    encoder.possible_crtcs = 0b111;
+
+    auto& connector = drm_state.AddConnector();
+    connector.connection = true;
+    connector.modes = kStandardModes;
+    connector.encoders = std::vector<uint32_t>{encoder.id};
+
+    secondary_connector_id = connector.id;
+  }
+
+  scoped_refptr<MockDrmDevice> drm =
+      AddAndInitializeDrmDeviceWithState(drm_state);
+  GmockDrmDevice* gmock_drm = static_cast<GmockDrmDevice*>(drm.get());
+
+  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
+  ASSERT_EQ(display_snapshots.size(), 2u);
+
+  // Modesets should fail by default, unless it is with CRTC-connector pairings
+  // specified with |desired_pairings|.
+  EXPECT_CALL(*gmock_drm, CommitProperties)
+      .Times(AtLeast(1))
+      .WillRepeatedly(Return(false));
+
+  CrtcConnectorPairs desired_pairings = {{crtc_1, primary_connector_id},
+                                         {crtc_3, secondary_connector_id}};
+  EXPECT_CALL(
+      *gmock_drm,
+      CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings), _,
+                       _, _))
+      .WillRepeatedly(Return(true));
+
+  // test should succeed.
+  EXPECT_TRUE(ConfigureDisplays(display_snapshots,
+                                {display::ModesetFlag::kTestModeset}));
+
+  EXPECT_CALL(
+      *gmock_drm,
+      CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings),
+                       // For non-test.
+                       DRM_MODE_ATOMIC_ALLOW_MODESET, _, _))
+      // Return false - even though testing with the exact same pairings
+      // succeeded.
+      .WillRepeatedly(Return(false));
+  // Commit with exact config should fail.
+  EXPECT_FALSE(ConfigureDisplays(display_snapshots,
+                                 {display::ModesetFlag::kCommitModeset}));
+
+  DrmDisplay* primary_display = FindDisplayByConnectorId(primary_connector_id);
+  EXPECT_EQ(primary_display->crtc(), crtc_1);
+  DrmDisplay* secondary_display =
+      FindDisplayByConnectorId(secondary_connector_id);
+  EXPECT_EQ(secondary_display->crtc(), crtc_3);
+}
+
+TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
+       ConfigureDisplaysAlternateCrtcFallbackAllFailed) {
+  auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithAllProperties();
+
+  // Create a pool of 3 CRTCs
+  AddPlaneOnCrtcAndGetCrtcId(drm_state);
+  AddPlaneOnCrtcAndGetCrtcId(drm_state);
+  AddPlaneOnCrtcAndGetCrtcId(drm_state);
+
+  // First, add a display with high bandwidth mode.
+  {
+    auto& encoder = drm_state.AddEncoder();
+    // Can use all 3 CRTCs
+    encoder.possible_crtcs = 0b111;
+
+    auto& connector = drm_state.AddConnector();
+    connector.connection = true;
+    connector.modes = std::vector<ResolutionAndRefreshRate>{
+        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
+    connector.encoders = std::vector<uint32_t>{encoder.id};
+  }
+  // Add a normal external display.
+  {
+    auto& encoder = drm_state.AddEncoder();
+    encoder.possible_crtcs = 0b111;
+
+    auto& connector = drm_state.AddConnector();
+    connector.connection = true;
+    connector.modes = kStandardModes;
+    connector.encoders = std::vector<uint32_t>{encoder.id};
+  }
+
+  scoped_refptr<MockDrmDevice> drm =
+      AddAndInitializeDrmDeviceWithState(drm_state);
+  GmockDrmDevice* gmock_drm = static_cast<GmockDrmDevice*>(drm.get());
+
+  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
+  ASSERT_EQ(display_snapshots.size(), 2u);
+  EXPECT_CALL(*gmock_drm, CommitProperties)
+      // Expect at least 4 calls to ensure fallback is being triggered (2 from
+      // the initial linear/preferred modifier test modeset, and another 2 for
+      // the first fallback).
+      .Times(AtLeast(4))
+      .WillRepeatedly(Return(false));
+
+  EXPECT_FALSE(ConfigureDisplays(display_snapshots,
+                                 {display::ModesetFlag::kTestModeset}));
+}
+
+TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
+       NoConfigureDisplaysAlternateCrtcFallbackWithHardwareMirroring) {
+  // Enable hardware mirroring
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      display::features::kEnableHardwareMirrorMode);
+  ASSERT_TRUE(display::features::IsHardwareMirrorModeEnabled());
+  // Re-initialize DrmGpuDisplayManager with HW mirroring enabled.
+  drm_gpu_display_manager_ = std::make_unique<DrmGpuDisplayManager>(
+      screen_manager_.get(), device_manager_.get());
+
+  auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithAllProperties();
+
+  // Create a pool of 2 CRTCs
+  AddPlaneOnCrtcAndGetCrtcId(drm_state);
+  AddPlaneOnCrtcAndGetCrtcId(drm_state);
+
+  {
+    auto& encoder = drm_state.AddEncoder();
+    encoder.possible_crtcs = 0b11;
+    auto& connector = drm_state.AddConnector();
+    connector.connection = true;
+    connector.modes = std::vector<ResolutionAndRefreshRate>{
+        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
+    connector.encoders = std::vector<uint32_t>{encoder.id};
+  }
+
+  scoped_refptr<MockDrmDevice> drm =
+      AddAndInitializeDrmDeviceWithState(drm_state);
+  GmockDrmDevice* gmock_drm = static_cast<GmockDrmDevice*>(drm.get());
+
+  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
+  ASSERT_EQ(display_snapshots.size(), 1u);
+
+  // Test-modeset should only be called twice - without any fallbacks attempted.
+  EXPECT_CALL(*gmock_drm, CommitProperties)
+      // Expect 2 calls as ScreenManager tests modeset with preferred modifiers
+      // and then linear modifiers on failure.
+      .Times(Exactly(2))
+      .WillRepeatedly(Return(false));
+
+  EXPECT_FALSE(ConfigureDisplays(display_snapshots,
+                                 {display::ModesetFlag::kTestModeset}));
+}
+
+// TODO: b/40263526 - Re-enable for ASan LSan builds after eliminiating circular
+// dependency causing the DrmDevice to leak.
+#if defined(LEAK_SANITIZER)
+#define MAYBE_ConfigureDisplaysUseSuccesfulStateForCommit \
+  DISABLED_ConfigureDisplaysUseSuccesfulStateForCommit
+#else
+#define MAYBE_ConfigureDisplaysUseSuccesfulStateForCommit \
+  ConfigureDisplaysUseSuccesfulStateForCommit
+#endif
+TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
+       MAYBE_ConfigureDisplaysUseSuccesfulStateForCommit) {
+  auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithAllProperties();
+
+  // Create a pool of 3 CRTCs
+  const uint32_t crtc_1 = AddPlaneOnCrtcAndGetCrtcId(drm_state);
+  AddPlaneOnCrtcAndGetCrtcId(drm_state);
+  const uint32_t crtc_3 = AddPlaneOnCrtcAndGetCrtcId(drm_state);
+
+  uint32_t primary_connector_id, secondary_connector_id;
+
+  // First, add a display with high bandwidth mode.
+  {
+    auto& encoder = drm_state.AddEncoder();
+    // Can use all 3 CRTCs
+    encoder.possible_crtcs = 0b111;
+
+    auto& connector = drm_state.AddConnector();
+    connector.connection = true;
+    connector.modes = std::vector<ResolutionAndRefreshRate>{
+        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
+    connector.encoders = std::vector<uint32_t>{encoder.id};
+
+    primary_connector_id = connector.id;
+  }
+  // Add a normal external display.
+  {
+    auto& encoder = drm_state.AddEncoder();
+    encoder.possible_crtcs = 0b111;
+
+    auto& connector = drm_state.AddConnector();
+    connector.connection = true;
+    connector.modes = kStandardModes;
+    connector.encoders = std::vector<uint32_t>{encoder.id};
+
+    secondary_connector_id = connector.id;
+  }
+
+  scoped_refptr<MockDrmDevice> drm =
+      AddAndInitializeDrmDeviceWithState(drm_state);
+  GmockDrmDevice* gmock_drm = static_cast<GmockDrmDevice*>(drm.get());
+
+  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
+  ASSERT_EQ(display_snapshots.size(), 2u);
+
+  CrtcConnectorPairs desired_pairings = {{crtc_1, primary_connector_id},
+                                         {crtc_3, secondary_connector_id}};
+  // First test modeset should fallback and succeed on |desired_pairings|.
+  {
+    // Modesets should fail by default, unless it is with CRTC-connector
+    // pairings specified with |desired_pairings|.
+    EXPECT_CALL(*gmock_drm, CommitProperties)
+        .Times(AtLeast(1))
+        .WillRepeatedly(Return(false));
+
+    EXPECT_CALL(
+        *gmock_drm,
+        CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings),
+                         _, _, _))
+        .WillOnce(Return(true));
+
+    EXPECT_TRUE(ConfigureDisplays(display_snapshots,
+                                  {display::ModesetFlag::kTestModeset}));
+
+    ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(gmock_drm));
+  }
+  // Second test modeset with different config should fail, and leave the
+  // DrmGpuDisplayManager with a failed CRTC-connector pairings.
+  {
+    std::vector<display::DisplayConfigurationParams> failing_request;
+    for (const auto& snapshot : display_snapshots) {
+      failing_request.emplace_back(snapshot->display_id(), snapshot->origin(),
+                                   snapshot->modes().back().get());
+    }
+
+    EXPECT_CALL(*gmock_drm, CommitProperties)
+        .Times(AtLeast(1))
+        .WillRepeatedly(Return(false));
+    EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
+        failing_request, {display::ModesetFlag::kTestModeset}));
+    ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(gmock_drm));
+  }
+  // A commit call made with previously successful config (first config) should
+  // restore the abstractions back to the state of its success.
+  {
+    // Only commit modeset should be called once.
+    EXPECT_CALL(*gmock_drm, CommitProperties).Times(0);
+    EXPECT_CALL(
+        *gmock_drm,
+        CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings),
+                         _, _, _))
+        .WillOnce(Return(true));
+
+    EXPECT_TRUE(ConfigureDisplays(display_snapshots,
+                                  {display::ModesetFlag::kCommitModeset}));
+
+    // DrmDevice seems to leak on successful configure in tests. Manually
+    // checking for mock calls and allowing leak for now.
+    ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(gmock_drm));
+    testing::Mock::AllowLeak(gmock_drm);
+  }
+
+  DrmDisplay* primary_display = FindDisplayByConnectorId(primary_connector_id);
+  EXPECT_EQ(crtc_1, primary_display->crtc());
+  DrmDisplay* secondary_display =
+      FindDisplayByConnectorId(secondary_connector_id);
+  EXPECT_EQ(crtc_3, secondary_display->crtc());
+}
 }  // namespace ui
 
 #endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_DISPLAY_MANAGER_UNITTEST_CC_
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
index 4413bcb..7a89c25 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.cc
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
@@ -17,23 +17,6 @@
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
 
-// Private types defined in libdrm. Define them here so we can peek at the
-// commit and ensure the expected state has been set correctly.
-struct drmModeAtomicReqItem {
-  uint32_t object_id;
-  uint32_t property_id;
-  uint64_t value;
-  uint32_t cursor;
-};
-
-typedef drmModeAtomicReqItem* drmModeAtomicReqItemPtr;
-
-struct _drmModeAtomicReq {
-  uint32_t cursor;
-  uint32_t size_items;
-  drmModeAtomicReqItemPtr items;
-};
-
 namespace ui {
 
 namespace {
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.h b/ui/ozone/platform/drm/gpu/mock_drm_device.h
index 86130cc4..e42359e3 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.h
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.h
@@ -28,6 +28,23 @@
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
 #include "ui/ozone/platform/drm/gpu/page_flip_request.h"
 
+// Private types defined in libdrm. Define them here so we can peek at the
+// commit and ensure the expected state has been set correctly.
+struct drmModeAtomicReqItem {
+  uint32_t object_id;
+  uint32_t property_id;
+  uint64_t value;
+  uint32_t cursor;
+};
+
+typedef drmModeAtomicReqItem* drmModeAtomicReqItemPtr;
+
+struct _drmModeAtomicReq {
+  uint32_t cursor;
+  uint32_t size_items;
+  drmModeAtomicReqItemPtr items;
+};
+
 namespace ui {
 
 using ResolutionAndRefreshRate = std::pair<gfx::Size, uint32_t>;
@@ -350,6 +367,9 @@
   void SetDriverName(std::optional<std::string> name);
   uint32_t GetFramebufferForCrtc(uint32_t crtc_id) const;
 
+ protected:
+  ~MockDrmDevice() override;
+
  private:
   // Properties of the plane associated with a fb.
   struct FramebufferProps {
@@ -358,8 +378,6 @@
     uint64_t modifier = 0;
   };
 
-  ~MockDrmDevice() override;
-
   bool UpdateProperty(uint32_t id,
                       uint64_t value,
                       std::vector<DrmWrapper::Property>* properties);
diff --git a/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc b/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
index 4ac0d7b..e1af8758 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
@@ -27,6 +27,7 @@
 #include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event_constants.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
 #include "ui/events/platform/platform_event_source.h"
 #include "ui/events/platform/scoped_event_dispatcher.h"
 #include "ui/gfx/geometry/point_conversions.h"
@@ -185,6 +186,7 @@
 
   // Starts the wayland drag session setting |this| object as delegate.
   state_ = State::kStarted;
+  has_received_enter_ = false;
   drag_source_ = source;
   origin_window_ = origin_window;
   data_device_->StartDrag(*data_source_, *origin_window, serial->value,
@@ -384,6 +386,7 @@
     // so we don't need to read it through Wayland and can just copy it here.
     DCHECK_EQ(state_, State::kStarted);
     DCHECK(offered_exchange_data_provider_);
+    has_received_enter_ = true;
     window_->OnDragDataAvailable(std::make_unique<OSExchangeData>(
         offered_exchange_data_provider_->Clone()));
   } else {
@@ -515,6 +518,7 @@
   icon_frame_callback_.reset();
   offered_exchange_data_provider_.reset();
   data_device_->ResetDragDelegate();
+  has_received_enter_ = false;
   state_ = State::kIdle;
 }
 
@@ -722,17 +726,30 @@
 uint32_t WaylandDataDragController::DispatchEvent(const PlatformEvent& event) {
   DCHECK_NE(state_, State::kIdle);
 
-  // Drag session start may be triggered asynchronously, eg: dragging web
-  // contents, which might lead to race conditions where mouse button release is
-  // processed at compositor-side, sent to the client and processed just after
-  // the start_drag request is issued. In such cases, the compositor may ignore
-  // the request, and protocol-wise there is no explicit mechanism for clients
-  // to be notified about it (eg: an error event), and the only way of detecting
-  // that, for now, is to monitor wl_pointer events here and abort the session
-  // if it comes in.
+  // Two distinct problematic edge cases are handled here, where mouse button
+  // release events come in after start_drag has already been requested:
+  //
+  // 1. If it's received before the drag session effectively starts at
+  // compositor side, which is possible given the asynchronous nature of the
+  // Wayland protocol. In this case, to preventing UI from getting stuck on the
+  // drag nested loop, we just abort the drag session by calling.
+  //
+  // 2. Otherwise, button release events may be received from buggy compositors
+  // in addition to the actual dnd drop events, in which case the event is
+  // suppressed, otherwise it leads to broken UI state, as observed for example
+  // in https://crbug.com/329703410.
+  //
+  // Currently, there's no reliable way in the protocol to determine when the
+  // drag session has effectively started, so as a best-effort heuristic we
+  // consider it started once wl_data_device.enter has been received at least
+  // once.
   if (event->type() == ET_MOUSE_RELEASED) {
-    OnDataSourceFinish(data_source_.get(), event->time_stamp(),
-                       /*completed=*/false);
+    if (!has_received_enter_) {
+      OnDataSourceFinish(data_source_.get(), event->time_stamp(),
+                         /*completed=*/false);
+    } else {
+      return POST_DISPATCH_STOP_PROPAGATION;
+    }
   }
 
   return POST_DISPATCH_PERFORM_DEFAULT;
diff --git a/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h b/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h
index 0fe592d..f64b3075 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h
+++ b/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h
@@ -130,6 +130,8 @@
   FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, StartDragWithText);
   FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, AsyncNoopStartDrag);
   FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest,
+                           SuppressPointerButtonReleasesAfterEnter);
+  FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest,
                            StartDragWithWrongMimeType);
   FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest,
                            ForeignDragHandleAskAction);
@@ -216,6 +218,9 @@
   State state_ = State::kIdle;
   std::optional<mojom::DragEventSource> drag_source_;
 
+  // In outgoing sessions, tracks if any drag enter has already been received.
+  bool has_received_enter_ = false;
+
   // Data offered by us to the other side.
   std::unique_ptr<WaylandDataSource> data_source_;
 
diff --git a/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc b/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc
index de8f232..bdb986d1 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc
@@ -976,11 +976,10 @@
   SendPointerButton(window_.get(), &delegate_, BTN_LEFT, /*pressed=*/false);
 
   // Attempt to start drag session and ensure it fails.
-  bool result_1 = window_->StartDrag(
+  ASSERT_FALSE(window_->StartDrag(
       os_exchange_data, DragDropTypes::DRAG_COPY, DragEventSource::kMouse,
       /*cursor=*/{},
-      /*can_grab_pointer=*/true, drag_finished_callback_->callback(), nullptr);
-  EXPECT_FALSE(result_1);
+      /*can_grab_pointer=*/true, drag_finished_callback_->callback(), nullptr));
   Mock::VerifyAndClearExpectations(drop_handler_.get());
   EXPECT_FALSE(drag_controller()->origin_window_);
   EXPECT_FALSE(drag_controller()->nested_dispatcher_);
@@ -989,6 +988,11 @@
     // Drag mustn't be started. Availability of data_source can be used to
     // determine if the client has initiated a drag session.
     ASSERT_FALSE(server->data_device_manager()->data_source());
+
+    ASSERT_TRUE(server->data_device_manager()->data_device());
+    server->data_device_manager()
+        ->data_device()
+        ->disable_auto_send_start_drag_events();
   });
 
   // 2. Send wl_pointer.button release just after drag start.
@@ -1001,26 +1005,59 @@
     EXPECT_CALL(*drag_finished_callback_,
                 OnDragFinished(Eq(DragOperation::kNone)))
         .Times(1);
+
     SendPointerButton(window_.get(), &delegate_, BTN_LEFT, /*pressed=*/false);
   }));
 
-  bool result_2 = window_->StartDrag(
+  bool result = window_->StartDrag(
       os_exchange_data, DragDropTypes::DRAG_COPY, DragEventSource::kMouse,
       /*cursor=*/{},
       /*can_grab_pointer=*/true, drag_finished_callback_->callback(), nullptr);
+
   PostToServerAndWait([](wl::TestWaylandServerThread* server) {
     // Drag must be started. Availability of data_source can be used to
     // determine if the client has initiated a drag session.
     ASSERT_TRUE(server->data_device_manager()->data_source());
   });
+
   // TODO(crbug.com/1022722): Double-check if this should return false instead.
-  EXPECT_TRUE(result_2);
+  EXPECT_TRUE(result);
+
   Mock::VerifyAndClearExpectations(drop_handler_.get());
   Mock::VerifyAndClearExpectations(drag_finished_callback_.get());
   EXPECT_FALSE(drag_controller()->origin_window_);
   EXPECT_FALSE(drag_controller()->nested_dispatcher_);
 }
 
+TEST_P(WaylandDataDragControllerTest, SuppressPointerButtonReleasesAfterEnter) {
+  OSExchangeData os_exchange_data;
+  os_exchange_data.SetString(sample_text_for_dnd());
+
+  // Press left pointer button and request to start a drag session.
+  FocusAndPressLeftPointerButton(window_.get(), &delegate_);
+  drag_controller()->StartSession(os_exchange_data, DragDropTypes::DRAG_COPY,
+                                  DragEventSource::kMouse);
+
+  // Ensure start_drag request is processed at compositor side and a first enter
+  // is received by the client. From that point onwards, any pointer button
+  // event must be no-op.
+  wl::SyncDisplay(connection_->display_wrapper(), *connection_->display());
+  ASSERT_TRUE(drag_controller()->has_received_enter_);
+
+  // Emulates a spurious pointer button release and ensure it is no-op.
+  EXPECT_CALL(*drop_handler_, OnDragLeave).Times(0);
+  SendPointerButton(window_.get(), &delegate_, BTN_LEFT, /*pressed=*/false);
+  Mock::VerifyAndClearExpectations(drop_handler_.get());
+  Mock::VerifyAndClearExpectations(drag_finished_callback_.get());
+  EXPECT_TRUE(drag_controller()->data_source_);
+
+  // Ok, we're done.
+  SendDndDrop();
+  EXPECT_FALSE(drag_controller()->data_source_);
+  EXPECT_FALSE(drag_controller()->origin_window_);
+  EXPECT_FALSE(drag_controller()->nested_dispatcher_);
+}
+
 // Regression test for https://crbug.com/1175083.
 TEST_P(WaylandDataDragControllerTest, StartDragWithCorrectSerial) {
   FocusAndPressLeftPointerButton(window_.get(), &delegate_);
diff --git a/ui/ozone/platform/wayland/test/test_data_device.cc b/ui/ozone/platform/wayland/test/test_data_device.cc
index 027a99b..d473cfa0 100644
--- a/ui/ozone/platform/wayland/test/test_data_device.cc
+++ b/ui/ozone/platform/wayland/test/test_data_device.cc
@@ -103,7 +103,10 @@
   CHECK(manager_);
   drag_serial_ = serial;
   manager_->set_data_source(source);
-  SendOfferAndEnter(origin, {});
+  if (auto_send_start_drag_events_) {
+    SendOfferAndEnter(origin, {});
+  }
+  auto_send_start_drag_events_ = true;
   wl_client_flush(wl_resource_get_client(resource()));
 }
 
diff --git a/ui/ozone/platform/wayland/test/test_data_device.h b/ui/ozone/platform/wayland/test/test_data_device.h
index ed072d6f..8b0df3c 100644
--- a/ui/ozone/platform/wayland/test/test_data_device.h
+++ b/ui/ozone/platform/wayland/test/test_data_device.h
@@ -50,10 +50,18 @@
 
   uint32_t drag_serial() const { return drag_serial_; }
 
+  // Configure this data device to not send offer/enter events next time it
+  // receives a start_drag request. Useful for tests that wish to emulate
+  // some specific compositor behavior when starting a drag session.
+  void disable_auto_send_start_drag_events() {
+    auto_send_start_drag_events_ = false;
+  }
+
  private:
   const raw_ptr<TestDataDeviceManager> manager_;
 
   uint32_t drag_serial_ = 0;
+  bool auto_send_start_drag_events_ = true;
 };
 
 }  // namespace wl
diff --git a/ui/views/accessibility/view_accessibility.cc b/ui/views/accessibility/view_accessibility.cc
index 2743a95..598db9f2 100644
--- a/ui/views/accessibility/view_accessibility.cc
+++ b/ui/views/accessibility/view_accessibility.cc
@@ -397,6 +397,7 @@
 }
 
 void ViewAccessibility::SetRole(const ax::mojom::Role role) {
+  DCHECK(IsValidRoleForViews(role)) << "Invalid role for Views.";
   if (role == GetViewAccessibilityRole()) {
     return;
   }
@@ -461,11 +462,6 @@
     if (data_.role == ax::mojom::Role::kUnknown) {
       ui::AXNodeData data;
       view_->GetAccessibleNodeData(&data);
-      if (data.role == ax::mojom::Role::kUnknown) {
-        // TODO(accessibility): Remove this once the OverrideRole functions are
-        // removed.
-        data.role = override_data_.role;
-      }
       data_.role = data.role;
     }
 
@@ -604,11 +600,6 @@
   return data_.HasState(ax::mojom::State::kIgnored);
 }
 
-void ViewAccessibility::OverrideRole(const ax::mojom::Role role) {
-  DCHECK(IsValidRoleForViews(role)) << "Invalid role for Views.";
-  override_data_.role = role;
-}
-
 void ViewAccessibility::OverrideName(const std::string& name,
                                      const ax::mojom::NameFrom name_from) {
   DCHECK_EQ(name.empty(),
@@ -616,15 +607,6 @@
       << "If the name is being removed to improve the user experience, "
          "|name_from| should be set to |kAttributeExplicitlyEmpty|.";
 
-  // |AXNodeData::SetName| expects a valid role. Some Views call |OverrideRole|
-  // prior to overriding the name. For those that don't, see if we can get the
-  // default role from the View.
-  if (override_data_.role == ax::mojom::Role::kUnknown) {
-    ui::AXNodeData data;
-    view_->GetAccessibleNodeData(&data);
-    override_data_.role = data.role;
-  }
-
   override_data_.SetNameFrom(name_from);
   override_data_.SetNameChecked(name);
 }
diff --git a/ui/views/accessibility/view_accessibility.h b/ui/views/accessibility/view_accessibility.h
index dc70659..afe242b5 100644
--- a/ui/views/accessibility/view_accessibility.h
+++ b/ui/views/accessibility/view_accessibility.h
@@ -59,7 +59,7 @@
 
   // Modifies |node_data| to reflect the current accessible state of the
   // associated View, taking any custom overrides into account
-  // (see OverrideFocus, OverrideRole, etc. below).
+  // (see OverrideFocus, etc. below).
   virtual void GetAccessibleNodeData(ui::AXNodeData* node_data) const;
 
   // Made to be overridden on platforms that need the temporary
@@ -197,10 +197,6 @@
                       const ax::mojom::DescriptionFrom description_from =
                           ax::mojom::DescriptionFrom::kAriaDescription);
 
-  // Deprecated. Use ViewAccessibility::SetRole instead.
-  // See https://crbug.com/324485311.
-  void OverrideRole(const ax::mojom::Role role);
-
   // Sets the accessible name to the specified string value.
   // By default the source type of the name is attribute. This source is
   // appropriate for most use cases where a View is providing a non-empty flat
@@ -380,7 +376,7 @@
 
   const ui::AXUniqueId unique_id_;
 
-  // Contains data set explicitly via OverrideRole, OverrideName, etc. that
+  // Contains data set explicitly via OverrideName, etc. that
   // overrides anything provided by GetAccessibleNodeData().
   ui::AXNodeData override_data_;
 
diff --git a/ui/views/accessibility/view_accessibility_utils.cc b/ui/views/accessibility/view_accessibility_utils.cc
index 0a0725d..a039879 100644
--- a/ui/views/accessibility/view_accessibility_utils.cc
+++ b/ui/views/accessibility/view_accessibility_utils.cc
@@ -44,6 +44,10 @@
 // static
 void ViewAccessibilityUtils::Merge(const ui::AXNodeData& source,
                                    ui::AXNodeData& destination) {
+  if (source.role != ax::mojom::Role::kUnknown) {
+    destination.role = source.role;
+  }
+
   for (const auto& attr : source.int_attributes) {
     destination.AddIntAttribute(attr.first, attr.second);
   }
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index b7265ce8..8d1968f8c 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -53,6 +53,7 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/selection_bound.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/touch_selection/touch_selection_metrics.h"
 #include "ui/views/accessibility/view_accessibility.h"
@@ -96,7 +97,6 @@
 #endif
 
 #if BUILDFLAG(IS_MAC)
-#include "ui/base/cocoa/defaults_utils.h"
 #include "ui/base/cocoa/secure_password_input.h"
 #endif
 
@@ -204,21 +204,7 @@
 
 // static
 base::TimeDelta Textfield::GetCaretBlinkInterval() {
-#if BUILDFLAG(IS_WIN)
-  static const size_t system_value = ::GetCaretBlinkTime();
-  if (system_value != 0) {
-    return (system_value == INFINITE) ? base::TimeDelta()
-                                      : base::Milliseconds(system_value);
-  }
-#elif BUILDFLAG(IS_MAC)
-  // If there's insertion point flash rate info in NSUserDefaults, use the
-  // blink period derived from that.
-  std::optional<base::TimeDelta> system_value(
-      ui::TextInsertionCaretBlinkPeriodFromDefaults());
-  if (system_value)
-    return *system_value;
-#endif
-  return base::Milliseconds(500);
+  return ui::NativeTheme::GetInstanceForNativeUi()->GetCaretBlinkInterval();
 }
 
 // static
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index c5379f4..e5b246b 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -876,16 +876,23 @@
   return platform_window()->GetBoundsInDIP();
 }
 
-void DesktopWindowTreeHostPlatform::OnOcclusionStateChanged(
-    ui::PlatformWindowOcclusionState occlusion_state) {
-  WindowTreeHostPlatform::OnOcclusionStateChanged(occlusion_state);
-  if (compositor() && aura::NativeWindowOcclusionTracker::
-                          IsNativeWindowOcclusionTrackingAlwaysEnabled(this)) {
-    if (compositor()->IsVisible()) {
-      GetContentWindow()->Show();
-    } else {
-      GetContentWindow()->Hide();
-    }
+void DesktopWindowTreeHostPlatform::OnCompositorVisibilityChanging(
+    ui::Compositor* compositor,
+    bool visible) {
+  // Make sure to show the content window before the compositor has become
+  // visible.
+  if (visible) {
+    GetContentWindow()->Show();
+  }
+}
+
+void DesktopWindowTreeHostPlatform::OnCompositorVisibilityChanged(
+    ui::Compositor* compositor,
+    bool visible) {
+  // Make sure to hide the content window after the compositor has become
+  // not visible.
+  if (!visible) {
+    GetContentWindow()->Hide();
   }
 }
 
@@ -912,9 +919,7 @@
       is_minimized != was_minimized) {
     if (is_minimized) {
       SetVisible(false);
-      GetContentWindow()->Hide();
     } else {
-      GetContentWindow()->Show();
       SetVisible(true);
     }
   }
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
index 5170551c..56fc1b5b 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
@@ -146,8 +146,12 @@
   void HideImpl() override;
   gfx::Rect CalculateRootWindowBounds() const override;
   gfx::Rect GetBoundsInDIP() const override;
-  void OnOcclusionStateChanged(
-      ui::PlatformWindowOcclusionState occlusion_state) override;
+
+  // CompositorObserver:
+  void OnCompositorVisibilityChanging(ui::Compositor* compositor,
+                                      bool visible) override;
+  void OnCompositorVisibilityChanged(ui::Compositor* compositor,
+                                     bool visible) override;
 
   // PlatformWindowDelegate:
   void OnClosed() override;
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index 8c4963b..3693085 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -88,20 +88,20 @@
       public_deps += [
         "cr_components/history_clusters:build_grdp",
         "cr_components/history_embeddings:build_grdp",
-        "cr_components/omnibox:build_grdp",
-        "cr_components/omnibox/icons:build_grdp",
+        "cr_components/searchbox:build_grdp",
+        "cr_components/searchbox/icons:build_grdp",
       ]
       grdp_files += [
         "$target_gen_dir/cr_components/history_clusters/resources.grdp",
         "$target_gen_dir/cr_components/history_embeddings/resources.grdp",
-        "$target_gen_dir/cr_components/omnibox/resources.grdp",
-        "$target_gen_dir/cr_components/omnibox/icons/resources.grdp",
+        "$target_gen_dir/cr_components/searchbox/resources.grdp",
+        "$target_gen_dir/cr_components/searchbox/icons/resources.grdp",
       ]
 
       if (is_chrome_branded) {
         public_deps +=
-            [ "cr_components/omnibox/icons:chrome_branded_build_grdp" ]
-        grdp_files += [ "$target_gen_dir/cr_components/omnibox/icons/chrome_branded_resources.grdp" ]
+            [ "cr_components/searchbox/icons:chrome_branded_build_grdp" ]
+        grdp_files += [ "$target_gen_dir/cr_components/searchbox/icons/chrome_branded_resources.grdp" ]
       }
     }
   }
diff --git a/ui/webui/resources/cr_components/commerce/browser_proxy.ts b/ui/webui/resources/cr_components/commerce/browser_proxy.ts
index 791433d..4487099 100644
--- a/ui/webui/resources/cr_components/commerce/browser_proxy.ts
+++ b/ui/webui/resources/cr_components/commerce/browser_proxy.ts
@@ -5,7 +5,7 @@
 import type {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
 import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
-import type {BookmarkProductInfo, PriceInsightsInfo, ProductInfo} from './shopping_service.mojom-webui.js';
+import type {BookmarkProductInfo, PriceInsightsInfo, ProductInfo, ProductSpecifications} from './shopping_service.mojom-webui.js';
 import {PageCallbackRouter, ShoppingServiceHandlerFactory, ShoppingServiceHandlerRemote} from './shopping_service.mojom-webui.js';
 
 let instance: BrowserProxy|null = null;
@@ -30,6 +30,9 @@
   showBookmarkEditorForCurrentUrl(): void;
   showFeedback(): void;
   getCallbackRouter(): PageCallbackRouter;
+  getProductInfoForUrl(url: Url): Promise<{productInfo: ProductInfo}>;
+  getProductSpecificationsForUrls(urls: Url[]):
+      Promise<{productSpecs: ProductSpecifications}>;
 }
 
 export class BrowserProxyImpl implements BrowserProxy {
diff --git a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html
index 1316062..90246c8 100644
--- a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html
+++ b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html
@@ -1,2 +1,77 @@
-hello world
-[[hasLoaded_]]
+<style>
+:host {
+  display: block;
+}
+
+.card {
+  background: var(--cr-card-background-color);
+  border-radius: var(--cr-card-border-radius);
+  box-shadow: var(--cr-elevation-3);
+}
+
+h2 {
+  display: flex;
+  align-items: center;
+  gap: 14px;
+  margin: 0;
+  padding: 23px 24px 13px;
+  font-size: 16px;
+  font-weight: 500;
+  line-height: 24px;
+  color: var(--cr-primary-text-color);
+}
+
+h2 iron-icon {
+  flex-shrink: 0;
+}
+
+hr {
+  border: 0;
+  height: 1px;
+  background: rgba(211, 227, 253, 1);
+  margin: 8px 24px;
+}
+
+hr:last-of-type {
+  display: none;
+}
+
+cr-url-list-item {
+  padding: 6px 24px;
+}
+
+.footer {
+  display: flex;
+  gap: 8px;
+  padding: 16px 24px 24px;
+}
+
+a {
+  color: var(--cr-link-color);
+}
+</style>
+
+<div class="card">
+  <h2 id="heading">
+    <iron-icon icon="history-embeddings:heading"></iron-icon>
+    [[i18n('historyEmbeddingsHeading', searchQuery)]]
+  </h2>
+
+  <div class="result-items">
+    <template is="dom-repeat" items="[[results]]">
+      <cr-url-list-item url="[[item.url]]" title="[[item.title]]"
+          description="[[item.domain]]"
+          on-click="onResultClick_">
+      </cr-url-list-item>
+      <hr>
+    </template>
+  </div>
+
+  <div class="footer">
+    <div>
+      [[i18n('historyEmbeddingsFooter')]]
+      <a href="#" target="_blank">[[i18n('learnMore')]]</a>
+    </div>
+    <cr-feedback-buttons></cr-feedback-buttons>
+  </div>
+</div>
diff --git a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts
index 9d6e06f..467db41 100644
--- a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts
+++ b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts
@@ -2,12 +2,35 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import '//resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
+import '//resources/cr_elements/cr_shared_vars.css.js';
+import '//resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
+
+import {I18nMixin} from '//resources/cr_elements/i18n_mixin.js';
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {DomRepeatEvent} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {HistoryEmbeddingsBrowserProxyImpl} from './browser_proxy.js';
 import {getTemplate} from './history_embeddings.html.js';
 
-export class HistoryEmbeddingsElement extends PolymerElement {
+// Temporary interface for some mocked history embeddings UI. This is only to
+// show some data in the UI for now and currently matches normal history
+// results (see HistoryEntry in //chrome/browser/resources/history/externs.ts).
+interface MockHistoryEntry {
+  domain: string;
+  title: string;
+  url: string;
+}
+
+export interface HistoryEmbeddingsElement {
+  $: {
+    heading: HTMLElement,
+  };
+}
+
+const HistoryEmbeddingsElementBase = I18nMixin(PolymerElement);
+
+export class HistoryEmbeddingsElement extends HistoryEmbeddingsElementBase {
   static get is() {
     return 'cr-history-embeddings';
   }
@@ -19,16 +42,24 @@
   static get properties() {
     return {
       hasLoaded_: Boolean,
+      searchQuery: String,
+      results: Array,
     };
   }
 
   private browserProxy_ = HistoryEmbeddingsBrowserProxyImpl.getInstance();
   private hasLoaded_ = false;
+  searchQuery: string;
+  results: MockHistoryEntry[];
 
   override ready() {
     super.ready();
     this.browserProxy_.doSomething().then(success => this.hasLoaded_ = success);
   }
+
+  private onResultClick_(e: DomRepeatEvent<MockHistoryEntry>) {
+    this.dispatchEvent(new CustomEvent('result-click', {detail: e.model.item}));
+  }
 }
 
 declare global {
diff --git a/ui/webui/resources/cr_components/history_embeddings/icons.html b/ui/webui/resources/cr_components/history_embeddings/icons.html
index cfb2880..dc151f4d 100644
--- a/ui/webui/resources/cr_components/history_embeddings/icons.html
+++ b/ui/webui/resources/cr_components/history_embeddings/icons.html
@@ -1,8 +1,10 @@
+<!-- TODO(b/328300718): Replace icons. -->
 <iron-iconset-svg name="history-embeddings" size="24">
   <svg>
     <defs>
       <path id="by-group" d="M3 18v-2h6v2H3Zm0-5v-2h12v2H3Zm0-5V6h18v2H3Z">
       </path>
+      <path id="heading" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path>
     </defs>
   </svg>
 </iron-iconset-svg>
diff --git a/ui/webui/resources/cr_components/omnibox/BUILD.gn b/ui/webui/resources/cr_components/searchbox/BUILD.gn
similarity index 94%
rename from ui/webui/resources/cr_components/omnibox/BUILD.gn
rename to ui/webui/resources/cr_components/searchbox/BUILD.gn
index 50e2441..f6dabbdf 100644
--- a/ui/webui/resources/cr_components/omnibox/BUILD.gn
+++ b/ui/webui/resources/cr_components/searchbox/BUILD.gn
@@ -7,7 +7,7 @@
 assert(!is_android && !is_ios)
 
 build_webui("build") {
-  grd_prefix = "cr_components_omnibox"
+  grd_prefix = "cr_components_searchbox"
   web_component_files = [
     "realbox.ts",
     "realbox_action.ts",
@@ -26,7 +26,7 @@
   mojo_files =
       [ "$root_gen_dir/components/omnibox/browser/omnibox.mojom-webui.ts" ]
 
-  ts_out_dir = "$root_gen_dir/ui/webui/resources/tsc/cr_components/omnibox"
+  ts_out_dir = "$root_gen_dir/ui/webui/resources/tsc/cr_components/searchbox"
   ts_composite = true
   ts_deps = [
     "//third_party/polymer/v3_0:library",
diff --git a/ui/webui/resources/cr_components/omnibox/OWNERS b/ui/webui/resources/cr_components/searchbox/OWNERS
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/OWNERS
rename to ui/webui/resources/cr_components/searchbox/OWNERS
diff --git a/ui/webui/resources/cr_components/omnibox/icons/BUILD.gn b/ui/webui/resources/cr_components/searchbox/icons/BUILD.gn
similarity index 90%
rename from ui/webui/resources/cr_components/omnibox/icons/BUILD.gn
rename to ui/webui/resources/cr_components/searchbox/icons/BUILD.gn
index 2757780..f5e05463 100644
--- a/ui/webui/resources/cr_components/omnibox/icons/BUILD.gn
+++ b/ui/webui/resources/cr_components/searchbox/icons/BUILD.gn
@@ -6,8 +6,8 @@
 import("//ui/webui/resources/tools/generate_grd.gni")
 
 generate_grd("build_grdp") {
-  grd_prefix = "omnibox_icons"
-  resource_path_prefix = "cr_components/omnibox/icons"
+  grd_prefix = "searchbox_icons"
+  resource_path_prefix = "cr_components/searchbox/icons"
   out_grd = "$target_gen_dir/resources.grdp"
   input_files = [
     "bookmark_cr23.svg",
@@ -68,8 +68,8 @@
 
 if (is_chrome_branded) {
   generate_grd("chrome_branded_build_grdp") {
-    grd_prefix = "omnibox_icons"
-    resource_path_prefix = "cr_components/omnibox/icons"
+    grd_prefix = "searchbox_icons"
+    resource_path_prefix = "cr_components/searchbox/icons"
     out_grd = "$target_gen_dir/chrome_branded_resources.grdp"
     input_files = [
       "google_g.svg",
diff --git a/ui/webui/resources/cr_components/omnibox/icons/bookmark_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/bookmark_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/bookmark_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/bookmark_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/calculator.svg b/ui/webui/resources/cr_components/searchbox/icons/calculator.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/calculator.svg
rename to ui/webui/resources/cr_components/searchbox/icons/calculator.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/calculator_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/calculator_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/calculator_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/calculator_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/calendar.svg b/ui/webui/resources/cr_components/searchbox/icons/calendar.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/calendar.svg
rename to ui/webui/resources/cr_components/searchbox/icons/calendar.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/chrome_product.svg b/ui/webui/resources/cr_components/searchbox/icons/chrome_product.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/chrome_product.svg
rename to ui/webui/resources/cr_components/searchbox/icons/chrome_product.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/chrome_product_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/chrome_product_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/chrome_product_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/chrome_product_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/clock_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/clock_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/clock_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/clock_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/currency.svg b/ui/webui/resources/cr_components/searchbox/icons/currency.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/currency.svg
rename to ui/webui/resources/cr_components/searchbox/icons/currency.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/currency_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/currency_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/currency_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/currency_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/default.svg b/ui/webui/resources/cr_components/searchbox/icons/default.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/default.svg
rename to ui/webui/resources/cr_components/searchbox/icons/default.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/definition.svg b/ui/webui/resources/cr_components/searchbox/icons/definition.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/definition.svg
rename to ui/webui/resources/cr_components/searchbox/icons/definition.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/definition_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/definition_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/definition_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/definition_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/dino.svg b/ui/webui/resources/cr_components/searchbox/icons/dino.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/dino.svg
rename to ui/webui/resources/cr_components/searchbox/icons/dino.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/dino_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/dino_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/dino_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/dino_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/drive_docs.svg b/ui/webui/resources/cr_components/searchbox/icons/drive_docs.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/drive_docs.svg
rename to ui/webui/resources/cr_components/searchbox/icons/drive_docs.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/drive_folder.svg b/ui/webui/resources/cr_components/searchbox/icons/drive_folder.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/drive_folder.svg
rename to ui/webui/resources/cr_components/searchbox/icons/drive_folder.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/drive_form.svg b/ui/webui/resources/cr_components/searchbox/icons/drive_form.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/drive_form.svg
rename to ui/webui/resources/cr_components/searchbox/icons/drive_form.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/drive_image.svg b/ui/webui/resources/cr_components/searchbox/icons/drive_image.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/drive_image.svg
rename to ui/webui/resources/cr_components/searchbox/icons/drive_image.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/drive_logo.svg b/ui/webui/resources/cr_components/searchbox/icons/drive_logo.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/drive_logo.svg
rename to ui/webui/resources/cr_components/searchbox/icons/drive_logo.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/drive_pdf.svg b/ui/webui/resources/cr_components/searchbox/icons/drive_pdf.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/drive_pdf.svg
rename to ui/webui/resources/cr_components/searchbox/icons/drive_pdf.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/drive_sheets.svg b/ui/webui/resources/cr_components/searchbox/icons/drive_sheets.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/drive_sheets.svg
rename to ui/webui/resources/cr_components/searchbox/icons/drive_sheets.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/drive_slides.svg b/ui/webui/resources/cr_components/searchbox/icons/drive_slides.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/drive_slides.svg
rename to ui/webui/resources/cr_components/searchbox/icons/drive_slides.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/drive_video.svg b/ui/webui/resources/cr_components/searchbox/icons/drive_video.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/drive_video.svg
rename to ui/webui/resources/cr_components/searchbox/icons/drive_video.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/extension_app.svg b/ui/webui/resources/cr_components/searchbox/icons/extension_app.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/extension_app.svg
rename to ui/webui/resources/cr_components/searchbox/icons/extension_app.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/finance.svg b/ui/webui/resources/cr_components/searchbox/icons/finance.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/finance.svg
rename to ui/webui/resources/cr_components/searchbox/icons/finance.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/finance_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/finance_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/finance_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/finance_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/history_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/history_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/history_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/history_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/incognito.svg b/ui/webui/resources/cr_components/searchbox/icons/incognito.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/incognito.svg
rename to ui/webui/resources/cr_components/searchbox/icons/incognito.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/incognito_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/incognito_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/incognito_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/incognito_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/journeys.svg b/ui/webui/resources/cr_components/searchbox/icons/journeys.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/journeys.svg
rename to ui/webui/resources/cr_components/searchbox/icons/journeys.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/journeys_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/journeys_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/journeys_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/journeys_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/mac_share.svg b/ui/webui/resources/cr_components/searchbox/icons/mac_share.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/mac_share.svg
rename to ui/webui/resources/cr_components/searchbox/icons/mac_share.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/mac_share_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/mac_share_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/mac_share_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/mac_share_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/note.svg b/ui/webui/resources/cr_components/searchbox/icons/note.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/note.svg
rename to ui/webui/resources/cr_components/searchbox/icons/note.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/page.svg b/ui/webui/resources/cr_components/searchbox/icons/page.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/page.svg
rename to ui/webui/resources/cr_components/searchbox/icons/page.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/page_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/page_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/page_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/page_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/search_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/search_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/search_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/search_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/share.svg b/ui/webui/resources/cr_components/searchbox/icons/share.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/share.svg
rename to ui/webui/resources/cr_components/searchbox/icons/share.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/share_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/share_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/share_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/share_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/sites.svg b/ui/webui/resources/cr_components/searchbox/icons/sites.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/sites.svg
rename to ui/webui/resources/cr_components/searchbox/icons/sites.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/sunrise.svg b/ui/webui/resources/cr_components/searchbox/icons/sunrise.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/sunrise.svg
rename to ui/webui/resources/cr_components/searchbox/icons/sunrise.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/sunrise_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/sunrise_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/sunrise_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/sunrise_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/tab.svg b/ui/webui/resources/cr_components/searchbox/icons/tab.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/tab.svg
rename to ui/webui/resources/cr_components/searchbox/icons/tab.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/tab_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/tab_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/tab_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/tab_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/translation.svg b/ui/webui/resources/cr_components/searchbox/icons/translation.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/translation.svg
rename to ui/webui/resources/cr_components/searchbox/icons/translation.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/translation_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/translation_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/translation_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/translation_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/trending_up.svg b/ui/webui/resources/cr_components/searchbox/icons/trending_up.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/trending_up.svg
rename to ui/webui/resources/cr_components/searchbox/icons/trending_up.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/trending_up_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/trending_up_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/trending_up_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/trending_up_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/when_is.svg b/ui/webui/resources/cr_components/searchbox/icons/when_is.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/when_is.svg
rename to ui/webui/resources/cr_components/searchbox/icons/when_is.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/when_is_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/when_is_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/when_is_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/when_is_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/win_share.svg b/ui/webui/resources/cr_components/searchbox/icons/win_share.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/win_share.svg
rename to ui/webui/resources/cr_components/searchbox/icons/win_share.svg
diff --git a/ui/webui/resources/cr_components/omnibox/icons/win_share_cr23.svg b/ui/webui/resources/cr_components/searchbox/icons/win_share_cr23.svg
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/icons/win_share_cr23.svg
rename to ui/webui/resources/cr_components/searchbox/icons/win_share_cr23.svg
diff --git a/ui/webui/resources/cr_components/omnibox/realbox.html b/ui/webui/resources/cr_components/searchbox/realbox.html
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox.html
rename to ui/webui/resources/cr_components/searchbox/realbox.html
diff --git a/ui/webui/resources/cr_components/omnibox/realbox.ts b/ui/webui/resources/cr_components/searchbox/realbox.ts
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox.ts
rename to ui/webui/resources/cr_components/searchbox/realbox.ts
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_action.html b/ui/webui/resources/cr_components/searchbox/realbox_action.html
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox_action.html
rename to ui/webui/resources/cr_components/searchbox/realbox_action.html
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_action.ts b/ui/webui/resources/cr_components/searchbox/realbox_action.ts
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox_action.ts
rename to ui/webui/resources/cr_components/searchbox/realbox_action.ts
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_browser_proxy.ts b/ui/webui/resources/cr_components/searchbox/realbox_browser_proxy.ts
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox_browser_proxy.ts
rename to ui/webui/resources/cr_components/searchbox/realbox_browser_proxy.ts
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_dropdown.html b/ui/webui/resources/cr_components/searchbox/realbox_dropdown.html
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox_dropdown.html
rename to ui/webui/resources/cr_components/searchbox/realbox_dropdown.html
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts b/ui/webui/resources/cr_components/searchbox/realbox_dropdown.ts
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts
rename to ui/webui/resources/cr_components/searchbox/realbox_dropdown.ts
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_dropdown_shared_style.css b/ui/webui/resources/cr_components/searchbox/realbox_dropdown_shared_style.css
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox_dropdown_shared_style.css
rename to ui/webui/resources/cr_components/searchbox/realbox_dropdown_shared_style.css
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_icon.html b/ui/webui/resources/cr_components/searchbox/realbox_icon.html
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox_icon.html
rename to ui/webui/resources/cr_components/searchbox/realbox_icon.html
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_icon.ts b/ui/webui/resources/cr_components/searchbox/realbox_icon.ts
similarity index 98%
rename from ui/webui/resources/cr_components/omnibox/realbox_icon.ts
rename to ui/webui/resources/cr_components/searchbox/realbox_icon.ts
index 43d4c50..eb9abc74 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_icon.ts
+++ b/ui/webui/resources/cr_components/searchbox/realbox_icon.ts
@@ -172,7 +172,7 @@
     }
 
     if (this.defaultIcon ===
-        '//resources/cr_components/omnibox/icons/google_g.svg') {
+        '//resources/cr_components/searchbox/icons/google_g.svg') {
       // The google_g.svg is a fully colored icon, so it needs to be displayed
       // as a background image as mask images will mask the colors.
       return `url(${this.defaultIcon})`;
@@ -240,7 +240,7 @@
     ];
     for (const icon of themedIcons) {
       if (imageUrl ===
-          'url(//resources/cr_components/omnibox/icons/' + icon + '.svg)') {
+          'url(//resources/cr_components/searchbox/icons/' + icon + '.svg)') {
         return true;
       }
     }
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_match.html b/ui/webui/resources/cr_components/searchbox/realbox_match.html
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/realbox_match.html
rename to ui/webui/resources/cr_components/searchbox/realbox_match.html
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_match.ts b/ui/webui/resources/cr_components/searchbox/realbox_match.ts
similarity index 99%
rename from ui/webui/resources/cr_components/omnibox/realbox_match.ts
rename to ui/webui/resources/cr_components/searchbox/realbox_match.ts
index 76c3a800..a8657e6 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_match.ts
+++ b/ui/webui/resources/cr_components/searchbox/realbox_match.ts
@@ -54,7 +54,7 @@
   };
 }
 
-// Displays an autocomplete match similar to those in the Omnibox.
+// Displays an autocomplete match.
 export class RealboxMatchElement extends PolymerElement {
   static get is() {
     return 'cr-realbox-match';
diff --git a/ui/webui/resources/cr_components/omnibox/utils.ts b/ui/webui/resources/cr_components/searchbox/utils.ts
similarity index 100%
rename from ui/webui/resources/cr_components/omnibox/utils.ts
rename to ui/webui/resources/cr_components/searchbox/utils.ts
diff --git a/ui/webui/resources/cr_elements/BUILD.gn b/ui/webui/resources/cr_elements/BUILD.gn
index 8609dd8..5aa0691 100644
--- a/ui/webui/resources/cr_elements/BUILD.gn
+++ b/ui/webui/resources/cr_elements/BUILD.gn
@@ -23,10 +23,8 @@
   if (include_polymer) {
     web_component_files += [
       "cr_a11y_announcer/cr_a11y_announcer.ts",
-      "cr_feedback_buttons/cr_feedback_buttons.ts",
       "cr_loading_gradient/cr_loading_gradient.ts",
       "cr_lottie/cr_lottie.ts",
-      "cr_profile_avatar_selector/cr_profile_avatar_selector_grid.ts",
       "cr_profile_avatar_selector/cr_profile_avatar_selector.ts",
       "cr_segmented_button/cr_segmented_button.ts",
       "cr_segmented_button/cr_segmented_button_option.ts",
@@ -77,6 +75,8 @@
       "cr_drawer/cr_drawer.ts",
       "cr_expand_button/cr_expand_button.html.ts",
       "cr_expand_button/cr_expand_button.ts",
+      "cr_feedback_buttons/cr_feedback_buttons.html.ts",
+      "cr_feedback_buttons/cr_feedback_buttons.ts",
       "cr_grid/cr_grid.html.ts",
       "cr_grid/cr_grid.ts",
       "cr_icon_button/cr_icon_button.html.ts",
@@ -147,6 +147,7 @@
       "cr_dialog/cr_dialog.css",
       "cr_drawer/cr_drawer.css",
       "cr_expand_button/cr_expand_button.css",
+      "cr_feedback_buttons/cr_feedback_buttons.css",
       "cr_grid/cr_grid.css",
       "cr_icon_button/cr_icon_button.css",
       "cr_input/cr_input.css",
diff --git a/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.css b/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.css
new file mode 100644
index 0000000..fc45bd5
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.css
@@ -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. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #import=../cr_shared_vars.css.js
+ * #import=../icons.html.js
+ * #scheme=relative
+ * #css_wrapper_metadata_end */
+
+.buttons {
+  --cr-feedback-buttons-icon-size_: 16px;
+  display: grid;
+  grid-auto-columns: var(--cr-feedback-buttons-icon-size_);
+  grid-auto-rows: var(--cr-feedback-buttons-icon-size_);
+  grid-auto-flow: column;
+  gap: 12px;
+  align-items: center;
+  justify-items: center;
+}
+
+cr-icon-button {
+  --cr-icon-button-fill-color: currentColor;
+  --cr-icon-button-icon-size: var(--cr-feedback-buttons-icon-size_);
+  --cr-icon-button-size: 24px;
+  margin: 0;
+}
diff --git a/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.html b/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.html
deleted file mode 100644
index 861b16b..0000000
--- a/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<style>
-  .buttons {
-    --cr-feedback-buttons-icon-size_: 16px;
-    display: grid;
-    grid-auto-columns: var(--cr-feedback-buttons-icon-size_);
-    grid-auto-rows: var(--cr-feedback-buttons-icon-size_);
-    grid-auto-flow: column;
-    gap: 12px;
-    align-items: center;
-    justify-items: center;
-  }
-
-  cr-icon-button {
-    --cr-icon-button-fill-color: currentColor;
-    --cr-icon-button-icon-size: var(--cr-feedback-buttons-icon-size_);
-    --cr-icon-button-size: 24px;
-    margin: 0;
-  }
-</style>
-
-<div class="buttons">
-  <cr-icon-button id="thumbsUp" iron-icon="[[getThumbsUpIcon_(selectedOption)]]"
-      aria-label="[[thumbsUpLabel_]]"
-      title="[[thumbsUpLabel_]]"
-      aria-pressed="[[getThumbsUpAriaPressed_(selectedOption)]]"
-      on-click="onThumbsUpClick_">
-  </cr-icon-button>
-  <cr-icon-button id="thumbsDown"
-      iron-icon="[[getThumbsDownIcon_(selectedOption)]]"
-      aria-label="[[thumbsDownLabel_]]"
-      title="[[thumbsDownLabel_]]"
-      aria-pressed="[[getThumbsDownAriaPressed_(selectedOption)]]"
-      on-click="onThumbsDownClick_">
-  </cr-icon-button>
-</div>
diff --git a/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.html.ts b/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.html.ts
new file mode 100644
index 0000000..03002467
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.html.ts
@@ -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.
+
+import {html} from '//resources/lit/v3_0/lit.rollup.js';
+
+import type {CrFeedbackButtonsElement} from './cr_feedback_buttons.js';
+
+export function getHtml(this: CrFeedbackButtonsElement) {
+  return html`
+<div class="buttons">
+  <cr-icon-button id="thumbsUp" iron-icon="${this.getThumbsUpIcon_()}"
+      aria-label="${this.thumbsUpLabel_}"
+      title="${this.thumbsUpLabel_}"
+      aria-pressed="${this.getThumbsUpAriaPressed_()}"
+      @click="${this.onThumbsUpClick_}">
+  </cr-icon-button>
+  <cr-icon-button id="thumbsDown"
+      iron-icon="${this.getThumbsDownIcon_()}"
+      aria-label="${this.thumbsDownLabel_}"
+      title="${this.thumbsDownLabel_}"
+      aria-pressed="${this.getThumbsDownAriaPressed_()}"
+      @click="${this.onThumbsDownClick_}">
+  </cr-icon-button>
+</div>`;
+}
diff --git a/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.ts b/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.ts
index 75315ae..f4a77434 100644
--- a/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.ts
+++ b/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.ts
@@ -3,15 +3,14 @@
 // found in the LICENSE file.
 
 import '../cr_icon_button/cr_icon_button.js';
-import '../cr_shared_vars.css.js';
-import '../icons.html.js';
 
 import {loadTimeData} from '//resources/js/load_time_data.js';
-import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 
 import type {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';
 
-import {getTemplate} from './cr_feedback_buttons.html.js';
+import {getCss} from './cr_feedback_buttons.css.js';
+import {getHtml} from './cr_feedback_buttons.html.js';
 
 export enum CrFeedbackOption {
   THUMBS_DOWN = 0,
@@ -26,57 +25,55 @@
   };
 }
 
-export class CrFeedbackButtonsElement extends PolymerElement {
+export class CrFeedbackButtonsElement extends CrLitElement {
   static get is() {
     return 'cr-feedback-buttons';
   }
 
-  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 {
-      selectedOption: {
-        type: String,
-        value: CrFeedbackOption.UNSPECIFIED,
-      },
-      thumbsDownLabel_: {
-        type: String,
-        value: () => loadTimeData.getString('thumbsDown'),
-      },
-      thumbsUpLabel_: {
-        type: String,
-        value: () => loadTimeData.getString('thumbsUp'),
-      },
+      selectedOption: {type: String},
+      thumbsDownLabel_: {type: String},
+      thumbsUpLabel_: {type: String},
     };
   }
 
-  selectedOption: CrFeedbackOption;
-  private thumbsDownLabel_: string;
-  private thumbsUpLabel_: string;
+  selectedOption: CrFeedbackOption = CrFeedbackOption.UNSPECIFIED;
+  protected thumbsDownLabel_: string = loadTimeData.getString('thumbsDown');
+  protected thumbsUpLabel_: string = loadTimeData.getString('thumbsUp');
 
-  private getThumbsDownAriaPressed_(): boolean {
+  protected getThumbsDownAriaPressed_(): boolean {
     return this.selectedOption === CrFeedbackOption.THUMBS_DOWN;
   }
 
-  private getThumbsDownIcon_(): string {
+  protected getThumbsDownIcon_(): string {
     return this.selectedOption === CrFeedbackOption.THUMBS_DOWN ?
         'cr:thumbs-down-filled' :
         'cr:thumbs-down';
   }
 
-  private getThumbsUpAriaPressed_(): boolean {
+  protected getThumbsUpAriaPressed_(): boolean {
     return this.selectedOption === CrFeedbackOption.THUMBS_UP;
   }
 
-  private getThumbsUpIcon_(): string {
+  protected getThumbsUpIcon_(): string {
     return this.selectedOption === CrFeedbackOption.THUMBS_UP ?
         'cr:thumbs-up-filled' :
         'cr:thumbs-up';
   }
 
-  private notifySelectedOptionChanged_() {
+  private async notifySelectedOptionChanged_() {
+    // Wait for the element's DOM to be updated before dispatching
+    // selected-option-changed event.
+    await this.updateComplete;
     this.dispatchEvent(new CustomEvent('selected-option-changed', {
       bubbles: true,
       composed: true,
@@ -84,14 +81,14 @@
     }));
   }
 
-  private onThumbsDownClick_() {
+  protected onThumbsDownClick_() {
     this.selectedOption = this.selectedOption === CrFeedbackOption.THUMBS_DOWN ?
         CrFeedbackOption.UNSPECIFIED :
         CrFeedbackOption.THUMBS_DOWN;
     this.notifySelectedOptionChanged_();
   }
 
-  private onThumbsUpClick_() {
+  protected onThumbsUpClick_() {
     this.selectedOption = this.selectedOption === CrFeedbackOption.THUMBS_UP ?
         CrFeedbackOption.UNSPECIFIED :
         CrFeedbackOption.THUMBS_UP;
diff --git a/ui/webui/resources/cr_elements/cr_grid/cr_grid.ts b/ui/webui/resources/cr_elements/cr_grid/cr_grid.ts
index d7d25ded..9d30277 100644
--- a/ui/webui/resources/cr_elements/cr_grid/cr_grid.ts
+++ b/ui/webui/resources/cr_elements/cr_grid/cr_grid.ts
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {assert} from '//resources/js/assert.js';
+import {hasKeyModifiers} from '//resources/js/util.js';
 import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js';
 
@@ -32,18 +34,17 @@
 
   static override get properties() {
     return {
-      columns: {
-        type: Number,
-      },
-
-      disableArrowNavigation: {
-        type: Boolean,
-      },
+      columns: {type: Number},
+      disableArrowNavigation: {type: Boolean},
+      focusSelector: {type: String},
+      ignoreModifiedKeyEvents: {type: Boolean},
     };
   }
 
-  disableArrowNavigation: boolean = false;
   columns: number = 1;
+  disableArrowNavigation: boolean = false;
+  focusSelector?: string;
+  ignoreModifiedKeyEvents: boolean = false;
 
   override updated(changedProperties: PropertyValues<this>) {
     super.updated(changedProperties);
@@ -53,17 +54,29 @@
     }
   }
 
+  private getSlottedParent_(element: HTMLElement): HTMLElement {
+    let parent = element;
+
+    while (parent.assignedSlot !== this.$.items &&
+           parent.parentElement !== null) {
+      parent = parent.parentElement;
+    }
+
+    assert(parent);
+    return parent;
+  }
+
   protected onKeyDown_(e: KeyboardEvent) {
     if (!this.disableArrowNavigation &&
         ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
-      e.preventDefault();
       const items =
           (this.$.items.assignedElements() as HTMLElement[]).filter(el => {
             return !!(
                 el.offsetWidth || el.offsetHeight ||
                 el.getClientRects().length);
           });
-      const currentIndex = items.indexOf(e.target as HTMLElement);
+      const currentIndex =
+          items.indexOf(this.getSlottedParent_(e.target as HTMLElement));
       const isRtl = window.getComputedStyle(this)['direction'] === 'rtl';
       const bottomRowColumns = items.length % this.columns;
       const direction = ['ArrowRight', 'ArrowDown'].includes(e.key) ? 1 : -1;
@@ -74,6 +87,12 @@
       switch (e.key) {
         case 'ArrowLeft':
         case 'ArrowRight':
+          // Ignores keys likely to be browse shortcuts (like Alt+Left for
+          // back).
+          if (this.ignoreModifiedKeyEvents && hasKeyModifiers(e)) {
+            return;
+          }
+
           delta = direction * (isRtl ? -1 : 1);
           break;
         case 'ArrowUp':
@@ -92,8 +111,14 @@
         delta += bottomRowColumns;
       }
 
+      e.preventDefault();
       const newIndex = (items.length + currentIndex + delta) % items.length;
-      items[newIndex]!.focus();
+      const item = items[newIndex]!;
+      const toFocus = this.focusSelector ?
+          item.querySelector<HTMLElement>(this.focusSelector) :
+          item;
+      assert(toFocus);
+      toFocus.focus();
     }
 
     if (['Enter', ' '].includes(e.key)) {
diff --git a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html
index 730c00c2..02ce1ed 100644
--- a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html
+++ b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html
@@ -9,11 +9,13 @@
         display: inline-flex;
       }
 
+      #avatar-grid {
+        --cr-column-width: var(--avatar-size);
+        --cr-grid-gap: var(--avatar-spacing);
+      }
+
       #avatar-grid .avatar-container {
-        height: var(--avatar-size);
-        margin: calc(var(--avatar-spacing) / 2);
         position: relative;
-        width: var(--avatar-size);
       }
 
       #avatar-grid .avatar {
@@ -87,22 +89,27 @@
         --paper-tooltip-min-width: none;
       }
     </style>
-    <cr-profile-avatar-selector-grid id="avatar-grid" role="radiogroup"
+    <cr-grid id="avatar-grid" role="radiogroup" columns="[[columns]]"
+        focus-selector=".avatar"
         ignore-modified-key-events="[[ignoreModifiedKeyEvents]]">
       <template is="dom-repeat" items="[[avatars]]">
-        <div class$=
-            "avatar-container [[getSelectedClass_(item, selectedAvatar)]]">
-          <cr-button id="[[getAvatarId_(index)]]" aria-label="[[item.label]]"
-              tabindex$="[[getTabIndex_(index, item, tabFocusableAvatar_)]]"
-              class="avatar" on-click="onAvatarClick_" role="radio"
-              style$="background-image: [[getIconImageSet_(item.url)]]"
-              aria-checked$="[[getCheckedAttribute_(item, selectedAvatar)]]">
-          </cr-button>
-          <iron-icon icon="cr:check" class="checkmark"></iron-icon>
+        <!-- Wrapper div is needed so that only a single node is slotted in
+            cr-grid for each avatar. -->
+        <div>
+          <div class$=
+              "avatar-container [[getSelectedClass_(item, selectedAvatar)]]">
+            <cr-button id="[[getAvatarId_(index)]]" aria-label="[[item.label]]"
+                tabindex$="[[getTabIndex_(index, item, tabFocusableAvatar_)]]"
+                class="avatar" on-click="onAvatarClick_" role="radio"
+                style$="background-image: [[getIconImageSet_(item.url)]]"
+                aria-checked$="[[getCheckedAttribute_(item, selectedAvatar)]]">
+            </cr-button>
+            <iron-icon icon="cr:check" class="checkmark"></iron-icon>
+          </div>
+          <paper-tooltip for="[[getAvatarId_(index)]]"
+              offset="0" fit-to-visible-bounds>
+            [[item.label]]
+          </paper-tooltip>
         </div>
-        <paper-tooltip for="[[getAvatarId_(index)]]"
-            offset="0" fit-to-visible-bounds>
-          [[item.label]]
-        </paper-tooltip>
       </template>
-    </cr-profile-avatar-selector-grid>
+    </cr-grid>
diff --git a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts
index 4e0836c..90b4c5fe 100644
--- a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts
+++ b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts
@@ -8,11 +8,11 @@
  */
 
 import '../cr_button/cr_button.js';
+import '../cr_grid/cr_grid.js';
 import '../cr_shared_vars.css.js';
 import '../cr_shared_style.css.js';
 import '//resources/polymer/v3_0/paper-styles/color.js';
 import '//resources/polymer/v3_0/paper-tooltip/paper-tooltip.js';
-import './cr_profile_avatar_selector_grid.js';
 
 import {assert} from '//resources/js/assert.js';
 import {getImage} from '//resources/js/icon.js';
@@ -71,6 +71,14 @@
         type: Number,
         computed: 'computeTabFocusableAvatar_(avatars, selectedAvatar)',
       },
+
+      /**
+       * Number of columns in the grid.
+       */
+      columns: {
+        type: Number,
+        value: 6,
+      },
     };
   }
 
diff --git a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.html b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.html
deleted file mode 100644
index 0fdcbd0..0000000
--- a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.html
+++ /dev/null
@@ -1,8 +0,0 @@
-    <style>
-      :host {
-        display: inline-flex;
-        flex-wrap: wrap;
-        margin: calc(var(--avatar-spacing) / -2);
-      }
-    </style>
-    <slot></slot>
diff --git a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.ts b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.ts
deleted file mode 100644
index 2bc7080..0000000
--- a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2016 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview 'cr-profile-avatar-selector-grid' is an accessible control for
- * profile avatar icons that allows keyboard navigation with all arrow keys.
- */
-
-import {assert} from '//resources/js/assert.js';
-import {hasKeyModifiers} from '//resources/js/util.js';
-import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {getTemplate} from './cr_profile_avatar_selector_grid.html.js';
-
-export class CrProfileAvatarSelectorGridElement extends PolymerElement {
-  static get is() {
-    return 'cr-profile-avatar-selector-grid';
-  }
-
-  static get template() {
-    return getTemplate();
-  }
-
-  static get properties() {
-    return {
-      ignoreModifiedKeyEvents: {
-        type: Boolean,
-        value: false,
-      },
-    };
-  }
-
-  ignoreModifiedKeyEvents: boolean;
-
-  override ready() {
-    super.ready();
-    this.addEventListener('keydown', this.onKeyDown_.bind(this));
-  }
-
-  private onKeyDown_(e: KeyboardEvent) {
-    const items = this.querySelectorAll<HTMLElement>('.avatar');
-    switch (e.key) {
-      case 'ArrowDown':
-      case 'ArrowUp':
-        this.moveFocusRow_(items, e.key);
-        e.preventDefault();
-        return;
-      case 'ArrowLeft':
-      case 'ArrowRight':
-        // Ignores keys likely to be browse shortcuts (like Alt+Left for back).
-        if (this.ignoreModifiedKeyEvents && hasKeyModifiers(e)) {
-          return;
-        }
-
-        this.moveFocusRow_(items, e.key);
-        e.preventDefault();
-        return;
-    }
-  }
-
-  /**
-   * Moves focus up/down/left/right according to the given direction. Wraps
-   * around as necessary.
-   */
-  private moveFocusRow_(
-      items: NodeListOf<HTMLElement>,
-      direction: 'ArrowDown'|'ArrowRight'|'ArrowUp'|'ArrowLeft') {
-    let offset =
-        (direction === 'ArrowDown' || direction === 'ArrowRight') ? 1 : -1;
-    const style = getComputedStyle(this);
-    const avatarSpacing =
-        parseInt(style.getPropertyValue('--avatar-spacing'), 10);
-    const avatarSize = parseInt(style.getPropertyValue('--avatar-size'), 10);
-    const rowSize = Math.floor(this.clientWidth / (avatarSpacing + avatarSize));
-    const rows = Math.ceil(items.length / rowSize);
-    const gridSize = rows * rowSize;
-
-    const focusIndex = Array.prototype.slice.call(items).findIndex(item => {
-      return (this.parentNode as ShadowRoot).activeElement === item;
-    });
-
-    let nextItem = null;
-    if (direction === 'ArrowDown' || direction === 'ArrowUp') {
-      for (let i = offset; Math.abs(i) <= rows; i += offset) {
-        nextItem = items[(focusIndex + i * rowSize + gridSize) % gridSize];
-        if (nextItem) {
-          break;
-        }
-        // This codepath can be hit when |gridSize| is larger than
-        // |items.length|, which means that there are empty grid spots at the
-        // end.
-      }
-    } else {
-      if (style.direction === 'rtl') {
-        offset *= -1;
-      }
-      let nextIndex = (focusIndex + offset) % items.length;
-      if (nextIndex < 0) {
-        nextIndex = items.length - 1;
-      }
-      nextItem = items[nextIndex];
-    }
-
-    nextItem!.focus();
-    assert((this.parentNode as ShadowRoot).activeElement === nextItem);
-  }
-}
-
-declare global {
-  interface HTMLElementTagNameMap {
-    'cr-profile-avatar-selector-grid': CrProfileAvatarSelectorGridElement;
-  }
-}
-
-customElements.define(
-    CrProfileAvatarSelectorGridElement.is, CrProfileAvatarSelectorGridElement);